Beispiel #1
0
 /**
  * Prepare the response.
  *
  * @return  mixed
  *
  * @since   1.0
  * @throws  \Exception
  */
 protected function prepareResponse()
 {
     $this->getContainer()->get('app')->getUser()->authorize('create');
     $comment = $this->getContainer()->get('app')->input->get('text', '', 'raw');
     $issue_number = $this->getContainer()->get('app')->input->getInt('issue_number');
     if (!$issue_number) {
         throw new \Exception('No issue number received.');
     }
     if (!$comment) {
         throw new \Exception('You should write a comment first...');
     }
     // @todo removeMe :(
     $comment .= sprintf('<br /><br />*This comment was created with the <a href="%1$s">%2$s Application</a> at <a href="%3$s">%4$s</a>.*', 'https://github.com/joomla/jissues', 'J!Tracker', $this->getContainer()->get('app')->get('uri')->base->full, $this->getContainer()->get('app')->get('uri')->base->full);
     $project = $this->getContainer()->get('app')->getProject();
     /* @type \Joomla\Github\Github $github */
     $github = $this->getContainer()->get('gitHub');
     $data = new \stdClass();
     $db = $this->getContainer()->get('db');
     if ($project->gh_user && $project->gh_project) {
         $gitHubResponse = $github->issues->comments->create($project->gh_user, $project->gh_project, $issue_number, $comment);
         if (!isset($gitHubResponse->id)) {
             throw new \Exception('Invalid response from GitHub');
         }
         $data->created_at = $gitHubResponse->created_at;
         $data->opened_by = $gitHubResponse->user->login;
         $data->comment_id = $gitHubResponse->id;
         $data->text_raw = $gitHubResponse->body;
         $data->text = $github->markdown->render($comment, 'gfm', $project->gh_user . '/' . $project->gh_project);
     } else {
         $date = new Date();
         $data->created_at = $date->format($db->getDateFormat());
         $data->opened_by = $this->getContainer()->get('app')->getUser()->username;
         $data->comment_id = '???';
         $data->text_raw = $comment;
         $data->text = $github->markdown->render($comment, 'markdown');
     }
     $table = new ActivitiesTable($db);
     $table->event = 'comment';
     $table->created_date = $data->created_at;
     $table->project_id = $project->project_id;
     $table->issue_number = $issue_number;
     $table->gh_comment_id = $data->comment_id;
     $table->user = $data->opened_by;
     $table->text = $data->text;
     $table->text_raw = $data->text_raw;
     $table->store();
     $data->activities_id = $table->activities_id;
     $this->response->data = $data;
     $this->response->message = g11n3t('Your comment has been submitted');
 }
 /**
  * Method to update data for an issue from GitHub
  *
  * @param   integer  $id  The comment ID
  *
  * @return  boolean  True on success
  *
  * @since   1.0
  */
 protected function updateComment($id)
 {
     // Try to render the comment with GitHub markdown
     $parsedText = $this->parseText($this->hookData->comment->body);
     // Only update fields that may have changed, there's no API endpoint to show that so make some guesses
     $data = array();
     $data['activities_id'] = $id;
     $data['text'] = $parsedText;
     $data['text_raw'] = $this->hookData->comment->body;
     try {
         $table = new ActivitiesTable($this->db);
         $table->load(array('activities_id' => $id));
         $table->save($data);
     } catch (\Exception $e) {
         $this->logger->error('Error updating the database for comment ' . $id . ':' . $e->getMessage());
         $this->getContainer()->get('app')->close();
     }
     $this->triggerEvent('onCommentAfterUpdate', $table);
     // Store was successful, update status
     $this->logger->info(sprintf('Updated comment %s/%s #%d to the tracker.', $this->project->gh_user, $this->project->gh_project, $id));
     return true;
 }
