r91952 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r91951‎ | r91952 | r91953 >
Date:12:44, 12 July 2011
Author:jeroendedauw
Status:deferred
Tags:
Comment:
applied patch by Van de Bugger
Modified paths:
  • /trunk/extensions/SubPageList/SubPageList.class.php (modified) (history)
  • /trunk/extensions/SubPageList/SubPageList.i18n.php (modified) (history)
  • /trunk/extensions/SubPageList/SubPageList.php (modified) (history)

Diff [purge]

Index: trunk/extensions/SubPageList/SubPageList.i18n.php
@@ -19,7 +19,9 @@
2020 $messages['en'] = array(
2121 'spl-desc' => 'Adds a <code><nowiki><splist /></nowiki></code> tag that enables you to list subpages',
2222
23 - 'spl-nosubpages' => '$1 has no subpages to list.',
 23+ 'spl-nosubpages' => 'Page "$1" has no subpages to list.',
 24+ 'spl-noparentpage' => 'Page "$1" does not exist.',
 25+ 'spl-nopages' => 'Namespace "$1" does not have pages.',
2426
2527 'spl-subpages-par-sort' => 'The direction to sort in.',
2628 'spl-subpages-par-sortby' => 'What to sort the subpages by.',
@@ -313,9 +315,11 @@
314316 */
315317 $messages['ru'] = array(
316318 'spl-desc' => 'Добавляет тег <code><nowiki><splist></splist></nowiki></code>, выводящий список подстраниц',
317 - 'spl-nosubpages' => '$1 не имеет подстраниц.',
 319+ 'spl-nosubpages' => 'Страница «$1» не имеет подстраниц.',
 320+ 'spl-noparentpage' => 'Страница «$1» не существует.',
 321+ 'spl-nopages' => 'Пространство имён «$1» не содержит страниц.',
318322 'spl-subpages-par-sort' => 'Направление сортировки.',
319 - 'spl-subpages-par-sortby' => 'По чему сортировать подстраницы.',
 323+ 'spl-subpages-par-sortby' => 'Ключ сортировки: название (title) или дата последней правки (lastedit).',
320324 'spl-subpages-par-format' => 'Список подстраниц может быть показан в нескольких форматах. Нумерованный список (ol), маркированный список (ul), список через запятые (list).',
321325 'spl-subpages-par-page' => 'Страница для которой показывать список подстраниц. По умолчанию текущая страница.',
322326 'spl-subpages-par-showpage' => 'Указывает, должна ли отображаться сама страница.',
@@ -357,4 +361,3 @@
358362 'spl-subpages-par-page' => 'Сторінка, для якої показати підсторінки. За умовчанням — поточна сторінка.',
359363 'spl-subpages-par-limit' => 'Максимальна кількість сторінок у списку.',
360364 );
361 -
Index: trunk/extensions/SubPageList/SubPageList.php
@@ -35,7 +35,7 @@
3636 die( '<b>Error:</b> You need to have <a href="http://www.mediawiki.org/wiki/Extension:Validator">Validator</a> installed in order to use <a href="http://www.mediawiki.org/wiki/Extension:SubPageList">SubPageList</a>.<br />' );
3737 }
3838
39 -define( 'SPL_VERSION', '0.3' );
 39+define( 'SPL_VERSION', '0.4 alpha' );
