diff options
-rw-r--r-- | apps/comments/appinfo/app.php | 10 | ||||
-rw-r--r-- | apps/comments/appinfo/info.xml | 14 | ||||
-rw-r--r-- | apps/comments/js/activitytabviewplugin.js | 2 | ||||
-rw-r--r-- | apps/comments/lib/Activity/Extension.php | 342 | ||||
-rw-r--r-- | apps/comments/lib/Activity/Filter.php | 90 | ||||
-rw-r--r-- | apps/comments/lib/Activity/Listener.php | 10 | ||||
-rw-r--r-- | apps/comments/lib/Activity/Provider.php | 198 | ||||
-rw-r--r-- | apps/comments/lib/Activity/Setting.php | 98 |
8 files changed, 407 insertions, 357 deletions
diff --git a/apps/comments/appinfo/app.php b/apps/comments/appinfo/app.php index 66e60dbbd85..144a4506d38 100644 --- a/apps/comments/appinfo/app.php +++ b/apps/comments/appinfo/app.php @@ -37,14 +37,6 @@ $eventDispatcher->addListener( } ); -$activityManager = \OC::$server->getActivityManager(); -$activityManager->registerExtension(function() { - $application = new \OCP\AppFramework\App('comments'); - /** @var \OCA\Comments\Activity\Extension $extension */ - $extension = $application->getContainer()->query(\OCA\Comments\Activity\Extension::class); - return $extension; -}); - $eventDispatcher->addListener(\OCP\Comments\CommentsEntityEvent::EVENT_ENTITY, function(\OCP\Comments\CommentsEntityEvent $event) { $event->addEntityCollection('files', function($name) { $nodes = \OC::$server->getUserFolder()->getById(intval($name)); @@ -67,7 +59,7 @@ $notificationManager->registerNotifier( $commentsManager = \OC::$server->getCommentsManager(); $commentsManager->registerEventHandler(function () { $application = new \OCP\AppFramework\App('comments'); - /** @var \OCA\Comments\Activity\Extension $extension */ + /** @var \OCA\Comments\EventHandler $handler */ $handler = $application->getContainer()->query(\OCA\Comments\EventHandler::class); return $handler; }); diff --git a/apps/comments/appinfo/info.xml b/apps/comments/appinfo/info.xml index 1c0a38e5668..9d5409a2d62 100644 --- a/apps/comments/appinfo/info.xml +++ b/apps/comments/appinfo/info.xml @@ -13,4 +13,18 @@ <types> <logging/> </types> + + <activity> + <settings> + <setting>OCA\Comments\Activity\Setting</setting> + </settings> + + <filters> + <filter>OCA\Comments\Activity\Filter</filter> + </filters> + + <providers> + <provider>OCA\Comments\Activity\Provider</provider> + </providers> + </activity> </info> diff --git a/apps/comments/js/activitytabviewplugin.js b/apps/comments/js/activitytabviewplugin.js index ca3253bd137..b6195b80c45 100644 --- a/apps/comments/js/activitytabviewplugin.js +++ b/apps/comments/js/activitytabviewplugin.js @@ -25,7 +25,7 @@ if (view === 'ActivityTabView') { $el.addClass('comment'); - if (this._isLong(model.get('message_prepared'))) { + if (model.get('message') && this._isLong(model.get('message'))) { $el.addClass('collapsed'); var $overlay = $('<div>').addClass('message-overlay'); $el.find('.activitymessage').after($overlay); diff --git a/apps/comments/lib/Activity/Extension.php b/apps/comments/lib/Activity/Extension.php deleted file mode 100644 index 2a155dd0064..00000000000 --- a/apps/comments/lib/Activity/Extension.php +++ /dev/null @@ -1,342 +0,0 @@ -<?php -/** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ - -namespace OCA\Comments\Activity; - -use OCP\Activity\IExtension; -use OCP\Activity\IManager; -use OCP\Comments\ICommentsManager; -use OCP\Comments\NotFoundException; -use OCP\IL10N; -use OCP\IURLGenerator; -use OCP\L10N\IFactory; -use OCP\Util; - -/** - * Class Extension - * - * @package OCA\Comments\Activity - */ -class Extension implements IExtension { - const APP_NAME = 'comments'; - - const ADD_COMMENT_SUBJECT = 'add_comment_subject'; - const ADD_COMMENT_MESSAGE = 'add_comment_message'; - - /** @var IFactory */ - protected $languageFactory; - - /** @var IManager */ - protected $activityManager; - - /** @var ICommentsManager */ - protected $commentsManager; - - /** @var IURLGenerator */ - protected $URLGenerator; - - /** - * @param IFactory $languageFactory - * @param IManager $activityManager - * @param ICommentsManager $commentsManager - * @param IURLGenerator $URLGenerator - */ - public function __construct(IFactory $languageFactory, IManager $activityManager, ICommentsManager $commentsManager, IURLGenerator $URLGenerator) { - $this->languageFactory = $languageFactory; - $this->activityManager = $activityManager; - $this->commentsManager = $commentsManager; - $this->URLGenerator = $URLGenerator; - } - - protected function getL10N($languageCode = null) { - return $this->languageFactory->get(self::APP_NAME, $languageCode); - } - - /** - * The extension can return an array of additional notification types. - * If no additional types are to be added false is to be returned - * - * @param string $languageCode - * @return array|false - */ - public function getNotificationTypes($languageCode) { - $l = $this->getL10N($languageCode); - - return array( - self::APP_NAME => [ - 'desc' => (string) $l->t('<strong>Comments</strong> for files'), - 'methods' => [self::METHOD_MAIL, self::METHOD_STREAM], - ], - ); - } - - /** - * For a given method additional types to be displayed in the settings can be returned. - * In case no additional types are to be added false is to be returned. - * - * @param string $method - * @return array|false - */ - public function getDefaultTypes($method) { - return $method === self::METHOD_STREAM ? [self::APP_NAME] : false; - } - - /** - * A string naming the css class for the icon to be used can be returned. - * If no icon is known for the given type false is to be returned. - * - * @param string $type - * @return string|false - */ - public function getTypeIcon($type) { - switch ($type) { - case self::APP_NAME: - return 'icon-comment'; - } - - return false; - } - - /** - * The extension can translate a given message to the requested languages. - * If no translation is available false is to be returned. - * - * @param string $app - * @param string $text - * @param array $params - * @param boolean $stripPath - * @param boolean $highlightParams - * @param string $languageCode - * @return string|false - */ - public function translate($app, $text, $params, $stripPath, $highlightParams, $languageCode) { - if ($app !== self::APP_NAME) { - return false; - } - - $l = $this->getL10N($languageCode); - - if ($this->activityManager->isFormattingFilteredObject()) { - $translation = $this->translateShort($text, $l, $params); - if ($translation !== false) { - return $translation; - } - } - - return $this->translateLong($text, $l, $params); - } - - /** - * @param string $text - * @param IL10N $l - * @param array $params - * @return bool|string - */ - protected function translateShort($text, IL10N $l, array $params) { - - switch ($text) { - case self::ADD_COMMENT_SUBJECT: - if ($this->authorIsCurrentUser($params[0])) { - return (string) $l->t('You commented'); - } - return (string) $l->t('%1$s commented', $params); - case self::ADD_COMMENT_MESSAGE: - return $this->convertParameterToComment($params[0]); - } - - return false; - } - - /** - * @param string $text - * @param IL10N $l - * @param array $params - * @return bool|string - */ - protected function translateLong($text, IL10N $l, array $params) { - - switch ($text) { - case self::ADD_COMMENT_SUBJECT: - if ($this->authorIsCurrentUser($params[0])) { - return (string) $l->t('You commented on %2$s', $params); - } - return (string) $l->t('%1$s commented on %2$s', $params); - case self::ADD_COMMENT_MESSAGE: - return $this->convertParameterToComment($params[0]); - } - - return false; - } - - /** - * Check if the author is the current user - * - * @param string $user Parameter e.g. `<user display-name="admin">admin</user>` - * @return bool - */ - protected function authorIsCurrentUser($user) { - try { - return strip_tags($user) === $this->activityManager->getCurrentUserId(); - } catch (\UnexpectedValueException $e) { - return false; - } - } - - /** - * The extension can define the type of parameters for translation - * - * Currently known types are: - * * file => will strip away the path of the file and add a tooltip with it - * * username => will add the avatar of the user - * - * @param string $app - * @param string $text - * @return array|false - */ - public function getSpecialParameterList($app, $text) { - if ($app === self::APP_NAME) { - switch ($text) { - case self::ADD_COMMENT_SUBJECT: - return [ - 0 => 'username', - 1 => 'file', - ]; - } - } - - return false; - } - - /** - * The extension can define the parameter grouping by returning the index as integer. - * In case no grouping is required false is to be returned. - * - * @param array $activity - * @return integer|false - */ - public function getGroupParameter($activity) { - return false; - } - - /** - * The extension can define additional navigation entries. The array returned has to contain two keys 'top' - * and 'apps' which hold arrays with the relevant entries. - * If no further entries are to be added false is no be returned. - * - * @return array|false - */ - public function getNavigation() { - $l = $this->getL10N(); - return [ - 'apps' => [ - self::APP_NAME => [ - 'id' => self::APP_NAME, - 'icon' => 'icon-comment', - 'name' => (string) $l->t('Comments'), - 'url' => $this->URLGenerator->linkToRoute('activity.Activities.showList', ['filter' => self::APP_NAME]), - ], - ], - 'top' => [], - ]; - } - - /** - * The extension can check if a custom filter (given by a query string like filter=abc) is valid or not. - * - * @param string $filterValue - * @return boolean - */ - public function isFilterValid($filterValue) { - return $filterValue === self::APP_NAME; - } - - /** - * The extension can filter the types based on the filter if required. - * In case no filter is to be applied false is to be returned unchanged. - * - * @param array $types - * @param string $filter - * @return array|false - */ - public function filterNotificationTypes($types, $filter) { - if ($filter === self::APP_NAME) { - return array_intersect($types, [self::APP_NAME]); - } - return false; - } - - /** - * For a given filter the extension can specify the sql query conditions including parameters for that query. - * In case the extension does not know the filter false is to be returned. - * The query condition and the parameters are to be returned as array with two elements. - * E.g. return array('`app` = ? and `message` like ?', array('mail', 'ownCloud%')); - * - * @param string $filter - * @return array|false - */ - public function getQueryForFilter($filter) { - return false; - } - - /** - * @param string $parameter - * @return string - */ - protected function convertParameterToComment($parameter) { - if (preg_match('/^\<parameter\>(\d*)\<\/parameter\>$/', $parameter, $matches)) { - try { - $comment = $this->commentsManager->get((int) $matches[1]); - $message = $comment->getMessage(); - $message = str_replace("\n", '<br />', str_replace(['<', '>'], ['<', '>'], $message)); - - foreach ($comment->getMentions() as $mention) { - if ($mention['type'] !== 'user') { - continue; - } - - try { - $displayName = $this->commentsManager->resolveDisplayName($mention['type'], $mention['id']); - } catch (\OutOfBoundsException $e) { - // No displayname, upon client's discretion what to display. - $displayName = $mention['id']; - } - - $message = preg_replace( - '/(^|\s)(' . '@' . $mention['id'] . ')(\b)/', - '${1}' . $this->regexSafeUser($mention['id'], $displayName) . '${3}', - $message - ); - } - return $message; - } catch (NotFoundException $e) { - return ''; - } - } - - return ''; - } - - protected function regexSafeUser($uid, $displayName) { - // FIXME evil internal API hackery, do NOT copy this - return str_replace('$', '\$', '<user display-name="' . Util::sanitizeHTML($displayName) . '">' . Util::sanitizeHTML($uid) . '</user>'); - } -} diff --git a/apps/comments/lib/Activity/Filter.php b/apps/comments/lib/Activity/Filter.php new file mode 100644 index 00000000000..b9417b6b236 --- /dev/null +++ b/apps/comments/lib/Activity/Filter.php @@ -0,0 +1,90 @@ +<?php +/** + * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\Comments\Activity; + + +use OCP\Activity\IFilter; +use OCP\IL10N; +use OCP\IURLGenerator; + +class Filter implements IFilter { + + /** @var IL10N */ + protected $l; + + /** @var IURLGenerator */ + protected $url; + + public function __construct(IL10N $l, IURLGenerator $url) { + $this->l = $l; + $this->url = $url; + } + + /** + * @return string Lowercase a-z only identifier + * @since 11.0.0 + */ + public function getIdentifier() { + return 'comments'; + } + + /** + * @return string A translated string + * @since 11.0.0 + */ + public function getName() { + return $this->l->t('Comments'); + } + + /** + * @return int + * @since 11.0.0 + */ + public function getPriority() { + return 40; + } + + /** + * @return string Full URL to an icon, empty string when none is given + * @since 11.0.0 + */ + public function getIcon() { + return $this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/comment.svg')); + } + + /** + * @param string[] $types + * @return string[] An array of allowed apps from which activities should be displayed + * @since 11.0.0 + */ + public function filterTypes(array $types) { + return $types; + } + + /** + * @return string[] An array of allowed apps from which activities should be displayed + * @since 11.0.0 + */ + public function allowedApps() { + return ['comments']; + } +} diff --git a/apps/comments/lib/Activity/Listener.php b/apps/comments/lib/Activity/Listener.php index ad1e2774148..2ac49144690 100644 --- a/apps/comments/lib/Activity/Listener.php +++ b/apps/comments/lib/Activity/Listener.php @@ -108,18 +108,18 @@ class Listener { } $activity = $this->activityManager->generateEvent(); - $activity->setApp(Extension::APP_NAME) - ->setType(Extension::APP_NAME) + $activity->setApp('comments') + ->setType('comments') ->setAuthor($actor) - ->setObject($event->getComment()->getObjectType(), $event->getComment()->getObjectId()) - ->setMessage(Extension::ADD_COMMENT_MESSAGE, [ + ->setObject($event->getComment()->getObjectType(), (int) $event->getComment()->getObjectId()) + ->setMessage('add_comment_message', [ $event->getComment()->getId(), ]); foreach ($users as $user => $path) { $activity->setAffectedUser($user); - $activity->setSubject(Extension::ADD_COMMENT_SUBJECT, [ + $activity->setSubject('add_comment_subject', [ $actor, $path, ]); diff --git a/apps/comments/lib/Activity/Provider.php b/apps/comments/lib/Activity/Provider.php new file mode 100644 index 00000000000..4999d418785 --- /dev/null +++ b/apps/comments/lib/Activity/Provider.php @@ -0,0 +1,198 @@ +<?php +/** + * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\Comments\Activity; + +use OCP\Activity\IEvent; +use OCP\Activity\IManager; +use OCP\Activity\IProvider; +use OCP\Comments\ICommentsManager; +use OCP\Comments\NotFoundException; +use OCP\IL10N; +use OCP\IURLGenerator; + +class Provider implements IProvider { + + /** @var IL10N */ + protected $l; + + /** @var IURLGenerator */ + protected $url; + + /** @var ICommentsManager */ + protected $commentsManager; + + /** @var IManager */ + protected $activityManager; + + /** + * @param IL10N $l + * @param IURLGenerator $url + * @param ICommentsManager $commentsManager + * @param IManager $activityManager + */ + public function __construct(IL10N $l, IURLGenerator $url, ICommentsManager $commentsManager, IManager $activityManager) { + $this->l = $l; + $this->url = $url; + $this->commentsManager = $commentsManager; + $this->activityManager = $activityManager; + } + + /** + * @param IEvent $event + * @param IEvent|null $previousEvent + * @return IEvent + * @throws \InvalidArgumentException + * @since 11.0.0 + */ + public function parse(IEvent $event, IEvent $previousEvent = null) { + if ($event->getApp() !== 'comments') { + throw new \InvalidArgumentException(); + } + + if ($event->getSubject() === 'add_comment_subject') { + $this->parseMessage($event); + $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/comment.svg'))); + + if ($this->activityManager->isFormattingFilteredObject()) { + try { + return $this->parseShortVersion($event); + } catch (\InvalidArgumentException $e) { + // Ignore and simply use the long version... + } + } + + return $this->parseLongVersion($event); + } else { + throw new \InvalidArgumentException(); + } + } + + /** + * @param IEvent $event + * @return IEvent + * @throws \InvalidArgumentException + * @since 11.0.0 + */ + protected function parseShortVersion(IEvent $event) { + $subjectParameters = $event->getSubjectParameters(); + + if ($event->getSubject() === 'add_comment_subject') { + if ($subjectParameters[0] === $this->activityManager->getCurrentUserId()) { + $event->setParsedSubject($this->l->t('You commented')) + ->setRichSubject($this->l->t('You commented'), []); + } else { + $author = $this->generateUserParameter($subjectParameters[0]); + $event->setParsedSubject($this->l->t('%1$s commented', [$author['name']])) + ->setRichSubject($this->l->t('{author} commented'), [ + 'author' => $author, + ]); + } + } else { + throw new \InvalidArgumentException(); + } + + return $event; + } + + /** + * @param IEvent $event + * @return IEvent + * @throws \InvalidArgumentException + * @since 11.0.0 + */ + protected function parseLongVersion(IEvent $event) { + $subjectParameters = $event->getSubjectParameters(); + + if ($event->getSubject() === 'add_comment_subject') { + if ($subjectParameters[0] === $this->activityManager->getCurrentUserId()) { + $event->setParsedSubject($this->l->t('You commented on %1$s', [ + trim($subjectParameters[1], '/'), + ])) + ->setRichSubject($this->l->t('You commented on {file}'), [ + 'file' => $this->generateFileParameter($event->getObjectId(), $subjectParameters[1]), + ]); + } else { + $author = $this->generateUserParameter($subjectParameters[0]); + $event->setParsedSubject($this->l->t('%1$s commented on %2$s', [ + $author['name'], + trim($subjectParameters[1], '/'), + ])) + ->setRichSubject($this->l->t('{author} commented on {file}'), [ + 'author' => $author, + 'file' => $this->generateFileParameter($event->getObjectId(), $subjectParameters[1]), + ]); + } + } else { + throw new \InvalidArgumentException(); + } + + return $event; + } + + protected function parseMessage(IEvent $event) { + $messageParameters = $event->getMessageParameters(); + try { + $comment = $this->commentsManager->get((int) $messageParameters[0]); + $message = $comment->getMessage(); + $message = str_replace("\n", '<br />', str_replace(['<', '>'], ['<', '>'], $message)); + + $mentionCount = 1; + $mentions = []; + foreach ($comment->getMentions() as $mention) { + if ($mention['type'] !== 'user') { + continue; + } + + $message = preg_replace( + '/(^|\s)(' . '@' . $mention['id'] . ')(\b)/', + //'${1}' . $this->regexSafeUser($mention['id'], $displayName) . '${3}', + '${1}' . '{mention' . $mentionCount . '}' . '${3}', + $message + ); + $mentions['mention' . $mentionCount] = $this->generateUserParameter($mention['id']); + $mentionCount++; + } + + $event->setParsedMessage($comment->getMessage()) + ->setRichMessage($message, $mentions); + } catch (NotFoundException $e) { + } + } + + protected function generateFileParameter($id, $path) { + return [ + 'type' => 'file', + 'id' => $id, + 'name' => basename($path), + 'path' => $path, + 'link' => $this->url->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $id]), + ]; + } + + protected function generateUserParameter($uid) { + return [ + 'type' => 'user', + 'id' => $uid, + 'name' => $uid,// FIXME Use display name + ]; + } +} diff --git a/apps/comments/lib/Activity/Setting.php b/apps/comments/lib/Activity/Setting.php new file mode 100644 index 00000000000..110ec4a31a4 --- /dev/null +++ b/apps/comments/lib/Activity/Setting.php @@ -0,0 +1,98 @@ +<?php +/** + * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\Comments\Activity; + + +use OCP\Activity\ISetting; +use OCP\IL10N; + +class Setting implements ISetting { + + /** @var IL10N */ + protected $l; + + /** + * @param IL10N $l + */ + public function __construct(IL10N $l) { + $this->l = $l; + } + + /** + * @return string Lowercase a-z and underscore only identifier + * @since 11.0.0 + */ + public function getIdentifier() { + return 'comments'; + } + + /** + * @return string A translated string + * @since 11.0.0 + */ + public function getName() { + return $this->l->t('<strong>Comments</strong> for files'); + } + + /** + * @return int whether the filter should be rather on the top or bottom of + * the admin section. The filters are arranged in ascending order of the + * priority values. It is required to return a value between 0 and 100. + * @since 11.0.0 + */ + public function getPriority() { + return 50; + } + + /** + * @return bool True when the option can be changed for the stream + * @since 11.0.0 + */ + public function canChangeStream() { + return true; + } + + /** + * @return bool True when the option can be changed for the stream + * @since 11.0.0 + */ + public function isDefaultEnabledStream() { + return true; + } + + /** + * @return bool True when the option can be changed for the mail + * @since 11.0.0 + */ + public function canChangeMail() { + return true; + } + + /** + * @return bool True when the option can be changed for the stream + * @since 11.0.0 + */ + public function isDefaultEnabledMail() { + return false; + } +} + |