Beispiel #3
0
 /**
  * Method to process the list of issues and inject into the database as needed
  *
  * @return  $this
  *
  * @since   1.0
  * @throws  \UnexpectedValueException
  */
 protected function processData()
 {
     if (!$this->items) {
         $this->logOut(g11n3t('Everything is up to date.'));
         return $this;
     }
     /* @type \Joomla\Database\DatabaseDriver $db */
     $db = $this->getContainer()->get('db');
     $query = $db->getQuery(true);
     $this->out(g11n3t('Adding events to the database...'), false);
     $progressBar = $this->getProgressBar(count($this->items));
     $this->usePBar ? $this->out() : null;
     $adds = 0;
     $count = 0;
     // Initialize our ActivitiesTable instance to insert the new record
     $table = new ActivitiesTable($db);
     foreach ($this->items as $issueId => $events) {
         $this->usePBar ? null : $this->out(sprintf(' #%d (%d/%d)...', $issueId, $count + 1, count($this->items)), false);
         foreach ($events as $event) {
             switch ($event->event) {
                 case 'referenced':
                 case 'closed':
                 case 'reopened':
                 case 'assigned':
                 case 'merged':
                 case 'head_ref_deleted':
                 case 'head_ref_restored':
                     $query->clear()->select($table->getKeyName())->from($db->quoteName('#__activities'))->where($db->quoteName('gh_comment_id') . ' = ' . (int) $event->id)->where($db->quoteName('project_id') . ' = ' . (int) $this->project->project_id);
                     $db->setQuery($query);
                     $id = (int) $db->loadResult();
                     $table->reset();
                     $table->{$table->getKeyName()} = null;
                     if ($id && !$this->force) {
                         if ($this->force) {
                             // Force update
                             $this->usePBar ? null : $this->out('F', false);
                             $table->{$table->getKeyName()} = $id;
                         } else {
                             // If we have something already, then move on to the next item
                             $this->usePBar ? null : $this->out('-', false);
                             continue;
                         }
                     } else {
                         $this->usePBar ? null : $this->out('+', false);
                     }
                     // Translate GitHub event names to "our" name schema
                     $evTrans = array('referenced' => 'reference', 'closed' => 'close', 'reopened' => 'reopen', 'assigned' => 'assign', 'merged' => 'merge', 'head_ref_deleted' => 'head_ref_deleted', 'head_ref_restored' => 'head_ref_restored');
                     $table->gh_comment_id = $event->id;
                     $table->issue_number = $issueId;
                     $table->project_id = $this->project->project_id;
                     $table->user = $event->actor->login;
                     $table->event = $evTrans[$event->event];
                     $table->created_date = (new Date($event->created_at))->format('Y-m-d H:i:s');
                     if ('referenced' == $event->event) {
                         // @todo obtain referenced information
                         /*
                         $reference = $this->github->issues->events->get(
                         	$this->project->gh_user, $this->project->gh_project, $event->id
                         );
                         
                         $this->checkGitHubRateLimit($this->github->issues->events->getRateLimitRemaining());
                         */
                     }
                     if ('assigned' == $event->event) {
                         $reference = $this->github->issues->events->get($this->project->gh_user, $this->project->gh_project, $event->id);
                         $table->text_raw = 'Assigned to ' . $reference->issue->assignee->login;
                         $table->text = $table->text_raw;
                         $this->checkGitHubRateLimit($this->github->issues->events->getRateLimitRemaining());
                     }
                     $table->store();
                     ++$adds;
                     break;
                 case 'mentioned':
                 case 'subscribed':
                 case 'unsubscribed':
                     continue;
                 default:
                     $this->logOut(sprintf('ERROR: Unknown Event: %s', $event->event));
                     continue;
             }
         }
         ++$count;
         $this->usePBar ? $progressBar->update($count) : null;
     }
     $this->out()->outOK()->logOut(sprintf(g11n3t('Added %d new issue events to the database'), $adds));
     return $this;
 }
 /**
  * Add a new event and store it to the database.
  *
  * @param   string   $event       The event name.
  * @param   string   $dateTime    Date and time.
  * @param   string   $userName    User name.
  * @param   integer  $projectId   Project id.
  * @param   integer  $itemNumber  THE item number.
  * @param   integer  $commentId   The comment id
  * @param   string   $text        The parsed html comment text.
  * @param   string   $textRaw     The raw comment text.
  *
  * @return  $this
  *
  * @since   1.0
  */
 protected function addActivityEvent($event, $dateTime, $userName, $projectId, $itemNumber, $commentId = null, $text = '', $textRaw = '')
 {
     $data = array();
     $date = new Date($dateTime);
     $data['created_date'] = $date->format($this->db->getDateFormat());
     $data['event'] = $event;
     $data['user'] = $userName;
     $data['project_id'] = (int) $projectId;
     $data['issue_number'] = (int) $itemNumber;
     $data['gh_comment_id'] = (int) $commentId;
     $data['text'] = $text;
     $data['text_raw'] = $textRaw;
     try {
         $activity = new ActivitiesTable($this->db);
         $activity->save($data);
     } catch (\Exception $exception) {
         $this->logger->info(sprintf('Error storing %s activity to the database (ProjectId: %d, ItemNo: %d): %s', $event, $projectId, $itemNumber, $exception->getMessage()));
         $this->getContainer()->get('app')->close();
     }
     return $this;
 }