4040
4141 $wgExtensionCredits['parserhook'][] = array(
4242 'path' => __FILE__,
Index: trunk/extensions/SubPageList/SubPageList.class.php
@@ -11,6 +11,7 @@
1212 * @licence GNU GPL v3 or later
1313 *
1414 * @author Jeroen De Dauw
 15+ * @author Van de Bugger
1516 * @author James McCormack (email: user "qedoc" at hotmail); preceding version Martin Schallnahs <myself@schaelle.de>, original Rob Church <robchur@gmail.com>
1617 * @copyright © 2008 James McCormack, preceding version Martin Schallnahs, original Rob Church
1718 */
@@ -68,20 +69,20 @@
6970
7071 $params['sortby'] = new Parameter( 'sortby' );
7172 $params['sortby']->addCriteria( new CriterionInArray( 'title', 'lastedit' ) );
72 - $params['sortby']->addManipulations( new ParamManipulationFunctions( 'strtolower' ) );
 73+ $params['sortby']->addManipulations( new ParamManipulationFunctions( 'strtolower' ) );
7374 $params['sortby']->setDefault( 'title' );
7475 $params['sortby']->setDescription( wfMsg( 'spl-subpages-par-sortby' ) );
7576
7677 $params['format'] = new Parameter( 'format' );
77 - $params['format']->addAliases( 'liststyle' );
 78+ $params['format']->addAliases( 'liststyle' );
7879 $params['format']->addCriteria( new CriterionInArray(
7980 'ul', 'unordered',
8081 'ol', 'ordered',
81 - 'list', 'bar'
 82+ 'list', 'bar'
8283 ) );
83 - $params['format']->addManipulations( new ParamManipulationFunctions( 'strtolower' ) );
84 - $params['format']->setDefault( 'ul' );
85 - $params['format']->setDescription( wfMsg( 'spl-subpages-par-format' ) );
 84+ $params['format']->addManipulations( new ParamManipulationFunctions( 'strtolower' ) );
 85+ $params['format']->setDefault( 'ul' );
 86+ $params['format']->setDescription( wfMsg( 'spl-subpages-par-format' ) );
8687
8788 $params['page'] = new Parameter( 'page' );
8889 $params['page']->addAliases( 'parent' );
@@ -90,18 +91,20 @@
9192
9293 $params['showpage'] = new Parameter( 'showpage', Parameter::TYPE_BOOLEAN );
9394 $params['showpage']->addAliases( 'showparent' );
94 - $params['showpage']->setDefault( 'no' );
 95+ $params['showpage']->setDefault( 'no' );
9596 $params['showpage']->setDescription( wfMsg( 'spl-subpages-par-showpage' ) );
9697
9798 $params['pathstyle'] = new Parameter( 'pathstyle' );
9899 $params['pathstyle']->addAliases( 'showpath' );
99100 $params['pathstyle']->addCriteria( new CriterionInArray(
100101 'none', 'no',
101 - 'children', 'notparent',
102 - 'full'
 102+ 'subpagename', 'children', 'notparent',
 103+ 'pagename',
 104+ 'full', // Deprecate? --vdb
 105+ 'fullpagename'
103106 ) );
104107 $params['pathstyle']->setDefault( 'none' );
105 - $params['pathstyle']->addManipulations( new ParamManipulationFunctions( 'strtolower' ) );
 108+ $params['pathstyle']->addManipulations( new ParamManipulationFunctions( 'strtolower' ) );
106109 $params['pathstyle']->setDescription( wfMsg( 'spl-subpages-par-pathstyle' ) );
107110
108111 $params['kidsonly'] = new Parameter( 'kidsonly', Parameter::TYPE_BOOLEAN );
@@ -110,8 +113,21 @@
111114
112115 $params['limit'] = new Parameter( 'limit', Parameter::TYPE_INTEGER );
113116 $params['limit']->setDefault( 200 );
114 - $params['limit']->addCriteria( new CriterionInRange( 1, 500 ) );
 117+ $params['limit']->addCriteria( new CriterionInRange( 1, 500 ) );
115118 $params['limit']->setDescription( wfMsg( 'spl-subpages-par-limit' ) );
 119+
 120+ // TODO: Add description strings. --vdb
 121+ $params['element'] = new Parameter( 'element', Parameter::TYPE_STRING, 'div' );
 122+ $params['element']->addCriteria( new CriterionInArray( 'div', 'p', 'span' ) );
 123+ $params['class'] = new Parameter( 'class', Parameter::TYPE_STRING, 'subpagelist' );
 124+ $params['intro'] = new Parameter( 'intro', Parameter::TYPE_STRING, '' );
 125+ $params['outro'] = new Parameter( 'outro', Parameter::TYPE_STRING, '' );
 126+ $params['default'] = new Parameter( 'default', Parameter::TYPE_STRING, '' );
 127+ $params['separator'] = new Parameter( 'separator', Parameter::TYPE_STRING, '&#160;· ' );
 128+ $params['separator']->addAliases( 'sep' );
 129+ $params['template'] = new Parameter( 'template', Parameter::TYPE_STRING, '' );
 130+ $params['links'] = new Parameter( 'links', Parameter::TYPE_BOOLEAN, true );
 131+ $params['links']->addAliases( 'link' );
116132
117133 return $params;
118134 }
@@ -140,17 +156,65 @@
141157 */
142158 public function render( array $parameters ) {
143159 $title = $this->getTitle( $parameters );
144 -
145160 $pages = $this->getSubPages( $title, $parameters );
146 -
 161+ // There is no need in encoding `$parameters['element']', because it is validated and can
 162+ // be only one of `span', `p', or `div'.
 163+ $element = $parameters['element'];
 164+ // Using `$parameters['class']' is dangerous and may be a security hole, because it may lead
 165+ // to incorrect (or malicious) HTML code. `encodeAttribute' solves the issue.
 166+ $class = Sanitizer::encodeAttribute( $parameters['class'] );
 167+ $open = "<$element class=\"$class\">";
 168+ $close = "</$element>";
 169+ $inlineList = ( $parameters['format'] == 'list' || $parameters['format'] == 'bar' );
 170+ $inlineText = ( $element == 'span' );
 171+ $list = '';
 172+
147173 if ( count( $pages ) > 0 ) {
148 - $list = $this->makeList( $title, $parameters, $pages );
 174+ $intro = $parameters['intro'];
 175+ $outro = $parameters['outro'];
 176+ if ( $inlineText && ! $inlineList ) {
 177+ if ( $intro !== '' ) {
 178+ $list .= $open . $intro . $close;
 179+ }
 180+ $list .=
 181+ "<div class=\"$class\">" .
 182+ $this->makeList( $title, $parameters, $pages ) .
 183+ "</div>";
 184+ if ( $outro !== "" ) {
 185+ $list .= $open . $outro . $close;
 186+ }
 187+ }
 188+ else {
 189+ $list =
 190+ $open . $intro .
 191+ $this->makeList( $title, $parameters, $pages ) .
 192+ $outro . $close;
 193+ }
 194+ $list = $this->parseWikitext( $list );
149195 }
150196 else {
151 - $list = "''" . wfMsg( 'spl-nosubpages', '[[' . $title->getFullText() . ']]' ) . "''\n";
 197+ $default = $parameters['default'];
 198+ if ( $default === "" ) {
 199+ if ( is_null( $title ) ) {
 200+ $list = "''" . wfMsg( 'spl-noparentpage', $parameters['page'] ) . "''";
 201+ }
 202+ elseif ( $title instanceof Title ) {
 203+ $list = "''" . wfMsg( 'spl-nosubpages', '[[' . $title->getFullText() . ']]' ) . "''";
 204+ }
 205+ else {
 206+ $list = "''" . wfMsg( 'spl-nopages', $parameters['page'] ) . "''";
 207+ }
 208+ }
 209+ elseif ( $default !== "-" ) {
 210+ $list = $default;
 211+ }
 212+ // Format element only if content is not empty.
 213+ if ( $list !== "" ) {
 214+ $list = $open . $this->parseWikitext( $list ) . $close;
 215+ }
152216 }
153 -
154 - return "<div class='subpagelist'>\n" . $this->parseWikitext( $list ) . "\n</div>";
 217+
 218+ return $list;
155219 }
156220
157221 /**
@@ -160,23 +224,32 @@
161225 *
162226 * @param array $parameters
163227 *
164 - * @return Title
 228+ * @return Instance of Title class — title of an existing page, or integer — index of an
 229+ * existing namespace, or null otherwise.
165230 */
166231 protected function getTitle( array $parameters ) {
167 - $title = false;
 232+ global $wgContLang;
168233
169 - if ( $parameters['page'] != '' ) {
170 - $specifiedTitle = Title::newFromText( $parameters['page'] );
171 -
172 - if ( !is_null( $specifiedTitle ) && $specifiedTitle->exists() ) {
173 - $title = $specifiedTitle;
 234+ $page = $parameters['page'];
 235+ $title = null;
 236+
 237+ if ( $page == '' ) {
 238+ $title = $this->parser->mTitle;
 239+ }
 240+ else {
 241+ $title = Title::newFromText( $page );
 242+ if ( is_null( $title ) ) {
 243+ // It is a wrog page name. Probably it is a namespace name?
 244+ $m = array();
 245+ if ( preg_match( '/^\s*(.*):\s*$/', $page, $m ) ) {
 246+ $title = $wgContLang->getNsIndex( $m[ 1 ] );
 247+ }
174248 }
 249+ else if ( ! $title->exists() ) {
 250+ $title = null;
 251+ }
175252 }
176253
177 - if ( $title === false ) {
178 - $title = $this->parser->mTitle;
179 - }
180 -
181254 return $title;
182255 }
183256
@@ -185,69 +258,110 @@
186259 *
187260 * @since 0.1
188261 *
189 - * @param Title $title
 262+ * @param $title can be either an instance of Title class (title of an existing page), or number
 263+ * (index of an existing namespace) or null.
190264 * @param array $parameters
191265 *
192266 * @return array of Title
193267 */
194268 protected function getSubPages( Title $title, array $parameters ) {
195269 $titles = array();
196 -
197 - $dbr = wfGetDB( DB_SLAVE );
198 -
199 - $options = array();
200 - $options['ORDER BY'] =
201 - ( $parameters['sortby'] == 'title' ? 'page_title' : 'page_touched' ) . ' ' .
202 - ( strtoupper( $parameters['sort'] ) );
203 - $options['LIMIT'] = $parameters['limit'];
204270
205 - $conditions = array();
206 - $conditions['page_namespace'] = $title->getNamespace(); // Don't let list cross namespaces.
207 - $conditions['page_is_redirect'] = 0;
208 -
209 - // TODO: this is rather resource heavy
210 - $conditions[] = 'page_title ' . $dbr->buildLike( $title->getDBkey() . '/', $dbr->anyString() );
 271+ if ( ! is_null( $title ) ) {
 272+ // TODO: Check whether subpages enabled?
 273+ $dbr = wfGetDB( DB_SLAVE );
 274+
 275+ $options = array();
 276+ $options['ORDER BY'] =
 277+ ( $parameters['sortby'] == 'title' ? 'page_title' : 'page_touched' ) . ' ' .
 278+ ( strtoupper( $parameters['sort'] ) );
 279+ $options['LIMIT'] = $parameters['limit'];
211280
212 - $fields = array();
213 - $fields[] = 'page_title';
214 - $fields[] = 'page_namespace';
215 -
216 - $res = $dbr->select( 'page', $fields, $conditions, __METHOD__, $options );
217 -
218 - foreach( $res as $row ) {
219 - $title = Title::makeTitleSafe( $row->page_namespace, $row->page_title );
220 - if( is_object( $title ) ) {
221 - $titles[] = $title;
 281+ $conditions = array();
 282+ $conditions['page_is_redirect'] = 0;
 283+ if ( $title instanceof Title ) {
 284+ $conditions['page_namespace'] = $title->getNamespace(); // Don't let list cross namespaces.
 285+ // TODO: this is rather resource heavy
 286+ $conditions[] = 'page_title ' . $dbr->buildLike( $title->getDBkey() . '/', $dbr->anyString() );
 287+ if ( $parameters['kidsonly'] ) {
 288+ $conditions[] = 'page_title NOT ' . $dbr->buildLike( $title->getDBkey() . '/', $dbr->anyString(), '/', $dbr->anyString() );
 289+ }
222290 }
223 - }
224 -
225 - $dbr->freeResult( $res );
 291+ else {
 292+ $conditions['page_namespace'] = $title;
 293+ if ( $parameters['kidsonly'] ) {
 294+ // Request only root pages, not subpages.
 295+ $conditions[] = 'page_title NOT ' . $dbr->buildLike( $dbr->anyString(), '/', $dbr->anyString() );
 296+ }
 297+ else {
 298+ $conditions[] = 'page_title ' . $dbr->buildLike( $dbr->anyString() );
 299+ }
 300+ }
226301
 302+ $fields = array();
 303+ $fields[] = 'page_title';
 304+ $fields[] = 'page_namespace';
 305+
 306+ $res = $dbr->select( 'page', $fields, $conditions, __METHOD__, $options );
 307+
 308+ foreach( $res as $row ) {
 309+ $title = Title::makeTitleSafe( $row->page_namespace, $row->page_title );
 310+ if( is_object( $title ) ) {
 311+ $titles[] = $title;
 312+ }
 313+ }
 314+
 315+ $dbr->freeResult( $res );
 316+
 317+ }
 318+
227319 return $titles;
228320 }
229321
230322 /**
231323 * Creates one list item.
232324 *
233 - * @param Title $title the title of a page
234 - * @param array $parameters
 325+ * @param $fullName — Full name of page, including namespace (but excluding fragment).
 326+ * @param $nsLen — Length of namespace name (including colon, if any).
 327+ * @param $parentLen — Length of parent title (not including namespace but including slash).
235328 *
236329 * @return string: wikitext for the list item
237330 */
238 - protected function makeListItem( Title $title, array $parameters ) {
 331+ protected function makeListItem( $fullName, $nsLen, $parentLen, array $parameters ) {
239332 switch( $parameters['pathstyle'] ) {
240 - case 'none' : case 'no' :
241 - $linktitle = substr( strrchr( $title->getText(), "/" ), 1 );
 333+ case 'none' : case 'no' :
 334+ // Just a last item.
 335+ $slash = strrpos( $fullName, '/' );
 336+ if ( $slash ) {
 337+ $item = substr( $fullName, $slash + 1 ); // +1 to skip slash.
 338+ }
 339+ else {
 340+ $item = substr( $fullName, $nsLen );
 341+ }
242342 break;
243 - case 'children' : case 'notparent' :
244 - $linktitle = substr( strstr( $title->getText(), "/" ), 1 );
 343+ case 'subpagename' : case 'children' : case 'notparent' :
 344+ // Pagename starting from parent.
 345+ $item = substr( $fullName, $nsLen + $parentLen );
245346 break;
246 - case 'full' :
247 - $linktitle = $title->getText();
 347+ case 'pagename' : case 'full' :
 348+ // Almost full (without namespace).
 349+ $item = substr( $fullName, $nsLen );
248350 break;
 351+ case 'fullpagename' :
 352+ // Full name (including namespace).
 353+ $item = $fullName;
 354+ break;
249355 }
250356
251 - return ' [[' . $title->getFullText() . '|' . $linktitle . ']]';
 357+ if ( $parameters['links'] ) {
 358+ $item = "[[$fullName|$item]]";
 359+ }
 360+
 361+ if ( $parameters['template'] !== '' ) {
 362+ $item = '{{' . $parameters['template'] . '|' . $item . '}}';
 363+ }
 364+
 365+ return $item;
252366 }
253367
254368 /**
@@ -262,69 +376,95 @@
263377 * @return string the whole list
264378 */
265379 protected function makeList( Title $title, array $parameters, array $titles ) {
266 - $token = '';
267 - $isSingleLine = false;
268 -
 380+ global $wgContLang;
 381+ $start = ''; // String to render once in the very beginning of each item.
 382+ $bullet = ''; // String to render between `$start' and item
 383+ // (may be rendered few times, depends on nesting level).
 384+ $sep = ''; // String to render between two items.
269385 $items = array();
270386
271387 switch ( $parameters['format'] ) {
272388 case 'ol' : case 'ordered' :
273 - $token = '#';
 389+ $start = "\n";
 390+ $bullet = '#';
274391 break;
275392 case 'ul' : case 'unordered' :
276 - $token = '*';
 393+ $start = "\n";
 394+ $bullet = '*';
277395 break;
278396 case 'list' : case 'bar' :
279 - $isSingleLine = true;
280 - $token = '&#160;· ';
 397+ $sep = $parameters['separator'];
281398 break;
282399 }
283 -
284 - if ( $parameters['showpage'] ) {
285 - $item = '[[' . $title->getFullText() .'|'. $title->getText() .']]';
286 -
287 - if ( !$isSingleLine ) {
288 - $item = $token . $item;
 400+ // A kind of optimization: I do not want to run loop every time I need series of bullets.
 401+ // Let us intialize $bullets array so $bullets[$i] is a bullet repeated $i times.
 402+ $bullets = array();
 403+ $bullets[0] = $bullet;
 404+
 405+ // WARNING: It seems strlen and other sring functions operated with bytes, not characters.
 406+ // But it seems it is ok for UTF-8 encoding...
 407+
 408+ if ( $title instanceof Title ) {
 409+ $nsName = $title->getNsText(); // Namespace name.
 410+ $parentFull = $title->getPrefixedText(); // Including namespace.
 411+ $parentText = $title->getText(); // Not including namespace.
 412+ $parentSlashCount = substr_count( $parentFull, '/' );
 413+ }
 414+ else {
 415+ $nsName = $wgContLang->getNsText( $title );
 416+ $parentFull = $nsName;
 417+ $parentText = '';
 418+ $parentSlashCount = -1;
 419+ }
 420+ // If prefix (namespace name) is not empty, count subsequent colon also.
 421+ $nsLen = strlen( $nsName );
 422+ if ( $nsLen > 0 ) {
 423+ ++ $nsLen;
 424+ }
 425+ // If parent page name is not empty, count subsequent slash also.
 426+ $parentLen = strlen( $parentText );
 427+ if ( $parentLen > 0 ) {
 428+ ++ $parentLen;
 429+ }
 430+ // Max nesting level.
 431+ $maxLevel = ( $parameters['kidsonly'] ? 1 : 1000 );
 432+
 433+ if ( $parameters['showpage'] && $title instanceof Title ) {
 434+ // If parent should be shown, correct starting point:
 435+ $slash = strrpos( $parentText, "/" );
 436+ if ( $slash ) {
 437+ $parentLen = $slash + 1;
289438 }
290 -
291 - $items[] = trim( $item );
 439+ else {
 440+ $parentLen = 0;
 441+ }
 442+ -- $parentSlashCount;
 443+ ++ $maxLevel;
 444+ // Render page itself as the very first item of the list.
 445+ $item =
 446+ $start . $bullet .
 447+ $this->makeListItem( $parentFull, $nsLen, $parentLen, $parameters );
 448+ $items[] = $item;
292449 }
293 -
294 - $parentLevel = substr_count( $title->getFullText(), '/' );
295 -
296 - $isFirst = true;
297 -
298 - foreach( $titles as $subPageTitle ) {
299 - $level = substr_count( $subPageTitle->getFullText(), '/' ) - $parentLevel;
300 -
301 - if ( !$parameters['kidsonly'] || $level < 2 ) {
302 -
303 - if ( $parameters['showpage'] ) {
304 - $level++;
305 - }
306 -
 450+
 451+ foreach( $titles as $pageTitle ) {
 452+ $pageFull = $pageTitle->getPrefixedText();
 453+ $level = substr_count( $pageFull, '/' ) - $parentSlashCount;
 454+ if ( $level <= $maxLevel ) {
307455 $item = '';
308 -
309 - if( $isSingleLine ) {
310 - if( $isFirst ) {
311 - $item .= ': ';
312 - }
313 - else {
314 - $item .= $token;
315 - }
316 - } else {
317 - for ( $i = 0; $i < $level; $i++ ) {
318 - $item .= $token;
319 - }
 456+ if ( $bullet != '' ) {
 457+ // Make sure $bullets[ $level ] is properly initialized.
 458+ for ( $l = sizeof( $bullets ); $l <= $level; ++ $l ) {
 459+ $bullets[$l] = $bullets[$l - 1] . $bullet;
 460+ }
 461+ $item .= $start . $bullets[ $level ];
320462 }
321 -
322 - $items[] = trim( $item . $this->makeListItem( $subPageTitle, $parameters ) );
 463+ $item .= $this->makeListItem( $pageFull, $nsLen, $parentLen, $parameters );
 464+ $items[] = $item;
323465 }
324 -
325 - $isFirst = false;
326466 }
327467
328 - return implode( $isSingleLine ? '' : "\n", $items );
 468+ return implode( $sep, $items );
329469 }
330470
331471 /**
@@ -340,6 +480,6 @@
341481 'noparse' => true,
342482 'isHTML' => true
343483 );
344 - }
 484+ }
345485
346486 }

Status & tagging log