Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 102 |
|
0.00% |
0 / 7 |
CRAP | |
0.00% |
0 / 1 |
ChangesListQuery | |
0.00% |
0 / 102 |
|
0.00% |
0 / 7 |
2652 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
setExtendWatchlist | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
loadMetadataBatch | |
0.00% |
0 / 32 |
|
0.00% |
0 / 1 |
210 | |||
excludeFromChangesList | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
getResult | |
0.00% |
0 / 28 |
|
0.00% |
0 / 1 |
72 | |||
isRecordHidden | |
0.00% |
0 / 35 |
|
0.00% |
0 / 1 |
600 | |||
changeSeparator | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace Flow\Formatter; |
4 | |
5 | use Flow\Data\Listener\RecentChangesListener; |
6 | use Flow\Data\ManagerGroup; |
7 | use Flow\Exception\FlowException; |
8 | use Flow\FlowActions; |
9 | use Flow\Model\UUID; |
10 | use Flow\Repository\TreeRepository; |
11 | use MediaWiki\Logger\LoggerFactory; |
12 | use RecentChange; |
13 | |
14 | class ChangesListQuery extends AbstractQuery { |
15 | |
16 | /** |
17 | * Check if the most recent action for an entity has been displayed already |
18 | * |
19 | * @var array |
20 | */ |
21 | protected $displayStatus = []; |
22 | |
23 | /** |
24 | * @var FlowActions |
25 | */ |
26 | protected $actions; |
27 | |
28 | /** |
29 | * @var bool |
30 | */ |
31 | protected $extendWatchlist = false; |
32 | |
33 | public function __construct( ManagerGroup $storage, TreeRepository $treeRepo, FlowActions $actions ) { |
34 | parent::__construct( $storage, $treeRepo ); |
35 | $this->actions = $actions; |
36 | } |
37 | |
38 | /** |
39 | * @param bool $extend |
40 | */ |
41 | public function setExtendWatchlist( $extend ) { |
42 | $this->extendWatchlist = (bool)$extend; |
43 | } |
44 | |
45 | /** |
46 | * @param \stdClass[] $rows List of recentchange database rows |
47 | * @param bool $isWatchlist |
48 | * @suppress PhanParamSignatureMismatch The signature doesn't match, though |
49 | */ |
50 | public function loadMetadataBatch( $rows, $isWatchlist = false ) { |
51 | $needed = []; |
52 | foreach ( $rows as $row ) { |
53 | if ( !isset( $row->rc_source ) || $row->rc_source !== RecentChangesListener::SRC_FLOW ) { |
54 | continue; |
55 | } |
56 | if ( !isset( $row->rc_params ) ) { |
57 | wfDebugLog( 'Flow', __METHOD__ . ': Bad row without rc_params passed in $rows' ); |
58 | continue; |
59 | } |
60 | $params = unserialize( $row->rc_params ); |
61 | if ( !$params ) { |
62 | wfDebugLog( 'Flow', __METHOD__ . ": rc_params does not contain serialized content: {$row->rc_params}" ); |
63 | continue; |
64 | } |
65 | $changeData = $params['flow-workflow-change']; |
66 | /** |
67 | * Check to make sure revision_type exists, this is to make sure corrupted |
68 | * flow recent change data doesn't throw error on the page. |
69 | * See bug 59106 for more detail |
70 | */ |
71 | if ( !isset( $changeData['revision_type'] ) ) { |
72 | continue; |
73 | } |
74 | if ( $this->excludeFromChangesList( $isWatchlist, $changeData['action'] ) ) { |
75 | continue; |
76 | } |
77 | if ( $isWatchlist && $this->isRecordHidden( $changeData ) ) { |
78 | continue; |
79 | } |
80 | $revisionType = $changeData['revision_type']; |
81 | $needed[$revisionType][] = UUID::create( $changeData['revision'] ); |
82 | } |
83 | |
84 | $found = []; |
85 | foreach ( $needed as $type => $uids ) { |
86 | $found[] = $this->storage->getMulti( $type, $uids ); |
87 | } |
88 | |
89 | $found = array_filter( $found ); |
90 | $count = count( $found ); |
91 | if ( $count === 0 ) { |
92 | $results = []; |
93 | } elseif ( $count === 1 ) { |
94 | $results = reset( $found ); |
95 | } else { |
96 | $results = array_merge( ...array_values( $found ) ); |
97 | } |
98 | |
99 | if ( $results ) { |
100 | parent::loadMetadataBatch( $results ); |
101 | } |
102 | } |
103 | |
104 | /** |
105 | * @param bool $isWatchlist Whether this is Special:Watchlist |
106 | * @param string $action The Flow action this line represents |
107 | * @return bool |
108 | */ |
109 | private function excludeFromChangesList( $isWatchlist, $action ) { |
110 | // If we want to exclude things from watchlist, we can add exclude_from_watchlist |
111 | if ( $isWatchlist ) { |
112 | return false; |
113 | } else { |
114 | return (bool)$this->actions->getValue( $action, 'exclude_from_recentchanges' ); |
115 | } |
116 | } |
117 | |
118 | /** |
119 | * @param null $cl No longer used |
120 | * @param RecentChange $rc |
121 | * @param bool $isWatchlist |
122 | * @return RecentChangesRow|bool False on failure |
123 | * @throws FlowException |
124 | */ |
125 | public function getResult( $cl, RecentChange $rc, $isWatchlist = false ) { |
126 | $rcParams = $rc->getAttribute( 'rc_params' ); |
127 | $params = unserialize( $rcParams ); |
128 | if ( !$params ) { |
129 | throw new FlowException( 'rc_params does not contain serialized content: ' . $rcParams ); |
130 | } |
131 | $changeData = $params['flow-workflow-change']; |
132 | |
133 | if ( !is_array( $changeData ) ) { |
134 | throw new FlowException( 'Flow data missing in recent changes.' ); |
135 | } |
136 | |
137 | /** |
138 | * Check to make sure revision_type exists, this is to make sure corrupted |
139 | * flow recent change data doesn't throw error on the page. |
140 | * See bug 59106 for more detail |
141 | */ |
142 | if ( !isset( $changeData['revision_type'] ) ) { |
143 | throw new FlowException( 'Corrupted rc without changeData: ' . $rc->getAttribute( 'rc_id' ) ); |
144 | } |
145 | |
146 | if ( $this->excludeFromChangesList( $isWatchlist, $changeData['action'] ) ) { |
147 | return false; |
148 | } |
149 | |
150 | // Only show most recent items for watchlist |
151 | if ( $isWatchlist && $this->isRecordHidden( $changeData ) ) { |
152 | return false; |
153 | } |
154 | |
155 | $alpha = UUID::create( $changeData['revision'] )->getAlphadecimal(); |
156 | if ( !isset( $this->revisionCache[$alpha] ) ) { |
157 | LoggerFactory::getInstance( 'Flow' )->error( |
158 | 'Revision not found in revisionCache: {alpha}', |
159 | [ |
160 | 'alpha' => $alpha, |
161 | 'rcParams' => $rcParams, |
162 | ] |
163 | ); |
164 | return false; |
165 | } |
166 | $revision = $this->revisionCache[$alpha]; |
167 | |
168 | $res = new RecentChangesRow; |
169 | $this->buildResult( $revision, 'timestamp', $res ); |
170 | $res->recentChange = $rc; |
171 | |
172 | return $res; |
173 | } |
174 | |
175 | /** |
176 | * Determines if a flow record should be displayed in Special:Watchlist |
177 | * |
178 | * @param array $changeData |
179 | * @return bool |
180 | */ |
181 | protected function isRecordHidden( array $changeData ) { |
182 | if ( $this->extendWatchlist ) { |
183 | return false; |
184 | } |
185 | // Check for legacy action names and convert it |
186 | $alias = $this->actions->getValue( $changeData['action'] ); |
187 | if ( is_string( $alias ) ) { |
188 | $action = $alias; |
189 | } else { |
190 | $action = $changeData['action']; |
191 | } |
192 | // * Display the most recent new post, edit post, edit title for a topic |
193 | // * Display the most recent header edit |
194 | // * Display all new topic and moderation actions |
195 | switch ( $action ) { |
196 | case 'create-header': |
197 | case 'edit-header': |
198 | if ( |
199 | isset( $this->displayStatus['header-' . $changeData['workflow']] ) && |
200 | $this->displayStatus['header-' . $changeData['workflow']] !== $changeData['revision'] |
201 | ) { |
202 | return true; |
203 | } |
204 | $this->displayStatus['header-' . $changeData['workflow']] = $changeData['revision']; |
205 | break; |
206 | |
207 | case 'hide-post': |
208 | case 'hide-topic': |
209 | case 'delete-post': |
210 | case 'delete-topic': |
211 | case 'suppress-post': |
212 | case 'suppress-topic': |
213 | case 'restore-post': |
214 | case 'restore-topic': |
215 | case 'lock-topic': |
216 | // moderation actions are always shown when visible to the user |
217 | return false; |
218 | |
219 | case 'new-topic': |
220 | case 'reply': |
221 | case 'edit-post': |
222 | case 'edit-title': |
223 | case 'create-topic-summary': |
224 | case 'edit-topic-summary': |
225 | if ( |
226 | isset( $this->displayStatus['topic-' . $changeData['workflow']] ) && |
227 | $this->displayStatus['topic-' . $changeData['workflow']] !== $changeData['revision'] |
228 | ) { |
229 | return true; |
230 | } |
231 | $this->displayStatus['topic-' . $changeData['workflow']] = $changeData['revision']; |
232 | break; |
233 | } |
234 | |
235 | return false; |
236 | } |
237 | |
238 | protected function changeSeparator() { |
239 | return ' <span class="mw-changeslist-separator">. .</span> '; |
240 | } |
241 | } |