Beispiel #5
0
 /**
  * Get an item.
  *
  * @param   integer  $identifier  The item identifier.
  *
  * @return  IssuesTable
  *
  * @since   1.0
  * @throws  \RuntimeException
  */
 public function getItem($identifier)
 {
     if (!$identifier) {
         throw new \RuntimeException('No id given');
     }
     $item = $this->db->setQuery($this->db->getQuery(true)->select('i.*')->from($this->db->quoteName('#__issues', 'i'))->where($this->db->quoteName('i.project_id') . ' = ' . (int) $this->getProject()->project_id)->where($this->db->quoteName('i.issue_number') . ' = ' . (int) $identifier)->select($this->db->quoteName('s.status', 'status_title'))->select($this->db->quoteName('s.closed', 'closed'))->leftJoin($this->db->quoteName('#__status', 's') . ' ON ' . $this->db->quoteName('i.status') . ' = ' . $this->db->quoteName('s.id'))->select('a1.title AS rel_title, a1.status AS rel_status')->join('LEFT', '#__issues AS a1 ON i.rel_number = a1.issue_number AND a1.project_id = ' . (int) $this->getProject()->project_id)->select('s1.closed AS rel_closed')->join('LEFT', '#__status AS s1 ON a1.status = s1.id')->select('t.name AS rel_name')->join('LEFT', '#__issues_relations_types AS t ON i.rel_type = t.id')->select('m.title AS milestone_title')->join('LEFT', '#__tracker_milestones AS m ON m.milestone_id = i.milestone_id')->select('u.id AS user_id')->leftJoin('#__users AS u ON i.opened_by = u.username'))->loadObject();
     if (!$item) {
         throw new \RuntimeException('Invalid Issue', 1);
     }
     // Fetch activities
     $table = new ActivitiesTable($this->db);
     $query = $this->db->getQuery(true);
     $query->select('a.*');
     $query->from($this->db->quoteName($table->getTableName(), 'a'));
     $query->where($this->db->quoteName('a.project_id') . ' = ' . (int) $this->getProject()->project_id);
     $query->where($this->db->quoteName('a.issue_number') . ' = ' . (int) $item->issue_number);
     $query->order($this->db->quoteName('a.created_date'));
     $activityData = $this->db->setQuery($query)->loadObjectList();
     $commits = json_decode($item->commits) ?: [];
     $activities = [];
     // Store the last commit to fetch the test results later
     $lastCommit = end($commits);
     foreach ($activityData as $i => $activity) {
         foreach ($commits as $i1 => $commit) {
             $d1 = new \DateTime($commit->committer_date);
             $d2 = new \DateTime($activity->created_date, new \DateTimeZone('UTC'));
             if ($d1 < $d2) {
                 $m = explode("\n", $commit->message);
                 $a = new \stdClass();
                 $a->event = 'commit';
                 $a->user = $commit->author_name;
                 $a->text = $m[0];
                 $a->created_date = $commit->committer_date;
                 $a->activities_id = $commit->sha;
                 $activities[] = $a;
                 unset($commits[$i1]);
             }
         }
         $activities[] = $activity;
     }
     $item->activities = $activities;
     // Fetch foreign relations
     $item->relations_f = $this->db->setQuery($this->db->getQuery(true)->from($this->db->quoteName('#__issues', 'a'))->join('LEFT', '#__issues_relations_types AS t ON a.rel_type = t.id')->join('LEFT', '#__status AS s ON a.status = s.id')->select('a.issue_number, a.title, a.rel_type')->select('t.name AS rel_name')->select('s.status AS status_title, s.closed AS closed')->where($this->db->quoteName('a.rel_number') . '=' . (int) $item->issue_number)->order(array('a.issue_number', 'a.rel_type')))->loadObjectList();
     // Group relations by type
     if ($item->relations_f) {
         $arr = array();
         foreach ($item->relations_f as $relation) {
             if (false == isset($arr[$relation->rel_name])) {
                 $arr[$relation->rel_name] = array();
             }
             $arr[$relation->rel_name][] = $relation;
         }
         $item->relations_f = $arr;
     }
     // Fetch the voting data
     $query->clear()->select('COUNT(id) AS votes, SUM(experienced) AS experienced, SUM(score) AS score')->from($this->db->quoteName('#__issues_voting'))->where($this->db->quoteName('issue_number') . ' = ' . (int) $item->id);
     $voteData = $this->db->setQuery($query)->loadObject();
     $item->votes = $voteData->votes;
     $item->experienced = $voteData->experienced;
     $item->score = $voteData->score;
     // Set the score if we have votes
     if ($item->votes > 0) {
         $item->importanceScore = $item->score / $item->votes;
     } else {
         $item->importanceScore = 0;
     }
     // Decode the merge status
     $item->gh_merge_status = json_decode($item->gh_merge_status);
     // Fetch test data
     if ($lastCommit) {
         $item->testsSuccess = $this->db->setQuery($query->clear()->select('username')->from($this->db->quoteName('#__issues_tests'))->where($this->db->quoteName('item_id') . ' = ' . (int) $item->id)->where($this->db->quoteName('result') . ' = 1')->where($this->db->quoteName('sha') . ' = ' . $this->db->quote($lastCommit->sha)))->loadColumn();
         sort($item->testsSuccess);
         $item->testsFailure = $this->db->setQuery($query->clear()->select('username')->from($this->db->quoteName('#__issues_tests'))->where($this->db->quoteName('item_id') . ' = ' . (int) $item->id)->where($this->db->quoteName('result') . ' = 2')->where($this->db->quoteName('sha') . ' = ' . $this->db->quote($lastCommit->sha)))->loadColumn();
         sort($item->testsFailure);
     }
     // Fetch category
     $item->categories = $this->db->setQuery($query->clear()->select('a.title, a.id, a.color, a.alias')->from($this->db->quoteName('#__issues_categories', 'a'))->innerJoin($this->db->quoteName('#__issue_category_map', 'b') . ' ON b.category_id = a.id')->where('b.issue_id =' . (int) $item->id))->loadObjectList();
     return $item;
 }
