Module.php #1

  • //
  • guest/
  • thomas_gray/
  • jambox/
  • main/
  • swarm/
  • module/
  • Comments/
  • Module.php
  • View
  • Commits
  • Open Download .zip Download (16 KB)
<?php
/**
 * Perforce Swarm
 *
 * @copyright   2012 Perforce Software. All rights reserved.
 * @license     Please see LICENSE.txt in top-level folder of this distribution.
 * @version     <release>/<patch>
 */

namespace Comments;

use Activity\Model\Activity;
use Application\Filter\Linkify;
use Attachments\Model\Attachment;
use Comments\Model\Comment as CommentModel;
use P4\File\File;
use Reviews\Model\Review;
use P4\Spec\Change;
use P4\Spec\Job;
use Projects\Model\Project;
use Users\Model\User;
use Zend\Mvc\MvcEvent;

class Module
{
    /**
     * Connect to queue event manager to handle new comments.
     *
     * @param   MvcEvent    $event  the bootstrap event
     * @return  void
     */
    public function onBootstrap(MvcEvent $event)
    {
        $application = $event->getApplication();
        $services    = $application->getServiceManager();
        $events      = $services->get('queue')->getEventManager();

        // when a comment is created, fetch it and prepare notifications.
        $events->attach(
            'task.comment',
            function ($event) use ($services) {
                $p4Admin  = $services->get('p4_admin');
                $keywords = $services->get('review_keywords');
                $id       = $event->getParam('id');
                $data     = $event->getParam('data') + array(
                    'user'     => null,
                    'previous' => array(),
                    'current'  => array(),
                );

                try {
                    // fetch comment record
                    $comment = CommentModel::fetch($id, $p4Admin);
                    $context = $comment->getFileContext();
                    $event->setParam('comment', $comment);

                    // there are several types of comment activity - compare new against old to see what happened
                    $data['current'] = $data['current'] ?: $comment->get();
                    $commentAction   = CommentModel::deriveAction($data['current'], $data['previous']);

                    // exit early if there's nothing to do
                    if ($commentAction === CommentModel::ACTION_NONE) {
                        return;
                    }

                    // determine the action to report
                    $action    = 'commented on';
                    $taskState = $data['current']['taskState'];
                    if ($commentAction === CommentModel::ACTION_ADD && $taskState !== CommentModel::TASK_COMMENT) {
                        $action = 'opened an issue on';
                    } elseif ($commentAction === CommentModel::ACTION_STATE_CHANGE) {
                        $oldState = isset($data['previous']['taskState'])
                            ? $data['previous']['taskState']
                            : null;
                        $transitions = array(
                            CommentModel::TASK_COMMENT   => 'cleared',
                            CommentModel::TASK_OPEN      => $oldState === CommentModel::TASK_COMMENT
                                ? 'opened'
                                : 'reopened',
                            CommentModel::TASK_ADDRESSED => 'addressed',
                            CommentModel::TASK_VERIFIED  => 'verified'
                        );

                        $action = isset($transitions[$taskState])
                            ? $transitions[$taskState] . ' an issue on'
                            : 'changed task state on';
                    } elseif ($commentAction === CommentModel::ACTION_EDIT) {
                        $action = 'edited a comment on';
                    }

                    // prepare comment info for activity streams
                    $activity = new Activity;
                    $activity->set(
                        array(
                            'type'          => 'comment',
                            'user'          => $data['user'] ?: $comment->get('user'),
                            'action'        => $action,
                            'target'        => $comment->get('topic'),
                            'description'   => $comment->get('body'),
                            'topic'         => $comment->get('topic'),
                            'depotFile'     => $context['file'],
                            'time'          => $comment->get('updated')
                        )
                    );
                    $event->setParam('activity', $activity);

                    // prepare attachment info for comment notification emails
                    if ($comment->get('attachments')) {
                        $event->setParam(
                            'attachments',
                            Attachment::fetchAll(
                                array(Attachment::FETCH_BY_IDS => $comment->get('attachments')),
                                $p4Admin
                            )
                        );
                    }

                    // default mail message subject is simply the topic name.
                    $subject = $comment->get('topic');

                    // enhance activity and mail info if we recognize the topic type
                    $to    = array();
                    $topic = $comment->get('topic');

                    // start by priming mentions with valid users in this new comment
                    // later we'll also add in @mentions from other locations
                    $mentions = Linkify::getCallouts($comment->get('body'));

                    // handle change comments
                    if (strpos($topic, 'changes/') === 0) {
                        $change = $context['change'] ?: end(explode('/', $topic));
                        $target = 'change ' . $change;
                        $hash   = 'comments';
                        if ($context['file']) {
                            $line    = isset($context['line']) ? ", line " .  $context['line'] : '';
                            $target .= " (" . File::decodeFilespec($context['name']) . $line . ")";
                            $hash    = $context['md5'] . ',c' . $comment->getId();
                        }

                        $activity->set('target', $target);
                        $activity->set('link',   array('change', array('change' => $change, 'fragment' => $hash)));

                        try {
                            $change = Change::fetch($change, $p4Admin);

                            // set 'change' field on activity, we want to ensure its the change id
                            // in theory it might be different from $change in case the change was renumbered
                            // and we got it from the topic as topics keep reference to the original id
                            $activity->set('change', $change->getId());

                            // change author, @mentions and project(s) should be notified
                            $to[]     = $change->getUser();
                            $mentions = array_merge($mentions, Linkify::getCallouts($change->getDescription()));
                            $activity->addFollowers($change->getUser());
                            $activity->addProjects(Project::getAffectedByChange($change, $p4Admin));

                            // enhance mail subject to use the change description (will be cropped)
                            $subject  = 'Change @' . $change->getId() . ' - '
                                      . $keywords->filter($change->getDescription());
                        } catch (\Exception $e) {
                            $services->get('logger')->err($e);
                        }
                    }

                    // handle review comments
                    if (strpos($topic, 'reviews/') === 0) {
                        $review = $context['review'] ?: end(explode('/', $topic));
                        $target = 'review ' . $review;
                        $hash   = 'comments';
                        if ($context['file']) {
                            $line    = isset($context['line']) ? ", line " .  $context['line'] : '';
                            $target .= " (" . File::decodeFilespec($context['name']) . $line . ")";
                            $hash    = $context['md5'] . ',c' . $comment->getId();
                        }

                        $activity->set('target', $target);
                        $activity->set('link',   array('review', array('review' => $review, 'fragment' => $hash)));

                        try {
                            $review = Review::fetch($review, $p4Admin);

                            // associate activity with review's head change so we can filter for restricted changes
                            $activity->set('change', $review->getHeadChange());

                            // add any folks that were @*mentioned as required reviewers
                            $review->addRequired(
                                User::filter(Linkify::getCallouts($comment->get('body'), true), $p4Admin)
                            );

                            // comment author and, valid, @mentioned users should be participants
                            $review->addParticipant($comment->get('user'))
                                   ->addParticipant(User::filter($mentions, $p4Admin))
                                   ->save();

                            // review comments should appear on the review stream
                            $activity->addStream('review-' . $review->getId());

                            // all review participants should be notified
                            $to = $review->getParticipants();
                            $activity->addFollowers($review->getParticipants());
                            $activity->addProjects($review->getProjects());

                            // enhance mail subject to use the review description (will be cropped)
                            $subject  = 'Review @' . $review->getId() . ' - '
                                      . $keywords->filter($review->get('description'));
                        } catch (\Exception $e) {
                            $services->get('logger')->err($e);
                        }
                    }

                    // handle job comments
                    if (strpos($topic, 'jobs/') === 0) {
                        $job    = end(explode('/', $topic));
                        $target = $job;
                        $hash   = 'comments';

                        $activity->set('target', $target);
                        $activity->set('link',   array('job', array('job' => $job, 'fragment' => $hash)));

                        try {
                            $job = Job::fetch($job, $p4Admin);

                            // add author, modifier and possibly others to the email recipients list
                            // we find users by looping through all job's defined fields and looking
                            // for default value of '$user'
                            $fields = $job->getSpecDefinition()->getFields();
                            foreach ($fields as $key => $field) {
                                if (isset($field['default']) && $field['default'] === '$user') {
                                    $to[] = $job->get($key);
                                }
                            }

                            // notify users mentioned in job's description
                            $to = array_merge($to, Linkify::getCallouts($job->getDescription()));

                            // associated change(s) users should also be notified
                            if (count($job->getChanges())) {
                                foreach ($job->getChangeObjects() as $change) {
                                    $to[] = $change->getUser();
                                }
                            }

                            // enhance mail subject to use the job description (will be cropped)
                            $subject = $job->getId() . ' - ' . $job->getDescription();
                        } catch (\Exception $e) {
                            $services->get('logger')->err($e);
                        }
                    }

                    // every user that participates in this comment thread
                    // should be notified of this activity (excluding author).
                    try {
                        // if the topic isn't a review we want to included any previous commenters and mentioned users.
                        // if the topic is a review, we skip this step and simply rely on the review participants,
                        // otherwise we might erroneously add back in removed reviewers
                        if (strpos($topic, 'reviews/') !== 0) {
                            // examine every comment on this topic to include:
                            // - all users who posted a comment to the topic
                            // - all users who were mentioned in a comment on this topic
                            $comments = CommentModel::fetchAll(array('topic' => $topic), $p4Admin);
                            $users    = array();
                            foreach ($comments as $entry) {
                                $users[]  = $entry->get('user');
                                $mentions = array_merge($mentions, Linkify::getCallouts($entry->get('body')));
                            }

                            $to = array_merge($to, $users);
                        }

                        // knock back the to list to only unique, valid ids
                        $to = array_unique(array_merge($to, $mentions));
                        $to = User::filter($to, $p4Admin);

                        // if we're emailing you its activity stream worthy, add em
                        $activity->addFollowers($to);

                        // don't email the person who carried out the action
                        $to = array_diff($to, array($data['user'] ?: $comment->get('user')));

                        // configure mail notification - only email for adds and edits
                        if (in_array($commentAction, array(CommentModel::ACTION_ADD, CommentModel::ACTION_EDIT))) {
                            $event->setParam(
                                'mail',
                                array(
                                    'subject'       => $subject,
                                    'cropSubject'   => 80,
                                    'toUsers'       => $to,
                                    'fromUser'      => $data['user'] ?: $comment->get('user'),
                                    'messageId'     => '<comment-' . $comment->getId() . '-' . time() . '@swarm>',
                                    'inReplyTo'     => '<topic-'   . $topic            . '@swarm>',
                                    'htmlTemplate'  => __DIR__ . '/view/mail/comment-html.phtml',
                                    'textTemplate'  => __DIR__ . '/view/mail/comment-text.phtml',
                                )
                            );
                        }
                    } catch (\Exception $e) {
                        $services->get('logger')->err($e);
                    }
                } catch (\Exception $e) {
                    $services->get('logger')->err($e);
                }
            },
            100
        );
    }

    public function getConfig()
    {
        return include __DIR__ . '/config/module.config.php';
    }

    public function getAutoloaderConfig()
    {
        return array(
            'Zend\Loader\StandardAutoloader' => array(
                'namespaces' => array(
                    __NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
                ),
            ),
        );
    }
}
# Change User Description Committed
#1 18334 Liz Lam initial add of jambox