[ 'instance' => [ 'name' => 'Instance URL', 'required' => true, 'exampleValue' => 'https://bugzilla.mozilla.org' ] ], 'Bug comments' => [ 'id' => [ 'name' => 'Bug tracking ID', 'type' => 'number', 'required' => true, 'title' => 'Insert bug tracking ID', 'exampleValue' => 121241 ], 'limit' => [ 'name' => 'Number of comments to return', 'type' => 'number', 'required' => false, 'title' => 'Specify number of comments to return', 'defaultValue' => -1 ], 'skiptags' => [ 'name' => 'Skip offtopic comments', 'type' => 'checkbox', 'title' => 'Excludes comments tagged as advocacy, metoo, or offtopic from the feed' ] ] ]; const SKIPPED_ACTIVITY = [ 'cc' => true, 'comment_tag' => true ]; const SKIPPED_TAGS = ['advocacy', 'metoo', 'offtopic']; private $instance; private $bugid; private $buguri; private $title; public function getName() { if (!is_null($this->title)) { return $this->title; } return parent::getName(); } public function getURI() { return $this->buguri ?? parent::getURI(); } public function collectData() { $this->instance = rtrim($this->getInput('instance'), '/'); $this->bugid = $this->getInput('id'); $this->buguri = $this->instance . '/show_bug.cgi?id=' . $this->bugid; $url = $this->instance . '/rest/bug/' . $this->bugid; $this->getTitle($url); $this->collectComments($url . '/comment'); $this->collectUpdates($url . '/history'); usort($this->items, function ($a, $b) { return $b['timestamp'] <=> $a['timestamp']; }); if ($this->getInput('limit') > 0) { $this->items = array_slice($this->items, 0, $this->getInput('limit')); } } protected function getTitle($url) { // Only request the summary for a faster request $json = self::getJSON($url . '?include_fields=summary'); $this->title = 'Bug ' . $this->bugid . ' - ' . $json['bugs'][0]['summary'] . ' - ' . // Remove https:// substr($this->instance, 8); } protected function collectComments($url) { $json = self::getJSON($url); // Array of comments is here if (!isset($json['bugs'][$this->bugid]['comments'])) { returnClientError('Cannot find REST endpoint'); } foreach ($json['bugs'][$this->bugid]['comments'] as $comment) { $item = []; if ( $this->getInput('skiptags') and array_intersect(self::SKIPPED_TAGS, $comment['tags']) ) { continue; } $item['categories'] = $comment['tags']; $item['uri'] = $this->buguri . '#c' . $comment['count']; $item['title'] = 'Comment ' . $comment['count']; $item['timestamp'] = $comment['creation_time']; $item['author'] = $this->getUser($comment['creator']); $item['content'] = $comment['text']; if (isset($comment['is_markdown']) and $comment['is_markdown']) { $item['content'] = markdownToHtml($item['content']); } if (!is_null($comment['attachment_id'])) { $item['enclosures'] = [$this->instance . '/attachment.cgi?id=' . $comment['attachment_id']]; } $this->items[] = $item; } } protected function collectUpdates($url) { $json = self::getJSON($url); // Array of changesets which contain an array of changes if (!isset($json['bugs']['0']['history'])) { returnClientError('Cannot find REST endpoint'); } foreach ($json['bugs']['0']['history'] as $changeset) { $author = $this->getUser($changeset['who']); $timestamp = $changeset['when']; foreach ($changeset['changes'] as $change) { // Skip updates to the cc list and comment tagging if (isset(self::SKIPPED_ACTIVITY[$change['field_name']])) { continue; } $item = []; $item['uri'] = $this->buguri; $item['title'] = 'Updated'; $item['timestamp'] = $timestamp; $item['author'] = $author; $item['content'] = ucfirst($change['field_name']) . ': ' . ($change['removed'] === '' ? '[nothing]' : $change['removed']) . ' -> ' . ($change['added'] === '' ? '[nothing]' : $change['added']); $this->items[] = $item; } } } protected function getUser($user) { // Check if the user endpoint is available if ($this->loadCacheValue($this->instance . 'userEndpointClosed')) { return $user; } $cache = $this->loadCacheValue($this->instance . $user); if ($cache) { return $cache; } $url = $this->instance . '/rest/user/' . $user . '?include_fields=real_name'; try { $json = self::getJSON($url); if (isset($json['error']) and $json['error']) { throw new Exception(); } } catch (Exception $e) { $this->saveCacheValue($this->instance . 'userEndpointClosed', true); return $user; } $username = $json['users']['0']['real_name']; if (empty($username)) { $username = $user; } $this->saveCacheValue($this->instance . $user, $username); return $username; } protected static function getJSON($url) { $headers = [ 'Accept: application/json', ]; return json_decode(getContents($url, $headers), true); } }