Beispiel #6
0
 /**
  * Compute the changes.
  *
  * @return  $this  Method allows chaining
  *
  * @since   1.0
  */
 private function processChanges()
 {
     $changes = array();
     foreach ($this as $fName => $field) {
         if (!$this->{$fName} && !$this->oldObject->{$fName}) {
             // Both values are "empty"
             continue;
         }
         if ($this->{$fName} != $this->oldObject->{$fName}) {
             $change = new \stdClass();
             $change->name = $fName;
             $change->old = $this->oldObject->{$fName};
             $change->new = $this->{$fName};
             switch ($fName) {
                 case 'modified_date':
                 case 'modified_by':
                     // Expected change ;)
                     break;
                 case 'description':
                     // Do nothing
                     break;
                 default:
                     $changes[] = $change;
                     break;
             }
         }
     }
     if ($changes) {
         $data = array();
         $data['event'] = 'change';
         $data['created_date'] = $this->modified_date;
         $data['user'] = $this->modified_by;
         $data['issue_number'] = (int) $this->issue_number;
         $data['project_id'] = (int) $this->project_id;
         $data['text'] = json_encode($changes);
         $table = new ActivitiesTable($this->db);
         $table->save($data);
     }
     return $this;
 }
Beispiel #7
0
 /**
  * Process the change in category for issues.
  *
  * @param   array  $src  The source, should include: $src['issue_number'], the issue's number; $src['project_id'],
  *                       the issue's project id; $src['old'] and $src['new'] for old and new categories; $src['modified_by'],
  *                       modified username.
  *
  * @since   1.0
  *
  * @return  $this
  */
 private function processChanges(array $src)
 {
     $date = new Date();
     $date = $date->format($this->getDb()->getDateFormat());
     $change = new \stdClass();
     $change->name = 'category';
     $change->old = array();
     $change->new = array();
     foreach ($src['old'] as $key => $old) {
         $oldCategory = $this->getItem($old);
         $change->old[$key]['title'] = $oldCategory->title;
         $change->old[$key]['color'] = $oldCategory->color;
     }
     foreach ($src['new'] as $key => $new) {
         $newCategory = $this->getItem($new);
         $change->new[$key]['title'] = $newCategory->title;
         $change->new[$key]['color'] = $newCategory->color;
     }
     $data = array();
     $data['event'] = 'change';
     $data['created_date'] = $date;
     $data['user'] = $src['modified_by'];
     $data['issue_number'] = (int) $src['issue_number'];
     $data['project_id'] = (int) $src['project_id'];
     $data['text'] = json_encode(array($change));
     $table = new ActivitiesTable($this->getDb());
     $table->save($data);
     return $this;
 }
