MediaWiki REL1_34
CategoryTree.php
Go to the documentation of this file.
1<?php
26
32 public $mOptions = [];
33
38
43 public function __construct( array $options ) {
44 global $wgCategoryTreeDefaultOptions;
45 $this->linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
46
47 // ensure default values and order of options.
48 // Order may become important, it may influence the cache key!
49 foreach ( $wgCategoryTreeDefaultOptions as $option => $default ) {
50 if ( isset( $options[$option] ) ) {
51 $this->mOptions[$option] = $options[$option];
52 } else {
53 $this->mOptions[$option] = $default;
54 }
55 }
56
57 $this->mOptions['mode'] = self::decodeMode( $this->mOptions['mode'] );
58
59 if ( $this->mOptions['mode'] == CategoryTreeMode::PARENTS ) {
60 // namespace filter makes no sense with CategoryTreeMode::PARENTS
61 $this->mOptions['namespaces'] = false;
62 }
63
64 $this->mOptions['hideprefix'] = self::decodeHidePrefix( $this->mOptions['hideprefix'] );
65 $this->mOptions['showcount'] = self::decodeBoolean( $this->mOptions['showcount'] );
66 $this->mOptions['namespaces'] = self::decodeNamespaces( $this->mOptions['namespaces'] );
67
68 if ( $this->mOptions['namespaces'] ) {
69 # automatically adjust mode to match namespace filter
70 if ( count( $this->mOptions['namespaces'] ) === 1
71 && $this->mOptions['namespaces'][0] == NS_CATEGORY ) {
72 $this->mOptions['mode'] = CategoryTreeMode::CATEGORIES;
73 } elseif ( !in_array( NS_FILE, $this->mOptions['namespaces'] ) ) {
74 $this->mOptions['mode'] = CategoryTreeMode::PAGES;
75 } else {
76 $this->mOptions['mode'] = CategoryTreeMode::ALL;
77 }
78 }
79 }
80
85 public function getOption( $name ) {
86 return $this->mOptions[$name];
87 }
88
92 private function isInverse() {
93 return $this->getOption( 'mode' ) == CategoryTreeMode::PARENTS;
94 }
95
100 private static function decodeNamespaces( $nn ) {
101 if ( $nn === false || is_null( $nn ) ) {
102 return false;
103 }
104
105 if ( !is_array( $nn ) ) {
106 $nn = preg_split( '![\s#:|]+!', $nn );
107 }
108
109 $namespaces = [];
110 $contLang = MediaWikiServices::getInstance()->getContentLanguage();
111 foreach ( $nn as $n ) {
112 if ( is_int( $n ) ) {
113 $ns = $n;
114 } else {
115 $n = trim( $n );
116 if ( $n === '' ) {
117 continue;
118 }
119
120 $lower = strtolower( $n );
121
122 if ( is_numeric( $n ) ) {
123 $ns = (int)$n;
124 } elseif ( $n == '-' || $n == '_' || $n == '*' || $lower == 'main' ) {
125 $ns = NS_MAIN;
126 } else {
127 $ns = $contLang->getNsIndex( $n );
128 }
129 }
130
131 if ( is_int( $ns ) ) {
132 $namespaces[] = $ns;
133 }
134 }
135
136 # get elements into canonical order
137 sort( $namespaces );
138 return $namespaces;
139 }
140
145 public static function decodeMode( $mode ) {
146 global $wgCategoryTreeDefaultOptions;
147
148 if ( is_null( $mode ) ) {
149 return $wgCategoryTreeDefaultOptions['mode'];
150 }
151 if ( is_int( $mode ) ) {
152 return $mode;
153 }
154
155 $mode = trim( strtolower( $mode ) );
156
157 if ( is_numeric( $mode ) ) {
158 return (int)$mode;
159 }
160
161 if ( $mode == 'all' ) {
162 $mode = CategoryTreeMode::ALL;
163 } elseif ( $mode == 'pages' ) {
165 } elseif ( $mode == 'categories' || $mode == 'sub' ) {
167 } elseif ( $mode == 'parents' || $mode == 'super' || $mode == 'inverse' ) {
169 } elseif ( $mode == 'default' ) {
170 $mode = $wgCategoryTreeDefaultOptions['mode'];
171 }
172
173 return (int)$mode;
174 }
175
182 public static function decodeBoolean( $value ) {
183 if ( is_null( $value ) ) {
184 return null;
185 }
186 if ( is_bool( $value ) ) {
187 return $value;
188 }
189 if ( is_int( $value ) ) {
190 return ( $value > 0 );
191 }
192
193 $value = trim( strtolower( $value ) );
194 if ( is_numeric( $value ) ) {
195 return ( (int)$value > 0 );
196 }
197
198 if ( $value == 'yes' || $value == 'y'
199 || $value == 'true' || $value == 't' || $value == 'on'
200 ) {
201 return true;
202 } elseif ( $value == 'no' || $value == 'n'
203 || $value == 'false' || $value == 'f' || $value == 'off'
204 ) {
205 return false;
206 } elseif ( $value == 'null' || $value == 'default' || $value == 'none' || $value == 'x' ) {
207 return null;
208 } else {
209 return false;
210 }
211 }
212
217 public static function decodeHidePrefix( $value ) {
218 global $wgCategoryTreeDefaultOptions;
219
220 if ( is_null( $value ) ) {
221 return $wgCategoryTreeDefaultOptions['hideprefix'];
222 }
223 if ( is_int( $value ) ) {
224 return $value;
225 }
226 if ( $value === true ) {
228 }
229 if ( $value === false ) {
231 }
232
233 $value = trim( strtolower( $value ) );
234
235 if ( $value == 'yes' || $value == 'y'
236 || $value == 'true' || $value == 't' || $value == 'on'
237 ) {
239 } elseif ( $value == 'no' || $value == 'n'
240 || $value == 'false' || $value == 'f' || $value == 'off'
241 ) {
243 } elseif ( $value == 'always' ) {
245 } elseif ( $value == 'never' ) {
247 } elseif ( $value == 'auto' ) {
249 } elseif ( $value == 'categories' || $value == 'category' || $value == 'smart' ) {
251 } else {
252 return $wgCategoryTreeDefaultOptions['hideprefix'];
253 }
254 }
255
260 public static function setHeaders( OutputPage $outputPage ) {
261 # Add the modules
262 $outputPage->addModuleStyles( 'ext.categoryTree.styles' );
263 $outputPage->addModules( 'ext.categoryTree' );
264 }
265
272 protected static function encodeOptions( array $options, $enc ) {
273 if ( $enc == 'mode' || $enc == '' ) {
274 $opt = $options['mode'];
275 } elseif ( $enc == 'json' ) {
276 $opt = FormatJson::encode( $options );
277 } else {
278 throw new Exception( 'Unknown encoding for CategoryTree options: ' . $enc );
279 }
280
281 return $opt;
282 }
283
288 public function getOptionsAsCacheKey( $depth = null ) {
289 $key = "";
290
291 foreach ( $this->mOptions as $k => $v ) {
292 if ( is_array( $v ) ) {
293 $v = implode( '|', $v );
294 }
295 $key .= $k . ':' . $v . ';';
296 }
297
298 if ( !is_null( $depth ) ) {
299 $key .= ";depth=" . $depth;
300 }
301 return $key;
302 }
303
308 public function getOptionsAsJsStructure( $depth = null ) {
309 if ( $depth !== null ) {
310 $opt = $this->mOptions;
311 $opt['depth'] = $depth;
312 $s = self::encodeOptions( $opt, 'json' );
313 } else {
314 $s = self::encodeOptions( $this->mOptions, 'json' );
315 }
316
317 return $s;
318 }
319
332 public function getTag( Parser $parser = null, $category, $hideroot = false, array $attr = [],
333 $depth = 1, $allowMissing = false
334 ) {
335 global $wgCategoryTreeDisableCache;
336
337 $category = trim( $category );
338 if ( $category === '' ) {
339 return false;
340 }
341
342 if ( $parser ) {
343 if ( $wgCategoryTreeDisableCache === true ) {
344 $parser->getOutput()->updateCacheExpiry( 0 );
345 } elseif ( is_int( $wgCategoryTreeDisableCache ) ) {
346 $parser->getOutput()->updateCacheExpiry( $wgCategoryTreeDisableCache );
347 }
348 }
349
350 $title = self::makeTitle( $category );
351
352 if ( $title === false || $title === null ) {
353 return false;
354 }
355
356 if ( isset( $attr['class'] ) ) {
357 $attr['class'] .= ' CategoryTreeTag';
358 } else {
359 $attr['class'] = ' CategoryTreeTag';
360 }
361
362 $attr['data-ct-mode'] = $this->mOptions['mode'];
363 $attr['data-ct-options'] = $this->getOptionsAsJsStructure();
364
365 $html = '';
366 $html .= Html::openElement( 'div', $attr );
367
368 if ( !$allowMissing && !$title->getArticleID() ) {
369 $html .= Html::openElement( 'span', [ 'class' => 'CategoryTreeNotice' ] );
370 if ( $parser ) {
371 $html .= $parser->recursiveTagParse(
372 wfMessage( 'categorytree-not-found', $category )->plain() );
373 } else {
374 $html .= wfMessage( 'categorytree-not-found', $category )->parse();
375 }
376 $html .= Html::closeElement( 'span' );
377 } else {
378 if ( !$hideroot ) {
379 $html .= $this->renderNode( $title, $depth );
380 } else {
381 $html .= $this->renderChildren( $title, $depth );
382 }
383 }
384
385 $html .= Xml::closeElement( 'div' );
386
387 return $html;
388 }
389
396 public function renderChildren( Title $title, $depth = 1 ) {
397 global $wgCategoryTreeMaxChildren, $wgCategoryTreeUseCategoryTable;
398
399 if ( $title->getNamespace() != NS_CATEGORY ) {
400 // Non-categories can't have children. :)
401 return '';
402 }
403
405
406 $inverse = $this->isInverse();
407 $mode = $this->getOption( 'mode' );
408 $namespaces = $this->getOption( 'namespaces' );
409
410 $tables = [ 'page', 'categorylinks' ];
411 $fields = [ 'page_id', 'page_namespace', 'page_title',
412 'page_is_redirect', 'page_len', 'page_latest', 'cl_to',
413 'cl_from' ];
414 $where = [];
415 $joins = [];
416 $options = [ 'ORDER BY' => 'cl_type, cl_sortkey', 'LIMIT' => $wgCategoryTreeMaxChildren ];
417
418 if ( $inverse ) {
419 $joins['categorylinks'] = [ 'RIGHT JOIN', [
420 'cl_to = page_title', 'page_namespace' => NS_CATEGORY
421 ] ];
422 $where['cl_from'] = $title->getArticleID();
423 } else {
424 $joins['categorylinks'] = [ 'JOIN', 'cl_from = page_id' ];
425 $where['cl_to'] = $title->getDBkey();
426 $options['USE INDEX']['categorylinks'] = 'cl_sortkey';
427
428 # namespace filter.
429 if ( $namespaces ) {
430 // NOTE: we assume that the $namespaces array contains only integers!
431 // decodeNamepsaces makes it so.
432 $where['page_namespace'] = $namespaces;
433 } elseif ( $mode != CategoryTreeMode::ALL ) {
434 if ( $mode == CategoryTreeMode::PAGES ) {
435 $where['cl_type'] = [ 'page', 'subcat' ];
436 } else {
437 $where['cl_type'] = 'subcat';
438 }
439 }
440 }
441
442 # fetch member count if possible
443 $doCount = !$inverse && $wgCategoryTreeUseCategoryTable;
444
445 if ( $doCount ) {
446 $tables = array_merge( $tables, [ 'category' ] );
447 $fields = array_merge( $fields, [
448 'cat_id', 'cat_title', 'cat_subcats', 'cat_pages', 'cat_files'
449 ] );
450 $joins['category'] = [ 'LEFT JOIN', [
451 'cat_title = page_title', 'page_namespace' => NS_CATEGORY ]
452 ];
453 }
454
455 $res = $dbr->select( $tables, $fields, $where, __METHOD__, $options, $joins );
456
457 # collect categories separately from other pages
458 $categories = '';
459 $other = '';
460
461 foreach ( $res as $row ) {
462 # NOTE: in inverse mode, the page record may be null, because we use a right join.
463 # happens for categories with no category page (red cat links)
464 if ( $inverse && $row->page_title === null ) {
465 $t = Title::makeTitle( NS_CATEGORY, $row->cl_to );
466 } else {
467 # TODO: translation support; ideally added to Title object
468 $t = Title::newFromRow( $row );
469 }
470
471 $cat = null;
472
473 if ( $doCount && $row->page_namespace == NS_CATEGORY ) {
474 $cat = Category::newFromRow( $row, $t );
475 }
476
477 $s = $this->renderNodeInfo( $t, $cat, $depth - 1 );
478
479 if ( $row->page_namespace == NS_CATEGORY ) {
480 $categories .= $s;
481 } else {
482 $other .= $s;
483 }
484 }
485
486 return $categories . $other;
487 }
488
494 public function renderParents( Title $title ) {
495 global $wgCategoryTreeMaxChildren;
496
498
499 $res = $dbr->select(
500 'categorylinks',
501 [ 'cl_to' ],
502 [ 'cl_from' => $title->getArticleID() ],
503 __METHOD__,
504 [
505 'LIMIT' => $wgCategoryTreeMaxChildren,
506 'ORDER BY' => 'cl_to'
507 ]
508 );
509
510 $special = SpecialPage::getTitleFor( 'CategoryTree' );
511
512 $s = '';
513
514 foreach ( $res as $row ) {
515 $t = Title::makeTitle( NS_CATEGORY, $row->cl_to );
516
517 if ( $s !== '' ) {
518 $s .= wfMessage( 'pipe-separator' )->escaped();
519 }
520
521 $s .= Xml::openElement( 'span', [ 'class' => 'CategoryTreeItem' ] );
522 $s .= $this->linkRenderer->makeLink(
523 $special,
524 $t->getText(),
525 [ 'class' => 'CategoryTreeLabel' ],
526 [ 'target' => $t->getPartialURL() ] + $this->mOptions
527 );
528 $s .= Xml::closeElement( 'span' );
529 }
530
531 return $s;
532 }
533
540 public function renderNode( Title $title, $children = 0 ) {
541 global $wgCategoryTreeUseCategoryTable;
542
543 if ( $wgCategoryTreeUseCategoryTable && $title->getNamespace() == NS_CATEGORY
544 && !$this->isInverse()
545 ) {
546 $cat = Category::newFromTitle( $title );
547 } else {
548 $cat = null;
549 }
550
551 return $this->renderNodeInfo( $title, $cat, $children );
552 }
553
562 public function renderNodeInfo( Title $title, Category $cat = null, $children = 0 ) {
563 $mode = $this->getOption( 'mode' );
564
565 $ns = $title->getNamespace();
566 $key = $title->getDBkey();
567
568 $hideprefix = $this->getOption( 'hideprefix' );
569
570 if ( $hideprefix == CategoryTreeHidePrefix::ALWAYS ) {
571 $hideprefix = true;
572 } elseif ( $hideprefix == CategoryTreeHidePrefix::AUTO ) {
573 $hideprefix = ( $mode == CategoryTreeMode::CATEGORIES );
574 } elseif ( $hideprefix == CategoryTreeHidePrefix::CATEGORIES ) {
575 $hideprefix = ( $ns == NS_CATEGORY );
576 } else {
577 $hideprefix = true;
578 }
579
580 // when showing only categories, omit namespace in label unless we explicitely defined the
581 // configuration setting
582 // patch contributed by Manuel Schneider <manuel.schneider@wikimedia.ch>, Bug 8011
583 if ( $hideprefix ) {
584 $label = $title->getText();
585 } else {
586 $label = $title->getPrefixedText();
587 }
588
589 $link = $this->linkRenderer->makeLink( $title, $label );
590
591 $count = false;
592 $s = '';
593
594 # NOTE: things in CategoryTree.js rely on the exact order of tags!
595 # Specifically, the CategoryTreeChildren div must be the first
596 # sibling with nodeName = DIV of the grandparent of the expland link.
597
598 $s .= Xml::openElement( 'div', [ 'class' => 'CategoryTreeSection' ] );
599 $s .= Xml::openElement( 'div', [ 'class' => 'CategoryTreeItem' ] );
600
601 $attr = [ 'class' => 'CategoryTreeBullet' ];
602
603 if ( $ns == NS_CATEGORY ) {
604 if ( $cat ) {
605 if ( $mode == CategoryTreeMode::CATEGORIES ) {
606 $count = intval( $cat->getSubcatCount() );
607 } elseif ( $mode == CategoryTreeMode::PAGES ) {
608 $count = intval( $cat->getPageCount() ) - intval( $cat->getFileCount() );
609 } else {
610 $count = intval( $cat->getPageCount() );
611 }
612 }
613 if ( $count === 0 ) {
614 $bullet = wfMessage( 'categorytree-empty-bullet' )->escaped() . ' ';
615 $attr['class'] = 'CategoryTreeEmptyBullet';
616 } else {
617 $linkattr = [];
618
619 $linkattr[ 'class' ] = "CategoryTreeToggle";
620 $linkattr['data-ct-title'] = $key;
621
622 if ( $children == 0 ) {
623 // Use ->plain() to ensure identical result as JS,
624 // which does:
625 // $link.text( mw.msg( 'categorytree-expand-bullet' ) );
626 $txt = wfMessage( 'categorytree-expand-bullet' )->plain();
627 $linkattr[ 'data-ct-state' ] = 'collapsed';
628 } else {
629 $txt = wfMessage( 'categorytree-collapse-bullet' )->plain();
630 $linkattr[ 'data-ct-loaded' ] = true;
631 $linkattr[ 'data-ct-state' ] = 'expanded';
632 }
633
634 $bullet = Html::element( 'span', $linkattr, $txt ) . ' ';
635 }
636 } else {
637 $bullet = wfMessage( 'categorytree-page-bullet' )->escaped();
638 }
639 $s .= Xml::tags( 'span', $attr, $bullet ) . ' ';
640
641 $s .= $link;
642
643 if ( $count !== false && $this->getOption( 'showcount' ) ) {
644 $s .= self::createCountString( RequestContext::getMain(), $cat, $count );
645 }
646
647 $s .= Xml::closeElement( 'div' );
648 $s .= Xml::openElement(
649 'div',
650 [
651 'class' => 'CategoryTreeChildren',
652 'style' => $children > 0 ? "display:block" : "display:none"
653 ]
654 );
655
656 if ( $ns == NS_CATEGORY && $children > 0 ) {
657 $children = $this->renderChildren( $title, $children );
658 if ( $children == '' ) {
659 $s .= Xml::openElement( 'i', [ 'class' => 'CategoryTreeNotice' ] );
660 if ( $mode == CategoryTreeMode::CATEGORIES ) {
661 $s .= wfMessage( 'categorytree-no-subcategories' )->escaped();
662 } elseif ( $mode == CategoryTreeMode::PAGES ) {
663 $s .= wfMessage( 'categorytree-no-pages' )->escaped();
664 } elseif ( $mode == CategoryTreeMode::PARENTS ) {
665 $s .= wfMessage( 'categorytree-no-parent-categories' )->escaped();
666 } else {
667 $s .= wfMessage( 'categorytree-nothing-found' )->escaped();
668 }
669 $s .= Xml::closeElement( 'i' );
670 } else {
671 $s .= $children;
672 }
673 }
674
675 $s .= Xml::closeElement( 'div' );
676 $s .= Xml::closeElement( 'div' );
677
678 return $s;
679 }
680
689 public static function createCountString( IContextSource $context, Category $cat = null,
690 $countMode
691 ) {
692 # Get counts, with conversion to integer so === works
693 # Note: $allCount is the total number of cat members,
694 # not the count of how many members are normal pages.
695 $allCount = $cat ? intval( $cat->getPageCount() ) : 0;
696 $subcatCount = $cat ? intval( $cat->getSubcatCount() ) : 0;
697 $fileCount = $cat ? intval( $cat->getFileCount() ) : 0;
698 $pages = $allCount - $subcatCount - $fileCount;
699
700 $attr = [
701 'title' => $context->msg( 'categorytree-member-counts' )
702 ->numParams( $subcatCount, $pages, $fileCount, $allCount, $countMode )->text(),
703 # numbers and commas get messed up in a mixed dir env
704 'dir' => $context->getLanguage()->getDir()
705 ];
706 $contLang = MediaWikiServices::getInstance()->getContentLanguage();
707 $s = $contLang->getDirMark() . ' ';
708
709 # Create a list of category members with only non-zero member counts
710 $memberNums = [];
711 if ( $subcatCount ) {
712 $memberNums[] = $context->msg( 'categorytree-num-categories' )
713 ->numParams( $subcatCount )->text();
714 }
715 if ( $pages ) {
716 $memberNums[] = $context->msg( 'categorytree-num-pages' )->numParams( $pages )->text();
717 }
718 if ( $fileCount ) {
719 $memberNums[] = $context->msg( 'categorytree-num-files' )
720 ->numParams( $fileCount )->text();
721 }
722 $memberNumsShort = $memberNums
723 ? $context->getLanguage()->commaList( $memberNums )
724 : $context->msg( 'categorytree-num-empty' )->text();
725
726 # Only $5 is actually used in the default message.
727 # Other arguments can be used in a customized message.
728 $s .= Xml::tags(
729 'span',
730 $attr,
731 $context->msg( 'categorytree-member-num' )
732 // Do not use numParams on params 1-4, as they are only used for customisation.
733 ->params( $subcatCount, $pages, $fileCount, $allCount, $memberNumsShort )
734 ->escaped()
735 );
736
737 return $s;
738 }
739
745 public static function makeTitle( $title ) {
746 $title = trim( $title );
747
748 if ( strval( $title ) === '' ) {
749 return null;
750 }
751
752 # The title must be in the category namespace
753 # Ignore a leading Category: if there is one
754 $t = Title::newFromText( $title, NS_CATEGORY );
755 if ( !$t || $t->getNamespace() != NS_CATEGORY || $t->getInterwiki() != '' ) {
756 // If we were given something like "Wikipedia:Foo" or "Template:",
757 // try it again but forced.
758 $title = "Category:$title";
759 $t = Title::newFromText( $title );
760 }
761 return $t;
762 }
763
771 public static function capDepth( $mode, $depth ) {
772 global $wgCategoryTreeMaxDepth;
773
774 if ( is_numeric( $depth ) ) {
775 $depth = intval( $depth );
776 } else {
777 return 1;
778 }
779
780 if ( is_array( $wgCategoryTreeMaxDepth ) ) {
781 $max = isset( $wgCategoryTreeMaxDepth[$mode] ) ? $wgCategoryTreeMaxDepth[$mode] : 1;
782 } elseif ( is_numeric( $wgCategoryTreeMaxDepth ) ) {
783 $max = $wgCategoryTreeMaxDepth;
784 } else {
785 wfDebug( 'CategoryTree::capDepth: $wgCategoryTreeMaxDepth is invalid.' );
786 $max = 1;
787 }
788
789 return min( $depth, $max );
790 }
791}
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Core functions for the CategoryTree extension, an AJAX based gadget to display the category structure...
renderNodeInfo(Title $title, Category $cat=null, $children=0)
Returns a string with a HTML represenation of the given page.
getTag(Parser $parser=null, $category, $hideroot=false, array $attr=[], $depth=1, $allowMissing=false)
Custom tag implementation.
LinkRenderer $linkRenderer
__construct(array $options)
PhanTypeInvalidDimOffset.
static setHeaders(OutputPage $outputPage)
Add ResourceLoader modules to the OutputPage object.
renderChildren(Title $title, $depth=1)
Returns a string with an HTML representation of the children of the given category.
static decodeBoolean( $value)
Helper function to convert a string to a boolean value.
renderParents(Title $title)
Returns a string with an HTML representation of the parents of the given category.
static decodeHidePrefix( $value)
static decodeMode( $mode)
getOptionsAsCacheKey( $depth=null)
getOption( $name)
static decodeNamespaces( $nn)
renderNode(Title $title, $children=0)
Returns a string with a HTML represenation of the given page.
static createCountString(IContextSource $context, Category $cat=null, $countMode)
Create a string which format the page, subcat and file counts of a category PhanParamReqAfterOpt $cat...
static makeTitle( $title)
Creates a Title object from a user provided (and thus unsafe) string.
static capDepth( $mode, $depth)
Internal function to cap depth PhanPluginDuplicateConditionalNullCoalescing until PHP7 is required.
static encodeOptions(array $options, $enc)
getOptionsAsJsStructure( $depth=null)
Category objects are immutable, strictly speaking.
Definition Category.php:29
Class that generates HTML links for pages.
MediaWikiServices is the service locator for the application scope of MediaWiki.
This is one of the Core classes and should be read at least once by any new developers.
addModuleStyles( $modules)
Load the styles of one or more ResourceLoader modules on this page.
addModules( $modules)
Load one or more ResourceLoader modules on this page.
PHP Parser - Processes wiki markup (which uses a more user-friendly syntax, such as "[[link]]" for ma...
Definition Parser.php:74
Represents a title within MediaWiki.
Definition Title.php:42
const NS_FILE
Definition Defines.php:75
const NS_MAIN
Definition Defines.php:69
const NS_CATEGORY
Definition Defines.php:83
Interface for objects which can provide a MediaWiki context on request.
$context
Definition load.php:45
const DB_REPLICA
Definition defines.php:25