Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
0.00% |
0 / 87 |
|
0.00% |
0 / 12 |
CRAP | |
0.00% |
0 / 1 |
| AbstractFormatter | |
0.00% |
0 / 87 |
|
0.00% |
0 / 12 |
1260 | |
0.00% |
0 / 1 |
| __construct | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
| getHistoryType | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
| formatTimestamp | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
30 | |||
| formatAnchorsAsPipeList | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
90 | |||
| getDiffAnchor | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
| getDiffPrevAnchor | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
| getDiffCurAnchor | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
| getHistAnchor | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
6 | |||
| changeSeparator | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| getDescription | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
6 | |||
| getDescriptionParams | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
12 | |||
| formatDescription | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
| getTitleLink | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
30 | |||
| 1 | <?php |
| 2 | |
| 3 | namespace Flow\Formatter; |
| 4 | |
| 5 | use Flow\Container; |
| 6 | use Flow\FlowActions; |
| 7 | use Flow\Model\Anchor; |
| 8 | use Flow\Model\PostRevision; |
| 9 | use Flow\RevisionActionPermissions; |
| 10 | use MediaWiki\Context\IContextSource; |
| 11 | use MediaWiki\Html\Html; |
| 12 | use MediaWiki\MediaWikiServices; |
| 13 | use MediaWiki\Message\Message; |
| 14 | |
| 15 | /** |
| 16 | * This is a "utility" class that might come in useful to generate |
| 17 | * some output per Flow entry, e.g. for RecentChanges, Contributions, ... |
| 18 | * These share a lot of common characteristics (like displaying a date, links to |
| 19 | * the posts, some description of the action, ...) |
| 20 | * |
| 21 | * Just extend from this class to use these common util methods, and make sure |
| 22 | * to pass the correct parameters to these methods. Basically, you'll need to |
| 23 | * create a new method that'll accept the objects for your specific |
| 24 | * implementation (like ChangesList & RecentChange objects for RecentChanges, or |
| 25 | * ContribsPager and a DB row for Contributions). From those rows, you should be |
| 26 | * able to derive the objects needed to pass to these utility functions (mainly |
| 27 | * Workflow, AbstractRevision, Title, User and Language objects) and return the |
| 28 | * output. |
| 29 | * |
| 30 | * For implementation examples, check Flow\RecentChanges\Formatter or |
| 31 | * Flow\Contributions\Formatter. |
| 32 | */ |
| 33 | abstract class AbstractFormatter { |
| 34 | /** |
| 35 | * @var RevisionActionPermissions |
| 36 | */ |
| 37 | protected $permissions; |
| 38 | |
| 39 | /** |
| 40 | * @var RevisionFormatter |
| 41 | */ |
| 42 | protected $serializer; |
| 43 | |
| 44 | public function __construct( |
| 45 | RevisionActionPermissions $permissions, |
| 46 | RevisionFormatter $serializer |
| 47 | ) { |
| 48 | $this->permissions = $permissions; |
| 49 | $this->serializer = $serializer; |
| 50 | } |
| 51 | |
| 52 | abstract protected function getHistoryType(); |
| 53 | |
| 54 | /** |
| 55 | * @see RevisionFormatter::buildLinks |
| 56 | * @see RevisionFormatter::getDateFormats |
| 57 | * |
| 58 | * @param array $data Expects an array with keys 'dateFormats', 'isModeratedNotLocked' |
| 59 | * and 'links'. The former should be an array having the key $key being |
| 60 | * tossed in here; the latter an array of links in the [key => [href, msg]] |
| 61 | * format, where 'key' corresponds with a $linksKeys value. The central is |
| 62 | * a boolean. |
| 63 | * @param string $key Date format to use - any of the keys in the array |
| 64 | * returned by RevisionFormatter::getDateFormats |
| 65 | * @param string[] $linkKeys Link key(s) to use as link for the timestamp; |
| 66 | * the first available key will be used (but accepts an array of multiple |
| 67 | * keys for when different kinds of data are tossed in, which may not all |
| 68 | * have the same kind of links available) |
| 69 | * @return string HTML |
| 70 | * @return-taint escaped |
| 71 | */ |
| 72 | protected function formatTimestamp( |
| 73 | array $data, |
| 74 | $key = 'timeAndDate', |
| 75 | array $linkKeys = [ 'header-revision', 'topic-revision', 'post-revision', 'summary-revision' ] |
| 76 | ) { |
| 77 | // Format timestamp: add link |
| 78 | $formattedTime = $data['dateFormats'][$key]; |
| 79 | |
| 80 | // Find the first available link to attach to the timestamp |
| 81 | $anchor = null; |
| 82 | foreach ( $linkKeys as $linkKey ) { |
| 83 | if ( isset( $data['links'][$linkKey] ) ) { |
| 84 | $anchor = $data['links'][$linkKey]; |
| 85 | break; |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | $class = [ 'mw-changeslist-date' ]; |
| 90 | if ( $data['isModeratedNotLocked'] ) { |
| 91 | $class[] = 'history-deleted'; |
| 92 | } |
| 93 | |
| 94 | if ( $anchor instanceof Anchor ) { |
| 95 | return Html::rawElement( |
| 96 | 'span', |
| 97 | [ 'class' => $class ], |
| 98 | $anchor->toHtml( $formattedTime ) |
| 99 | ); |
| 100 | } else { |
| 101 | return Html::element( 'span', [ 'class' => $class ], $formattedTime ); |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | /** |
| 106 | * Generate HTML for "(foo | bar | baz)" based on the links provided by |
| 107 | * RevisionFormatter. |
| 108 | * |
| 109 | * @param (Anchor|Message)[] $links |
| 110 | * @param IContextSource $ctx |
| 111 | * @param string[]|null $request List of link names to be allowed in result output |
| 112 | * @return string Html valid for user output |
| 113 | */ |
| 114 | protected function formatAnchorsAsPipeList( |
| 115 | array $links, |
| 116 | IContextSource $ctx, |
| 117 | ?array $request = null |
| 118 | ) { |
| 119 | if ( $request === null ) { |
| 120 | $request = array_keys( $links ); |
| 121 | } elseif ( !$request ) { |
| 122 | // empty array was passed |
| 123 | return ''; |
| 124 | } |
| 125 | $have = array_combine( $request, $request ); |
| 126 | |
| 127 | $formatted = []; |
| 128 | foreach ( $links as $key => $link ) { |
| 129 | if ( isset( $have[$key] ) ) { |
| 130 | if ( $link instanceof Anchor ) { |
| 131 | $formatted[] = $link->toHtml(); |
| 132 | } elseif ( $link instanceof Message ) { |
| 133 | $formatted[] = $link->escaped(); |
| 134 | } |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | if ( $formatted ) { |
| 139 | $content = $ctx->getLanguage()->pipeList( $formatted ); |
| 140 | if ( $content ) { |
| 141 | return $ctx->msg( 'parentheses' )->rawParams( $content )->escaped(); |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | return ''; |
| 146 | } |
| 147 | |
| 148 | /** |
| 149 | * Gets the "diff" link; linking to the diff against the previous revision, |
| 150 | * in a format that can be wrapped in an array and passed to |
| 151 | * formatLinksAsPipeList. |
| 152 | * |
| 153 | * @param Anchor[] $input |
| 154 | * @param IContextSource $ctx |
| 155 | * @return Anchor|Message |
| 156 | */ |
| 157 | protected function getDiffAnchor( array $input, IContextSource $ctx ) { |
| 158 | if ( !isset( $input['diff'] ) ) { |
| 159 | // plain text with no link |
| 160 | return $ctx->msg( 'diff' ); |
| 161 | } |
| 162 | |
| 163 | return $input['diff']; |
| 164 | } |
| 165 | |
| 166 | /** |
| 167 | * Gets the "prev" link; linking to the diff against the previous revision, |
| 168 | * in a format that can be wrapped in an array and passed to |
| 169 | * formatLinksAsPipeList. |
| 170 | * |
| 171 | * @param Anchor[] $input |
| 172 | * @param IContextSource $ctx |
| 173 | * @return Anchor|Message |
| 174 | */ |
| 175 | protected function getDiffPrevAnchor( array $input, IContextSource $ctx ) { |
| 176 | if ( !isset( $input['diff-prev'] ) ) { |
| 177 | // plain text with no link |
| 178 | return $ctx->msg( 'last' ); |
| 179 | } |
| 180 | |
| 181 | return $input['diff-prev']; |
| 182 | } |
| 183 | |
| 184 | /** |
| 185 | * Gets the "cur" link; linking to the diff against the current revision, |
| 186 | * in a format that can be wrapped in an array and passed to |
| 187 | * formatLinksAsPipeList. |
| 188 | * |
| 189 | * @param Anchor[] $input |
| 190 | * @param IContextSource $ctx |
| 191 | * @return Anchor|Message |
| 192 | */ |
| 193 | protected function getDiffCurAnchor( array $input, IContextSource $ctx ) { |
| 194 | if ( !isset( $input['diff-cur'] ) ) { |
| 195 | // plain text with no link |
| 196 | return $ctx->msg( 'cur' ); |
| 197 | } |
| 198 | |
| 199 | return $input['diff-cur']; |
| 200 | } |
| 201 | |
| 202 | /** |
| 203 | * Gets the "hist" link; linking to the history of a certain element, in a |
| 204 | * format that can be wrapped in an array and passed to |
| 205 | * formatLinksAsPipeList. |
| 206 | * |
| 207 | * @param Anchor[] $input |
| 208 | * @param IContextSource $ctx |
| 209 | * @return Anchor|Message |
| 210 | */ |
| 211 | protected function getHistAnchor( array $input, IContextSource $ctx ) { |
| 212 | $anchor = $input['post-history'] ?? |
| 213 | $input['topic-history'] ?? |
| 214 | $input['board-history'] ?? null; |
| 215 | |
| 216 | if ( $anchor instanceof Anchor ) { |
| 217 | $anchor->setMessage( $ctx->msg( 'hist' ) ); |
| 218 | return $anchor; |
| 219 | } else { |
| 220 | // plain text with no link |
| 221 | return $ctx->msg( 'hist' ); |
| 222 | } |
| 223 | } |
| 224 | |
| 225 | /** |
| 226 | * @return string Html valid for user output |
| 227 | */ |
| 228 | protected function changeSeparator() { |
| 229 | return ' <span class="mw-changeslist-separator">. .</span> '; |
| 230 | } |
| 231 | |
| 232 | /** |
| 233 | * @param array $data |
| 234 | * @param IContextSource $ctx |
| 235 | * @return Message |
| 236 | */ |
| 237 | protected function getDescription( array $data, IContextSource $ctx ) { |
| 238 | // Build description message, piggybacking on history i18n |
| 239 | $changeType = $data['changeType']; |
| 240 | $actions = $this->permissions->getActions(); |
| 241 | |
| 242 | $key = $actions->getValue( $changeType, 'history', 'i18n-message' ); |
| 243 | // find specialized message for this particular formatter type |
| 244 | // E.g. the -irc messages. |
| 245 | $msg = $ctx->msg( $key . '-' . $this->getHistoryType() ); |
| 246 | if ( !$msg->exists() ) { |
| 247 | // fallback to default msg |
| 248 | $msg = $ctx->msg( $key ); |
| 249 | } |
| 250 | |
| 251 | return $msg->params( ...$this->getDescriptionParams( $data, $actions, $changeType ) ); |
| 252 | } |
| 253 | |
| 254 | /** |
| 255 | * @param array $data |
| 256 | * @param FlowActions $actions |
| 257 | * @param string $changeType |
| 258 | * @return array |
| 259 | */ |
| 260 | protected function getDescriptionParams( array $data, FlowActions $actions, $changeType ) { |
| 261 | $source = $actions->getValue( $changeType, 'history', 'i18n-params' ); |
| 262 | $params = []; |
| 263 | foreach ( $source as $param ) { |
| 264 | if ( isset( $data['properties'][$param] ) ) { |
| 265 | $params[] = $data['properties'][$param]; |
| 266 | } else { |
| 267 | wfDebugLog( 'Flow', __METHOD__ . |
| 268 | ": Missing expected parameter $param for change type $changeType" ); |
| 269 | $params[] = ''; |
| 270 | } |
| 271 | } |
| 272 | |
| 273 | return $params; |
| 274 | } |
| 275 | |
| 276 | /** |
| 277 | * Generate an HTML revision description. |
| 278 | * |
| 279 | * @param array $data |
| 280 | * @param IContextSource $ctx |
| 281 | * @return string Html valid for user output |
| 282 | */ |
| 283 | protected function formatDescription( array $data, IContextSource $ctx ) { |
| 284 | $msg = $this->getDescription( $data, $ctx ); |
| 285 | return '<span class="plainlinks">' . $msg->parse() . '</span>'; |
| 286 | } |
| 287 | |
| 288 | /** |
| 289 | * Returns HTML links to the page title and (if the action is topic-related) |
| 290 | * the topic. |
| 291 | * |
| 292 | * @param array $data |
| 293 | * @param FormatterRow $row |
| 294 | * @param IContextSource $ctx |
| 295 | * @return string HTML linking to topic & board |
| 296 | */ |
| 297 | protected function getTitleLink( array $data, FormatterRow $row, IContextSource $ctx ) { |
| 298 | $ownerLink = MediaWikiServices::getInstance()->getLinkRenderer()->makeLink( |
| 299 | $row->workflow->getOwnerTitle(), |
| 300 | null, |
| 301 | [ 'class' => 'mw-title' ] |
| 302 | ); |
| 303 | |
| 304 | if ( !isset( $data['links']['topic'] ) || !$row->revision instanceof PostRevision ) { |
| 305 | return $ownerLink; |
| 306 | } |
| 307 | /** @var Anchor $topic */ |
| 308 | $topic = $data['links']['topic']; |
| 309 | |
| 310 | // generated link has generic link text, should be actual topic title |
| 311 | // @phan-suppress-next-line PhanUndeclaredMethod $row->revision being PostRevision is not inferred |
| 312 | $root = $row->revision->getRootPost(); |
| 313 | if ( $root && $this->permissions->isAllowed( $root, 'view' ) ) { |
| 314 | $topicDisplayText = Container::get( 'templating' ) |
| 315 | ->getContent( $root, 'topic-title-plaintext' ); |
| 316 | $topic->setMessage( $topicDisplayText ); |
| 317 | } |
| 318 | |
| 319 | return $ctx->msg( 'flow-rc-topic-of-board' )->rawParams( |
| 320 | $topic->toHtml(), |
| 321 | $ownerLink |
| 322 | )->escaped(); |
| 323 | } |
| 324 | } |