Beispiel #8
0
 /**
  * Execute the controller.
  *
  * @return  string  The rendered view.
  *
  * @since   1.0
  * @throws  \JTracker\Authentication\Exception\AuthenticationException
  * @throws  \RuntimeException
  * @throws  \UnexpectedValueException
  */
 public function execute()
 {
     /* @type \JTracker\Application $application */
     $application = $this->getContainer()->get('app');
     $src = $application->input->get('item', array(), 'array');
     $user = $application->getUser();
     $project = $application->getProject();
     $model = new IssueModel($this->getContainer()->get('db'));
     $model->setProject($project);
     $issueNumber = isset($src['issue_number']) ? (int) $src['issue_number'] : 0;
     if (!$issueNumber) {
         throw new \UnexpectedValueException('No issue number received.');
     }
     $item = $model->getItem($issueNumber);
     $data = array();
     if ($user->check('edit')) {
         // The user has full "edit" permission.
         $data = $src;
         // Allow admins to update labels and milestones
         if (!$user->check('manage')) {
             if (!empty($item->labels)) {
                 $data['labels'] = explode(',', $item->labels);
             }
             $data['milestone_id'] = $item->milestone_id;
         }
     } elseif ($user->canEditOwn($item->opened_by)) {
         // The user has "edit own" permission.
         $data['id'] = (int) $src['id'];
         $data['issue_number'] = (int) $src['issue_number'];
         $data['title'] = $src['title'];
         $data['description_raw'] = $src['description_raw'];
         // Take the remaining values from the stored item
         if (!empty($item->labels)) {
             $data['labels'] = explode(',', $item->labels);
         }
         $data['status'] = $item->status;
         $data['priority'] = $item->priority;
         $data['build'] = $item->build;
         $data['rel_number'] = $item->rel_number;
         $data['rel_type'] = $item->rel_type;
         $data['easy'] = $item->easy;
         $data['milestone_id'] = $item->milestone_id;
     } else {
         // The user has no "edit" permission.
         throw new AuthenticationException($user, 'edit');
     }
     $gitHub = GithubFactory::getInstance($application);
     // Check if the state has changed (e.g. open/closed)
     $oldState = $model->getOpenClosed($item->status);
     $state = $model->getOpenClosed($data['status']);
     // Project is managed on GitHub
     if ($project->gh_user && $project->gh_project) {
         // @todo assignee
         $assignee = null;
         // Prepare labels
         $ghLabels = [];
         if (!empty($data['labels'])) {
             foreach ($project->getLabels() as $id => $label) {
                 if (in_array($id, $data['labels'])) {
                     $ghLabels[] = $label->name;
                 }
             }
         }
         // Prepare milestone
         $ghMilestone = null;
         if (!empty($data['milestone_id'])) {
             foreach ($project->getMilestones() as $milestone) {
                 if ($milestone->milestone_id == $data['milestone_id']) {
                     $ghMilestone = $milestone->milestone_number;
                 }
             }
         }
         try {
             $gitHubResponse = $this->updateGitHub($item->issue_number, $data, $state, $oldState, $assignee, $ghMilestone, $ghLabels);
             // Set the modified_date from GitHub (important!)
             $data['modified_date'] = $gitHubResponse->updated_at;
         } catch (GithubException $exception) {
             $this->getContainer()->get('app')->getLogger()->error(sprintf('Error code %1$s received from GitHub when editing an issue with the following data:' . ' GitHub User: %2$s; GitHub Repo: %3$s; Issue Number: %4$s; State: %5$s, Old state: %6$s' . '  The error message returned was: %7$s', $exception->getCode(), $project->gh_user, $project->gh_project, $item->issue_number, $state, $oldState, $exception->getMessage()));
             throw new \RuntimeException('Invalid response from GitHub');
         }
         // Render the description text using GitHub's markdown renderer.
         $data['description'] = $gitHub->markdown->render($data['description_raw'], 'gfm', $project->gh_user . '/' . $project->gh_project);
     } else {
         // Project is managed by JTracker only
         // Render the description text using GitHub's markdown renderer.
         $data['description'] = $gitHub->markdown->render($src['description_raw'], 'markdown');
         $data['modified_date'] = (new Date())->format($this->getContainer()->get('db')->getDateFormat());
     }
     try {
         $data['modified_by'] = $user->username;
         // If the user have edit permission, let him / her modify the categories.
         if ($user->check('edit')) {
             $categoryModel = new CategoryModel($this->getContainer()->get('db'));
             $category['issue_id'] = $data['id'];
             $category['modified_by'] = $user->username;
             $category['categories'] = $application->input->get('categories', null, 'array');
             $category['issue_number'] = $data['issue_number'];
             $category['project_id'] = $project->project_id;
             $categoryModel->updateCategory($category);
         }
         // Pass the old and new states into the save method
         $data['old_state'] = $oldState;
         $data['new_state'] = $state;
         // Values that are not supposed to change.
         $data['commits'] = $item->commits;
         $data['pr_head_sha'] = $item->pr_head_sha;
         // Save the record.
         $model->save($data);
         $comment = $application->input->get('comment', '', 'raw');
         // Save the comment.
         if ($comment) {
             /* @type \JTracker\Github\Github $github */
             $github = $this->getContainer()->get('gitHub');
             $project = $application->getProject();
             $gitHubHelper = new GitHubHelper($github);
             $comment .= $gitHubHelper->getApplicationComment($application, $project, $issueNumber);
             $data = new \stdClass();
             $db = $this->getContainer()->get('db');
             if ($project->gh_user && $project->gh_project) {
                 $gitHubResponse = $github->issues->comments->create($project->gh_user, $project->gh_project, $issueNumber, $comment);
                 if (!isset($gitHubResponse->id)) {
                     throw new \RuntimeException('Invalid response from GitHub');
                 }
                 $data->created_at = $gitHubResponse->created_at;
                 $data->opened_by = $gitHubResponse->user->login;
                 $data->comment_id = $gitHubResponse->id;
                 $data->text_raw = $gitHubResponse->body;
                 $data->text = $github->markdown->render($comment, 'gfm', $project->gh_user . '/' . $project->gh_project);
             } else {
                 $date = new Date();
                 $data->created_at = $date->format($db->getDateFormat());
                 $data->opened_by = $application->getUser()->username;
                 $data->comment_id = '???';
                 $data->text_raw = $comment;
                 $data->text = $github->markdown->render($comment, 'markdown');
             }
             $table = new ActivitiesTable($db);
             $table->event = 'comment';
             $table->created_date = $data->created_at;
             $table->project_id = $project->project_id;
             $table->issue_number = $issueNumber;
             $table->gh_comment_id = $data->comment_id;
             $table->user = $data->opened_by;
             $table->text = $data->text;
             $table->text_raw = $data->text_raw;
             $table->store();
         }
         $application->enqueueMessage('The changes have been saved.', 'success')->redirect('/tracker/' . $application->input->get('project_alias') . '/' . $issueNumber);
     } catch (\RuntimeException $exception) {
         $application->enqueueMessage($exception->getMessage(), 'error');
         // @todo preserve data when returning to edit view on failure.
         $application->redirect($application->get('uri.base.path') . 'tracker/' . $application->input->get('project_alias') . '/' . $issueNumber . '/edit');
     }
     return parent::execute();
 }
