Skip to content

Conversation

@blizzz
Copy link
Member

@blizzz blizzz commented Sep 19, 2016

Contributes to #753

The PR implements notification for @-mentioning against User IDs (according to step 1 in #753).

  1. In a shared file, write a comment and type the user id of one of the sharees:

spectacle f23672

  1. A notification will appear…

spectacle q23777

  1. …and look like this

spectacle i23777

Upon click the user is brought to the Files app, in the dir of the file or folder commented on. the file or folder is also highlighted.

When a comment containing a mention is deleted, the notification is also removed, if applicable.

It does not include or implement a subscription system.

Please test and review @nickvergessen (most code you have seen at owncloud/core#24495 already) @schiessle @jancborchardt

@blizzz blizzz added this to the Nextcloud 11.0 milestone Sep 19, 2016
@mention-bot
Copy link

@blizzz, thanks for your PR! By analyzing the annotation information on this pull request, we identified @nickvergessen to be a potential reviewer

@blizzz blizzz mentioned this pull request Sep 19, 2016
7 tasks
@blizzz blizzz changed the title Notificacations for simple @-mentioning in comments Notifications for simple @-mentioning in comments Sep 19, 2016

/**
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing @license

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done (this is for me, Github keeps showing me the dealt-with comments despite code changes, making it hard to track whats left…)

$notificationManager = \OC::$server->getNotificationManager();
$notificationManager->registerNotifier(
function() {
return new \OCA\Comments\Notification\Notifier(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use $application->getContainer()->query('OCA\Comments\Notification\Notifier') for automatic DI?
Or even better $application->getContainer()->query(\OCA\Comments\Notification\Notifier::class)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

$notificationListener = function(\OCP\Comments\CommentsEvent $event) {
$application = new \OCP\AppFramework\App('comments');
/** @var \OCA\Comments\Notification\Listener $listener */
$listener = $application->getContainer()->query('OCA\Comments\Notification\Listener');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use \OCA\Comments\Notification\Listener::class instead of magic string?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for better maintainability.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


$application = new Application();
$application->registerRoutes($this, ['routes' => [
['name' => 'Notifications#view', 'url' => '/notifications/view/{id}', 'verb' => 'GET'],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please include the type in the url and dispatch an event on the endpoint.
Comments are already used by other apps, which would like to redirect to other parts then

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ID is a unique comment ID, not coupled to Actor or Object type. And the URL is still within apps/comments/. Still an issue?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm should be fine then I guess

parent::__construct('comments', $urlParams);
$container = $this->getContainer();

$container->registerService('NotificationsController', function(IContainer $c) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use magic instead:
$container->registerAlias('NotificationsController', \OCA\Comments\Controller\Notifications::class);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

try {
$comment = $this->commentsManager->get($id);
if($comment->getObjectType() !== 'files') {
return new NotFoundResponse();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as mentioned above, dispatch an event, so announcements can redirect to the given announcement

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now this should not be a problem anymore, because the code path is a result from a registered EventHandler. So, if a different app wants to work with Notifications, it will react upon any event, create the notification pointing to a controller of its own. Thus, to be considered done.

$notification = $this->notificationManager->createNotification();
$notification->setApp('comments')
->setObject('comment', $comment->getId())
->setSubject('mention', [$comment->getObjectType(), $comment->getObjectId()] )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would remove this, object+user should be fine already, but I guess we will never change that data

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is possible to change the data in theory. Currently it does not happen in files and it would not be reasonable either (unless you introduce moving comments from one file to the other…). The notification data itself does not change. If setting the subject with 'mention' alone works, it would be the safer approach indeed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

$comment = $event->getComment();

if($comment->getObjectType() !== 'files') {
// we only support notifications for files at this point
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:(

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now, this is also dependent on the registered EventHandler, custom types/apps would need to register their own. → Done.

$notification
->setApp('comments')
->setObject('comment', $comment->getId())
->setSubject('mention', [ $comment->getObjectType(), $comment->getObjectId() ])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should listen to delete files hook, and then delete the notifications? But could also be a step 2

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, that already works 😎

When files are deleted, the related comments are deleted, too. And this triggers deletion of notifications. Tested :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw, even works for Unshared files.

$displayName = $commenter->getDisplayName();
}
} else if($comment->getActorType() === ICommentsManager::DELETED_USER) {
$displayName = $l->t('a now deleted user');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will only work in english. I'd to a switch with the full message.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, missing context for translators :(

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@blizzz
Copy link
Member Author

blizzz commented Sep 20, 2016

As discussed with @nickvergessen:

To be user to be integrated with other apps, we extend the API to register a CommentEntity. This will be notified on events. Therefore, we can remove the usage of the EventDispatcher and specific/coupled implementations. (So far we have one client using Comments infrastructure except of Files which is the Announcement Center – this is were improvement suggestions come from).

Updates inline

@blizzz blizzz added 2. developing Work in progress and removed 3. to review Waiting for reviews labels Sep 20, 2016
@blizzz blizzz force-pushed the comments-user-mention branch 2 times, most recently from e0b5bcc to a8f41c8 Compare September 26, 2016 22:54
@blizzz blizzz added 3. to review Waiting for reviews and removed 2. developing Work in progress labels Sep 27, 2016
@blizzz blizzz force-pushed the comments-user-mention branch from 41076b8 to 1317bbe Compare September 27, 2016 12:31
@blizzz
Copy link
Member Author

blizzz commented Sep 27, 2016

@nickvergessen reevaluate your review, please. 🎆


$dir = $this->folder->getRelativePath($files[0]->getParent()->getPath());
$url = $this->urlGenerator->linkToRouteAbsolute(
'files.view.index',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd use

            $link = $this->urlGenerator->linkToRouteAbsolute(
                'files.viewcontroller.showFile',
                ['fileId' => $comment->getObjectId()]
            );

this automatically highlights the row in the file list and will also in the future open the sidebar.
Maybe we can also add a #comments-tab or something, so it can automatically open the comments tab in this case.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have it locally, push later today.

in the future open the sidebar.
Maybe we can also add a #comments-tab or something, so it can automatically open the comments tab in this case.

Are there already issues or PRs on this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues/PRs as far as I know

@nickvergessen
Copy link
Member

Other then that works well, will also try to get this working for the announcements later today.

Todos for follow up PRs

  • Allow autocomplete of names when user enumeration is allowed
  • Open sidebar comments tab (maybe even highlight the comment that has the mention)
  • Highlight the actual mention (maybe with avatar?) in the comment text

if($event->getEvent() === CommentsEvent::EVENT_DELETE) {
$this->notificationManager->markProcessed($notification);
} else {
$this->notificationManager->notify($notification);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bit odd, but if I see this correctly, when you modify a comment so someone is not mentioned anymore, the notification is not removed, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's right. (As comparison, Github does not behave differently, which does not mean it is correct however).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so the issue is that we have garbage in the DB.


$notification->setUser($user);
if($event->getEvent() === CommentsEvent::EVENT_DELETE) {
$this->notificationManager->markProcessed($notification);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also this is not reached, when deleting a comment, because your EventHandler does only handle add?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting point. I believe to remember that it was needed. However, deleting a comment indeed does not end up here. And that's probably a bug. While the notification is not shown to the user, it still is present in the database.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the deletion handler. We could also hook in an activity thing and log about deleted activities if it makes sense. i tried this a bit, but could not get my head around formatting 🙊

@MorrisJobke
Copy link
Member

@blizzz could you check out the comments? Thanks :)

@MorrisJobke MorrisJobke added 2. developing Work in progress and removed 3. to review Waiting for reviews labels Oct 5, 2016
@jancborchardt
Copy link
Member

jancborchardt commented Oct 6, 2016

Regarding the UI of the notification, it needs to look a bit nicer like this:

😎 Joas mentioned you in a comment on 🖼 Berlin Weißensee at night.jpg

Smiley is a placeholder for the avatar, and image for the filetype. Remember that usernames need to always have avatars next to them. And ideally filenames should always have a connection to their filetype icons or previews!
If the bolding or the avatar is not possible with the notification app or API yet, we should take steps to make it so.

As the first step, the wording should be fixed to the above. :) (That way there is important info directly at the beginning of the message: Someone mentioned you…)

@blizzz
Copy link
Member Author

blizzz commented Oct 6, 2016

@jancborchardt thanks for having a look!

Regarding the UI of the notification, it needs to look a bit nicer like this:

😎 Joas mentioned you in a comment on 🖼️ Berlin Weißensee at night.jpg

Smiley is a placeholder for the avatar, and image for the filetype. Remember that usernames need to always have avatars next to them. And ideally filenames should always have a connection to their filetype icons or previews!
If the bolding or the avatar is not possible with the notification app or API yet, we should take steps to make it so.

IIRC notifications accept plain text only at the moment. Thus, it's out of scope here. It makes sense however, I agree!

@jancborchardt might be an addition to #753

As the first step, the wording should be fixed to the above. :) (That way there is important info directly at the beginning of the message: Someone mentioned you…)

Good point.

  • Imprve notification wording to the above

blizzz and others added 2 commits October 7, 2016 17:11
(WIP) notify user when mentioned in comments

Fix doc, and create absolute URL for as notification link.

PSR-4 compatibility changes

also move notification creation to comments app

Do not notify yourself

unit test for controller and application

smaller fixes

- translatable app name
- remove doubles in mention array
- micro perf optimization
- display name: special label for deleted users, keep user id for users that could not be fetched from userManager

Comment Notification-Listener Unit Test

fix email adresses

remove notification when triggering comment was deleted

add and adjust tests

add missing @license tags

simplify NotificationsController registration

appinfo simplification, php docs

make string easier to translate

adjust test

replace dispatcher-based listeners with a registration method and interface

safer to not pass optional data parameter to setSubject for marking as processed. ID and mention suffices

Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>

update comment

Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
@blizzz
Copy link
Member Author

blizzz commented Oct 7, 2016

Allow autocomplete of names when user enumeration is allowed

See 3. from #753

Open sidebar comments tab (maybe even highlight the comment that has the mention)
Highlight the actual mention (maybe with avatar?) in the comment text

I'll mention it there as well.

blizzz added 2 commits October 8, 2016 01:10
Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
@blizzz blizzz force-pushed the comments-user-mention branch from 4fc4f02 to a9671a4 Compare October 7, 2016 23:19
@blizzz
Copy link
Member Author

blizzz commented Oct 7, 2016

@jancborchardt here you go spectacle bh5697

(the user id in brackets is custom ldap setting, by default you see the normal display name, so don't be afraid)

@blizzz blizzz added 3. to review Waiting for reviews and removed 2. developing Work in progress labels Oct 7, 2016
* notifications can be cleaned up, no polluted DB
* updating comments will re-notify users or remove notifications, depending on the message

Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
@blizzz
Copy link
Member Author

blizzz commented Oct 12, 2016

Added a pre-update event so, so that there will be no orphaned notifications in the DB. Also users are getting either re-notified, notified, and un-notifies, depending on whether they are still mentioned, newly mentioned or removed.

@nickvergessen how's your satisfaction grade now? :)

@codecov-io
Copy link

codecov-io commented Oct 12, 2016

Current coverage is 57.43% (diff: 95.85%)

Merging #1449 into master will increase coverage by 1.19%

@@             master      #1449   diff @@
==========================================
  Files          1070       1081     +11   
  Lines         60505      62974   +2469   
  Methods        6829       7186    +357   
  Messages          0          0           
  Branches          0          0           
==========================================
+ Hits          34026      36168   +2142   
- Misses        26479      26806    +327   
  Partials          0          0           

Sunburst

Diff Coverage File Path
0% new apps/comments/appinfo/routes.php
•• 23% apps/comments/appinfo/app.php
•••••••• 80% new apps/comments/lib/Notification/Notifier.php
•••••••• 89% new apps/comments/tests/Unit/EventHandlerTest.php
••••••••• 93% new apps/comments/lib/Controller/Notifications.php
••••••••• 95% lib/private/Comments/Manager.php
••••••••• 97% new ...ps/comments/tests/Unit/Notification/ListenerTest.php
•••••••••• 100% lib/public/Comments/CommentsEvent.php
•••••••••• 100% ...ate/AppFramework/DependencyInjection/DIContainer.php
•••••••••• 100% new apps/comments/lib/AppInfo/Application.php

Review all 16 files changed

Powered by Codecov. Last update 9e7824f...70c7781

* @param string $message
* @return string[] containing the mentions, e.g. ['@alice', '@bob']
*/
public function extractMentions($message) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I am currently looking into 2. and 4. from #753 and extractMentions() can be well used in other code parts. Listeners is the wrong place to be, thus.

It could be a direct method of a IComment and I think it makes most sense. However this extends the API with a method. Except we add an additional interface, IMentionable within the Comments namespace. It would be an optional ability and we remain backwards compatibility. Downside: for now it would be just one method (more might come with further implementing #753, but it is unlikely). A future change would cause the same dilemma. Now the time is not bad since I have not heard about a single 3rd party Comments implementation.

Other places would be the Comments Manager (similar issue, and makes less sense to me) or we introduce a public Utility class (might get too close to our implementation, limiting flexibility). Or it would be an interface as well and we gain too much additional overhead…

=> I work on it on the subsequent PR, does not need to be dealt with here. For now I'll extend IComment, but I am not fixed on this, can be discussed and easily moved around.


$eventType = $event->getEvent();
if( $eventType === CommentsEvent::EVENT_ADD
&& $event instanceof CommentsEvent
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not necessary on all the ifs because you typehint it in the methods signature ;)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Legay from when i had the EntityEvent in mind… thanks for noticing 🙇‍♂️

* @param CommentsEvent $event
*/
private function activityHandler(CommentsEvent $event) {
$c = $this->app->getContainer();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is hacky, why not inject the actual objects?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, why? 🤔 🔕 🖊️

public function evaluate(CommentsEvent $event) {
$comment = $event->getComment();

if($comment->getObjectType() !== 'files') {
Copy link
Member

@nickvergessen nickvergessen Oct 14, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has already been taken care of

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[ $displayName, $fileName ]
);
}
$notification->setParsedSubject($subject);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could do:

$notification->setParsedMessage($comment->getMessage());

Then the notification also contains the text of the comment. I think that makes actually sense?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might reveal sensitive information, when popping up on a big screen? It might be also too much text for a notification, so should be trimmed down.

Facebook shows the first few words at the end on their notification, Diaspora a bit similar, but then this is how they tell apart different posts, there is no name or title. Imho the filename is sufficient and we should not put more information here. At least in the first iteration. If it proves to suck we still can adjust, my feeling is less is more however.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trimming is something the notification API listeners take care of ;)

Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
@blizzz
Copy link
Member Author

blizzz commented Oct 14, 2016

@nickvergessen should be fine now.

@nickvergessen
Copy link
Member

👍

@blizzz
Copy link
Member Author

blizzz commented Oct 14, 2016

Another reviewer? @jancborchardt @rullzer @LukasReschke perhaps?

@MorrisJobke
Copy link
Member

Tested and works 👍

@MorrisJobke MorrisJobke merged commit 96f8f20 into master Oct 17, 2016
@jancborchardt jancborchardt deleted the comments-user-mention branch October 17, 2016 16:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants