Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
96.67% covered (success)
96.67%
29 / 30
83.33% covered (warning)
83.33%
5 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
SuppressionRowUpdateGenerator
96.67% covered (success)
96.67%
29 / 30
83.33% covered (warning)
83.33%
5 / 6
16
0.00% covered (danger)
0.00%
0 / 1
 update
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 setNewTitleFromNsAndText
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 newTitleFromNsAndText
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 updatePageIdFromTitle
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
3
 updatePageLinkedExtraData
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
5
 extra
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
3.07
1<?php
2
3namespace MediaWiki\Extension\Notifications;
4
5use MediaWiki\Title\Title;
6use RowUpdateGenerator;
7use stdClass;
8
9/**
10 * Performs updates required for respecting suppression within echo:
11 *   Updates event_page_id based on event_page_title and event_page_namespace
12 *   Updates extra data for page-linked events to contain page id's
13 */
14class SuppressionRowUpdateGenerator implements RowUpdateGenerator {
15    /**
16     * @var callable Hack to allow replacing Title::makeTitleSafe in tests
17     */
18    protected $newTitleFromNsAndText = [ 'Title', 'makeTitleSafe' ];
19
20    /**
21     * @inheritDoc
22     */
23    public function update( $row ) {
24        $update = $this->updatePageIdFromTitle( $row );
25        if ( $row->event_extra !== null && $row->event_type === 'page-linked' ) {
26            $update = $this->updatePageLinkedExtraData( $row, $update );
27        }
28
29        return $update;
30    }
31
32    /**
33     * Hackish method of mocking Title::newFromText for tests
34     *
35     * @param callable $callable
36     */
37    public function setNewTitleFromNsAndText( $callable ) {
38        $this->newTitleFromNsAndText = $callable;
39    }
40
41    /**
42     * Hackish method of mocking Title::makeTitleSafe for tests
43     *
44     * @param int $namespace The namespace of the page to look up
45     * @param string $text The page name to look up
46     * @return Title|null The title located for the namespace + text, or null if invalid
47     */
48    protected function newTitleFromNsAndText( $namespace, $text ) {
49        return call_user_func( $this->newTitleFromNsAndText, $namespace, $text );
50    }
51
52    /**
53     * Migrates all echo events from having page title and namespace as rows in the table
54     * to having only a page id in the table.  Any event from a page that doesn't have an
55     * article id gets the title+namespace moved to the event extra data
56     *
57     * @param stdClass $row A row from the database
58     * @return array All updates required for this row
59     */
60    protected function updatePageIdFromTitle( $row ) {
61        $update = [];
62        $title = $this->newTitleFromNsAndText( $row->event_page_namespace, $row->event_page_title );
63        if ( $title !== null ) {
64            $pageId = $title->getArticleID();
65            if ( $pageId ) {
66                // If the title has a proper id from the database, store it
67                $update['event_page_id'] = $pageId;
68            } else {
69                // For titles that do not refer to a WikiPage stored in the database
70                // move the title/namespace into event_extra
71                $extra = $this->extra( $row );
72                $extra['page_title'] = $row->event_page_title;
73                $extra['page_namespace'] = $row->event_page_namespace;
74
75                $update['event_extra'] = serialize( $extra );
76            }
77        }
78
79        return $update;
80    }
81
82    /**
83     * Updates the extra data for page-linked events to point to the id of the article
84     * rather than the namespace+title combo.
85     *
86     * @param stdClass $row A row from the database
87     * @param array $update
88     *
89     * @return array All updates required for this row
90     */
91    protected function updatePageLinkedExtraData( $row, array $update ) {
92        $extra = $this->extra( $row, $update );
93
94        if ( isset( $extra['link-from-title'] ) && isset( $extra['link-from-namespace'] ) ) {
95            $title = $this->newTitleFromNsAndText( $extra['link-from-namespace'], $extra['link-from-title'] );
96            unset( $extra['link-from-title'], $extra['link-from-namespace'] );
97            // Link from page is always from a content page, if null or no article id it was
98            // somehow invalid
99            if ( $title !== null && $title->getArticleID() ) {
100                $extra['link-from-page-id'] = $title->getArticleID();
101            }
102
103            $update['event_extra'] = serialize( $extra );
104        }
105
106        return $update;
107    }
108
109    /**
110     * Return the extra data for a row, if an update wants to change the
111     * extra data returns that updated data rather than the original. If
112     * no extra data exists returns []
113     *
114     * @param stdClass $row The database row being updated
115     * @param array $update Updates that need to be applied to the database row
116     * @return array The event extra data
117     */
118    protected function extra( $row, array $update = [] ) {
119        if ( isset( $update['event_extra'] ) ) {
120            return unserialize( $update['event_extra'] );
121        }
122
123        if ( $row->event_extra ) {
124            return unserialize( $row->event_extra );
125        }
126
127        return [];
128    }
129
130}