Beispiel #9
0
 /**
  * Method to process the list of issues and inject into the database as needed
  *
  * @return  $this
  *
  * @since   1.0
  */
 protected function processData()
 {
     if (!$this->items) {
         $this->logOut(g11n3t('Everything is up to date.'));
         return $this;
     }
     /* @type \Joomla\Database\DatabaseDriver $db */
     $db = $this->getContainer()->get('db');
     // Initialize our query object
     $query = $db->getQuery(true);
     $this->out(sprintf(g11n4t('Processing comments for one modified issue...', 'Processing comments for %d modified issues...', count($this->items)), count($this->items)));
     $adds = 0;
     $updates = 0;
     $count = 1;
     // Initialize our ActivitiesTable instance to insert the new record
     $table = new ActivitiesTable($db);
     // Comments ids for computing the difference
     $commentsIds = array();
     // Comments ids to delete
     $toDelete = array();
     // Start processing the comments now
     foreach ($this->items as $issueNumber => $comments) {
         if (!count($comments)) {
             $this->out()->out(sprintf(g11n3t('No comments for issue # %d'), $issueNumber));
         } else {
             $this->out()->out(sprintf(g11n4t('Processing one comment for issue # %2$d (%3$d/%4$d)', 'Processing %1$d comments for issue # %2$d (%3$d/%4$d)', count($comments)), count($comments), $issueNumber, $count, count($this->items)));
             $progressBar = $this->getProgressBar(count($comments));
             $this->usePBar ? $this->out() : null;
             foreach ($comments as $i => $comment) {
                 // Store the comment id for computing the difference
                 $commentsIds[] = $comment->id;
                 $check = $db->setQuery($query->clear()->select($table->getKeyName())->select($db->quoteName('updated_date'))->from($db->quoteName($table->getTableName()))->where($db->quoteName('gh_comment_id') . ' = ' . (int) $comment->id)->where($db->quoteName('project_id') . ' = ' . (int) $this->project->project_id))->loadObject();
                 if ($check) {
                     if (!$this->force) {
                         // If we have something already, check if it needs an update...
                         $d1 = new Date($check->updated_date);
                         $d2 = new Date($comment->updated_at);
                         if ($d1 == $d2) {
                             // No update required
                             $this->usePBar ? $progressBar->update($i + 1) : $this->out('-', false);
                             continue;
                         }
                     }
                     $table->load($check->{$table->getKeyName()});
                     $this->usePBar ? null : $this->out($this->force ? 'F ' : '~ ', false);
                 } else {
                     // New item
                     $table->reset();
                     $table->{$table->getKeyName()} = null;
                     $this->usePBar ? null : $this->out('+', false);
                 }
                 $table->gh_comment_id = $comment->id;
                 $table->issue_number = (int) $issueNumber;
                 $table->project_id = $this->project->project_id;
                 $table->user = $comment->user->login;
                 $table->event = 'comment';
                 $table->text_raw = $comment->body;
                 $table->text = $this->github->markdown->render($comment->body, 'gfm', $this->project->gh_user . '/' . $this->project->gh_project);
                 $this->checkGitHubRateLimit($this->github->markdown->getRateLimitRemaining());
                 $table->created_date = (new Date($comment->created_at))->format('Y-m-d H:i:s');
                 $table->updated_date = (new Date($comment->updated_at))->format('Y-m-d H:i:s');
                 $table->store();
                 if ($check) {
                     ++$updates;
                 } else {
                     ++$adds;
                 }
                 $this->usePBar ? $progressBar->update($i + 1) : null;
             }
             ++$count;
         }
         // Compute the difference between GitHub comments and issue comments
         $issueComments = $this->getIssueCommentsIds($issueNumber);
         $commentsToDelete = array_diff($issueComments, $commentsIds);
         $toDelete = array_merge($toDelete, $commentsToDelete);
     }
     // Delete comments which does not exist on GitHub
     if (!empty($toDelete)) {
         $this->deleteIssuesComments($toDelete);
     }
     $this->out()->outOK()->logOut(sprintf(g11n3t('%1$d added, %2$d updated, %3$d deleted.'), $adds, $updates, count($toDelete)));
     return $this;
 }