Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
DiscussionToolsEventTrait
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 3
156
0.00% covered (danger)
0.00%
0 / 1
 userCan
n/a
0 / 0
n/a
0 / 0
0
 isBundled
n/a
0 / 0
n/a
0 / 0
0
 getBundledEvents
n/a
0 / 0
n/a
0 / 0
0
 getBundledIds
n/a
0 / 0
n/a
0 / 0
0
 getCommentLink
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
30
 getContentSnippet
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 addMarkAsRead
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2/**
3 * Common code for all Echo event presentation models for DiscussionTools events.
4 *
5 * @file
6 * @ingroup Extensions
7 * @license MIT
8 */
9
10namespace MediaWiki\Extension\DiscussionTools\Notifications;
11
12use EchoDiscussionParser;
13use MediaWiki\Revision\RevisionRecord;
14use WikiMap;
15
16/**
17 * This trait must be only used on EchoEventPresentationModel subclasses.
18 *
19 * @property \EchoEvent $event
20 * @property \Language $language
21 * @property \EchoPresentationModelSection $section
22 */
23trait DiscussionToolsEventTrait {
24
25    /**
26     * @param int $type RevisionRecord::DELETED_* constant
27     * @return bool
28     */
29    abstract protected function userCan( $type );
30
31    /**
32     * @return bool
33     */
34    abstract protected function isBundled();
35
36    /**
37     * @return \EchoEvent[]
38     */
39    abstract protected function getBundledEvents();
40
41    /**
42     * @return int[]|false
43     */
44    abstract protected function getBundledIds();
45
46    /**
47     * Get a link to the individual comment, if available.
48     *
49     * @return string|null Full URL linking to the comment, null if not available
50     */
51    protected function getCommentLink(): ?string {
52        if ( !$this->userCan( RevisionRecord::DELETED_TEXT ) ) {
53            return null;
54        }
55        if ( !$this->isBundled() ) {
56            // For a single-comment notification, make a pretty(ish) direct link to the comment.
57            // The browser scrolls and we highlight it client-side.
58            $commentId = $this->event->getExtraParam( 'comment-id' );
59            if ( !$commentId ) {
60                return null;
61            }
62            $title = $this->event->getTitle();
63            return $title->createFragmentTarget( $commentId )->getFullURL();
64        } else {
65            // For a multi-comment notification, we can't make a direct link, because we don't know
66            // which comment appears first on the page; the best we can do is a link to the section.
67            // We handle both scrolling and highlighting client-side, using the ugly parameter
68            // listing all comments.
69
70            // Bundling works differently for different notification types:
71            // * Subscribed topic notifications are bundled per-section.
72            // * User talk page notifications are bundled per-page (so basically, always bundled).
73            // * Mention notifications are *never* bundled.
74
75            // Just pass the oldest comment in the bundle. The client has access to the comment
76            // tree and so can work out all the other comments since this one.
77
78            // This does not include the newest comment, $this->event, but we are looking
79            // for the oldest comment.
80            $bundledEvents = $this->getBundledEvents();
81            $oldestEvent = end( $bundledEvents );
82            $params = [ 'dtnewcommentssince' => $oldestEvent->getExtraParam( 'comment-id' ) ];
83            if ( $this->event->getExtraParam( 'subscribed-comment-name' ) ) {
84                // Topic notifications: Tell client to restrict highlights to this thread
85                $params[ 'dtinthread' ] = 1;
86            }
87            // This may or may not have a fragment identifier, depending on whether it was recorded for
88            // the first one of the bundled events. It's usually not needed because we handle scrolling
89            // client-side, but we can keep it for no-JS users, and to reduce the jump when scrolling.
90            $titleWithOptionalSection = $this->section->getTitleWithSection();
91            return $titleWithOptionalSection->getFullURL( $params );
92        }
93    }
94
95    /**
96     * Get a snippet of the individual comment, if available.
97     *
98     * @return string The snippet, as plain text (may be empty)
99     */
100    protected function getContentSnippet(): string {
101        if ( !$this->userCan( RevisionRecord::DELETED_TEXT ) ) {
102            return '';
103        }
104        // Note that we store plain text in the 'content' param.
105        // Echo also has a 'content' param (for mention notifications), but it contains wikitext.
106        $content = $this->event->getExtraParam( 'content' );
107        if ( !$content ) {
108            return '';
109        }
110        return $this->language->truncateForVisual( $content, EchoDiscussionParser::DEFAULT_SNIPPET_LENGTH );
111    }
112
113    /**
114     * Add mark-as-read params to a link array
115     *
116     * Taken from EchoEventPresentationModel::getPrimaryLinkWithMarkAsRead
117     * TODO: Upstream to Echo?
118     *
119     * @param array $link Link
120     * @return array
121     */
122    protected function addMarkAsRead( $link ) {
123        global $wgEchoCrossWikiNotifications;
124        if ( $link ) {
125            $eventIds = [ $this->event->getId() ];
126            if ( $this->getBundledIds() ) {
127                $eventIds = array_merge( $eventIds, $this->getBundledIds() );
128            }
129
130            $queryParams = [ 'markasread' => implode( '|', $eventIds ) ];
131            if ( $wgEchoCrossWikiNotifications ) {
132                $queryParams['markasreadwiki'] = WikiMap::getCurrentWikiId();
133            }
134
135            $link['url'] = wfAppendQuery( $link['url'], $queryParams );
136        }
137        return $link;
138    }
139
140}