ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
Title.php
Go to the documentation of this file.
1 <?php
8 if (!class_exists('UtfNormal')) {
9  require_once('include/Unicode/UtfNormal.php');
10 }
11 
12 
13 // patched: alex, 30.4.2019: Added missing defines
14 
15 define('NS_MAIN', "nsmain");
16 define('NS_SPECIAL', "nsspecial");
17 
18 define('GAID_FOR_UPDATE', 1);
19 
20 # Title::newFromTitle maintains a cache to avoid
21 # expensive re-normalization of commonly used titles.
22 # On a batch operation this can become a memory leak
23 # if not bounded. After hitting this many titles,
24 # reset the cache.
25 define('MW_TITLECACHE_MAX', 1000);
26 
27 # Constants for pr_cascade bitfield
28 define('CASCADE', 1);
29 
36 class Title
37 {
41  private static $titleCache = array();
42  private static $interwikiCache = array();
43 
44 
54  public $mTextform; # Text form (spaces not underscores) of the main part
55  public $mUrlform; # URL-encoded form of the main part
56  public $mDbkeyform; # Main part with underscores
57  public $mNamespace; # Namespace index, i.e. one of the NS_xxxx constants
58  public $mInterwiki; # Interwiki prefix (or null string)
59  public $mFragment; # Title fragment (i.e. the bit after the #)
60  public $mArticleID; # Article ID, fetched from the link cache on demand
61  public $mLatestID; # ID of most recent revision
62  public $mRestrictions; # Array of groups allowed to edit this article
63  public $mCascadeRestriction; # Cascade restrictions on this page to included templates and images?
64  public $mRestrictionsExpiry; # When do the restrictions on this page expire?
65  public $mHasCascadingRestrictions; # Are cascading restrictions in effect on this page?
66  public $mCascadeRestrictionSources;# Where are the cascading restrictions coming from on this page?
67  public $mRestrictionsLoaded; # Boolean for initialisation on demand
68  public $mPrefixedText; # Text form including namespace/interwiki, initialised on demand
69  public $mDefaultNamespace; # Namespace index when there is no namespace
70  # Zero except in {{transclusion}} tags
71  public $mWatched; # Is $wgUser watching this page? NULL if unfilled, accessed through userIsWatching()
79  /* private */ public function __construct()
80  {
81  $this->mInterwiki = $this->mUrlform =
82  $this->mTextform = $this->mDbkeyform = '';
83  $this->mArticleID = -1;
84  $this->mNamespace = NS_MAIN;
85  $this->mRestrictionsLoaded = false;
86  $this->mRestrictions = array();
87  # Dont change the following, NS_MAIN is hardcoded in several place
88  # See bug #696
89  $this->mDefaultNamespace = NS_MAIN;
90  $this->mWatched = null;
91  $this->mLatestID = false;
92  $this->mOldRestrictions = false;
93  }
94 
102  public static function newFromDBkey($key)
103  {
104  $t = new Title();
105  $t->mDbkeyform = $key;
106  if ($t->secureAndSplit()) {
107  return $t;
108  } else {
109  return null;
110  }
111  }
112 
124  public static function newFromText($text, $defaultNamespace = NS_MAIN)
125  {
126  if (is_object($text)) {
127  throw new MWException('Title::newFromText given an object');
128  }
129 
138  if ($defaultNamespace == NS_MAIN && isset(Title::$titleCache[$text])) {
139  return Title::$titleCache[$text];
140  }
141 
145  $filteredText = Sanitizer::decodeCharReferences($text);
146 
147  $t = new Title();
148  $t->mDbkeyform = str_replace(' ', '_', $filteredText);
149  $t->mDefaultNamespace = $defaultNamespace;
150 
151  static $cachedcount = 0 ;
152  if ($t->secureAndSplit()) {
153  if ($defaultNamespace == NS_MAIN) {
154  if ($cachedcount >= MW_TITLECACHE_MAX) {
155  # Avoid memory leaks on mass operations...
156  Title::$titleCache = array();
157  $cachedcount = 0;
158  }
159  $cachedcount++;
161  }
162  return $t;
163  } else {
164  $ret = null;
165  return $ret;
166  }
167  }
168 
175  public static function newFromURL($url)
176  {
177  global $wgLegalTitleChars;
178  $t = new Title();
179 
180  # For compatibility with old buggy URLs. "+" is usually not valid in titles,
181  # but some URLs used it as a space replacement and they still come
182  # from some external search tools.
183  if (strpos($wgLegalTitleChars, '+') === false) {
184  $url = str_replace('+', ' ', $url);
185  }
186 
187  $t->mDbkeyform = str_replace(' ', '_', $url);
188  if ($t->secureAndSplit()) {
189  return $t;
190  } else {
191  return null;
192  }
193  }
194 
204  public static function newFromID($id)
205  {
206  $fname = 'Title::newFromID';
207  $dbr = wfGetDB(DB_SLAVE);
208  $row = $dbr->selectRow(
209  'page',
210  array( 'page_namespace', 'page_title' ),
211  array( 'page_id' => $id ),
212  $fname
213  );
214  if ($row !== false) {
215  $title = Title::makeTitle($row->page_namespace, $row->page_title);
216  } else {
217  $title = null;
218  }
219  return $title;
220  }
221 
225  public static function newFromIDs($ids)
226  {
227  $dbr = wfGetDB(DB_SLAVE);
228  $res = $dbr->select(
229  'page',
230  array( 'page_namespace', 'page_title' ),
231  'page_id IN (' . $dbr->makeList($ids) . ')',
232  __METHOD__
233  );
234 
235  $titles = array();
236  while ($row = $dbr->fetchObject($res)) {
237  $titles[] = Title::makeTitle($row->page_namespace, $row->page_title);
238  }
239  return $titles;
240  }
241 
253  public static function &makeTitle($ns, $title)
254  {
255  $t = new Title();
256  $t->mInterwiki = '';
257  $t->mFragment = '';
258  $t->mNamespace = intval($ns);
259  $t->mDbkeyform = str_replace(' ', '_', $title);
260  $t->mArticleID = ($ns >= 0) ? -1 : 0;
261  $t->mUrlform = wfUrlencode($t->mDbkeyform);
262  $t->mTextform = str_replace('_', ' ', $title);
263  return $t;
264  }
265 
275  public static function makeTitleSafe($ns, $title)
276  {
277  $t = new Title();
278  $t->mDbkeyform = Title::makeName($ns, $title);
279  if ($t->secureAndSplit()) {
280  return $t;
281  } else {
282  return null;
283  }
284  }
285 
290  public static function newMainPage()
291  {
292  return Title::newFromText(wfMsgForContent('mainpage'));
293  }
294 
301  public static function newFromRedirect($text)
302  {
303  $mwRedir = MagicWord::get('redirect');
304  $rt = null;
305  if ($mwRedir->matchStart($text)) {
306  $m = array();
307  if (preg_match('/\[{2}(.*?)(?:\||\]{2})/', $text, $m)) {
308  # categories are escaped using : for example one can enter:
309  # #REDIRECT [[:Category:Music]]. Need to remove it.
310  if (substr($m[1], 0, 1) == ':') {
311  # We don't want to keep the ':'
312  $m[1] = substr($m[1], 1);
313  }
314 
315  $rt = Title::newFromText($m[1]);
316  # Disallow redirects to Special:Userlogout
317  if (!is_null($rt) && $rt->isSpecial('Userlogout')) {
318  $rt = null;
319  }
320  }
321  }
322  return $rt;
323  }
324 
325  #----------------------------------------------------------------------------
326  # Static functions
327  #----------------------------------------------------------------------------
328 
337  public function nameOf($id)
338  {
339  $fname = 'Title::nameOf';
340  $dbr = wfGetDB(DB_SLAVE);
341 
342  $s = $dbr->selectRow('page', array( 'page_namespace','page_title' ), array( 'page_id' => $id ), $fname);
343  if ($s === false) {
344  return null;
345  }
346 
347  $n = Title::makeName($s->page_namespace, $s->page_title);
348  return $n;
349  }
350 
355  public static function legalChars()
356  {
357  global $wgLegalTitleChars;
358 
359  $wgLegalTitleChars = " %!\"$&'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF+";
360 
361  return $wgLegalTitleChars;
362  }
363 
373  public static function indexTitle($ns, $title)
374  {
375  global $wgContLang;
376 
377  $lc = SearchEngine::legalSearchChars() . '&#;';
378  $t = $wgContLang->stripForSearch($title);
379  $t = preg_replace("/[^{$lc}]+/", ' ', $t);
380  $t = $wgContLang->lc($t);
381 
382  # Handle 's, s'
383  $t = preg_replace("/([{$lc}]+)'s( |$)/", "\\1 \\1's ", $t);
384  $t = preg_replace("/([{$lc}]+)s'( |$)/", "\\1s ", $t);
385 
386  $t = preg_replace("/\\s+/", ' ', $t);
387 
388  if ($ns == NS_IMAGE) {
389  $t = preg_replace("/ (png|gif|jpg|jpeg|ogg)$/", "", $t);
390  }
391  return trim($t);
392  }
393 
394  /*
395  * Make a prefixed DB key from a DB key and a namespace index
396  * @param int $ns numerical representation of the namespace
397  * @param string $title the DB key form the title
398  * @return string the prefixed form of the title
399  */
400  public static function makeName($ns, $title)
401  {
402  global $wgContLang;
403 
404  $n = $wgContLang->getNsText($ns);
405  return $n == '' ? $title : "$n:$title";
406  }
407 
415  public function getInterwikiLink($key)
416  {
417  global $wgMemc, $wgInterwikiExpiry;
418  global $wgInterwikiCache, $wgContLang;
419 
420  return ""; // changed. alex
421 
422  $fname = 'Title::getInterwikiLink';
423 
424  $key = $wgContLang->lc($key);
425 
426  $k = wfMemcKey('interwiki', $key);
427  if (array_key_exists($k, Title::$interwikiCache)) {
428  return Title::$interwikiCache[$k]->iw_url;
429  }
430 
431  if ($wgInterwikiCache) {
433  }
434 
435  $s = $wgMemc->get($k);
436  # Ignore old keys with no iw_local
437  if ($s && isset($s->iw_local) && isset($s->iw_trans)) {
439  return $s->iw_url;
440  }
441 
442  $dbr = wfGetDB(DB_SLAVE);
443  $res = $dbr->select(
444  'interwiki',
445  array( 'iw_url', 'iw_local', 'iw_trans' ),
446  array( 'iw_prefix' => $key ),
447  $fname
448  );
449  if (!$res) {
450  return '';
451  }
452 
453  $s = $dbr->fetchObject($res);
454  if (!$s) {
455  # Cache non-existence: create a blank object and save it to memcached
456  $s = (object) false;
457  $s->iw_url = '';
458  $s->iw_local = 0;
459  $s->iw_trans = 0;
460  }
461  $wgMemc->set($k, $s, $wgInterwikiExpiry);
463 
464  return $s->iw_url;
465  }
466 
474  public static function getInterwikiCached($key)
475  {
476  global $wgInterwikiCache, $wgInterwikiScopes, $wgInterwikiFallbackSite;
477  static $db, $site;
478 
479  if (!$db) {
480  $db = dba_open($wgInterwikiCache, 'r', 'cdb');
481  }
482  /* Resolve site name */
483  if ($wgInterwikiScopes >= 3 and !$site) {
484  $site = dba_fetch('__sites:' . wfWikiID(), $db);
485  if ($site == "") {
486  $site = $wgInterwikiFallbackSite;
487  }
488  }
489  $value = dba_fetch(wfMemcKey($key), $db);
490  if ($value == '' and $wgInterwikiScopes >= 3) {
491  /* try site-level */
492  $value = dba_fetch("_{$site}:{$key}", $db);
493  }
494  if ($value == '' and $wgInterwikiScopes >= 2) {
495  /* try globals */
496  $value = dba_fetch("__global:{$key}", $db);
497  }
498  if ($value == 'undef') {
499  $value = '';
500  }
501  $s = (object) false;
502  $s->iw_url = '';
503  $s->iw_local = 0;
504  $s->iw_trans = 0;
505  if ($value != '') {
506  list($local, $url) = explode(' ', $value, 2);
507  $s->iw_url = $url;
508  $s->iw_local = (int) $local;
509  }
510  Title::$interwikiCache[wfMemcKey('interwiki', $key)] = $s;
511  return $s->iw_url;
512  }
520  public function isLocal()
521  {
522  if ($this->mInterwiki != '') {
523  # Make sure key is loaded into cache
524  $this->getInterwikiLink($this->mInterwiki);
525  $k = wfMemcKey('interwiki', $this->mInterwiki);
526  return (bool) (Title::$interwikiCache[$k]->iw_local);
527  } else {
528  return true;
529  }
530  }
531 
538  public function isTrans()
539  {
540  if ($this->mInterwiki == '') {
541  return false;
542  }
543  # Make sure key is loaded into cache
544  $this->getInterwikiLink($this->mInterwiki);
545  $k = wfMemcKey('interwiki', $this->mInterwiki);
546  return (bool) (Title::$interwikiCache[$k]->iw_trans);
547  }
548 
552  public static function escapeFragmentForURL($fragment)
553  {
554  $fragment = str_replace(' ', '_', $fragment);
555  $fragment = urlencode(Sanitizer::decodeCharReferences($fragment));
556  $replaceArray = array(
557  '%3A' => ':',
558  '%' => '.'
559  );
560  return strtr($fragment, $replaceArray);
561  }
562 
563  #----------------------------------------------------------------------------
564  # Other stuff
565  #----------------------------------------------------------------------------
566 
572  public function getText()
573  {
574  return $this->mTextform;
575  }
580  public function getPartialURL()
581  {
582  return $this->mUrlform;
583  }
588  public function getDBkey()
589  {
590  return $this->mDbkeyform;
591  }
596  public function getNamespace()
597  {
598  return $this->mNamespace;
599  }
604  public function getNsText()
605  {
606  global $wgContLang, $wgCanonicalNamespaceNames;
607 
608  if ('' != $this->mInterwiki) {
609  // This probably shouldn't even happen. ohh man, oh yuck.
610  // But for interwiki transclusion it sometimes does.
611  // Shit. Shit shit shit.
612  //
613  // Use the canonical namespaces if possible to try to
614  // resolve a foreign namespace.
615  if (isset($wgCanonicalNamespaceNames[$this->mNamespace])) {
616  return $wgCanonicalNamespaceNames[$this->mNamespace];
617  }
618  }
619  return $wgContLang->getNsText($this->mNamespace);
620  }
625  /* public function getSubjectNsText() {
626  global $wgContLang;
627  return $wgContLang->getNsText( Namespace::getSubject( $this->mNamespace ) );
628  }*/
629 
634  /* public function getTalkNsText() {
635  global $wgContLang;
636  return( $wgContLang->getNsText( Namespace::getTalk( $this->mNamespace ) ) );
637  }*/
638 
643  /* public function canTalk() {
644  return( Namespace::canTalk( $this->mNamespace ) );
645  }*/
646 
651  public function getInterwiki()
652  {
653  return $this->mInterwiki;
654  }
659  public function getFragment()
660  {
661  return $this->mFragment;
662  }
667  public function getFragmentForURL()
668  {
669  if ($this->mFragment == '') {
670  return '';
671  } else {
672  return '#' . Title::escapeFragmentForURL($this->mFragment);
673  }
674  }
679  public function getDefaultNamespace()
680  {
682  }
683 
689  public function getIndexTitle()
690  {
691  return Title::indexTitle($this->mNamespace, $this->mTextform);
692  }
693 
699  public function getPrefixedDBkey()
700  {
701  $s = $this->prefix($this->mDbkeyform);
702  $s = str_replace(' ', '_', $s);
703  return $s;
704  }
705 
711  public function getPrefixedText()
712  {
713  if (empty($this->mPrefixedText)) { // FIXME: bad usage of empty() ?
714  $s = $this->prefix($this->mTextform);
715  $s = str_replace('_', ' ', $s);
716  $this->mPrefixedText = $s;
717  }
718  return $this->mPrefixedText;
719  }
720 
727  public function getFullText()
728  {
729  $text = $this->getPrefixedText();
730  if ('' != $this->mFragment) {
731  $text .= '#' . $this->mFragment;
732  }
733  return $text;
734  }
735 
740  public function getBaseText()
741  {
742  global $wgNamespacesWithSubpages;
743  if (isset($wgNamespacesWithSubpages[ $this->mNamespace ]) && $wgNamespacesWithSubpages[ $this->mNamespace ]) {
744  $parts = explode('/', $this->getText());
745  # Don't discard the real title if there's no subpage involved
746  if (count($parts) > 1) {
747  unset($parts[ count($parts) - 1 ]);
748  }
749  return implode('/', $parts);
750  } else {
751  return $this->getText();
752  }
753  }
754 
759  public function getSubpageText()
760  {
761  global $wgNamespacesWithSubpages;
762  if (isset($wgNamespacesWithSubpages[ $this->mNamespace ]) && $wgNamespacesWithSubpages[ $this->mNamespace ]) {
763  $parts = explode('/', $this->mTextform);
764  return($parts[ count($parts) - 1 ]);
765  } else {
766  return($this->mTextform);
767  }
768  }
769 
774  public function getSubpageUrlForm()
775  {
776  $text = $this->getSubpageText();
777  $text = wfUrlencode(str_replace(' ', '_', $text));
778  $text = str_replace('%28', '(', str_replace('%29', ')', $text)); # Clean up the URL; per below, this might not be safe
779  return($text);
780  }
781 
786  public function getPrefixedURL()
787  {
788  $s = $this->prefix($this->mDbkeyform);
789  $s = str_replace(' ', '_', $s);
790 
791  $s = wfUrlencode($s) ;
792 
793  # Cleaning up URL to make it look nice -- is this safe?
794  $s = str_replace('%28', '(', $s);
795  $s = str_replace('%29', ')', $s);
796 
797  return $s;
798  }
799 
809  public function getFullURL($query = '', $variant = false)
810  {
811  global $wgContLang, $wgServer, $wgRequest;
812 
813  if ('' == $this->mInterwiki) {
814  $url = $this->getLocalUrl($query, $variant);
815 
816  // Ugly quick hack to avoid duplicate prefixes (bug 4571 etc)
817  // Correct fix would be to move the prepending elsewhere.
818  if ($wgRequest->getVal('action') != 'render') {
819  $url = $wgServer . $url;
820  }
821  } else {
822  $baseUrl = $this->getInterwikiLink($this->mInterwiki);
823 
824  $namespace = wfUrlencode($this->getNsText());
825  if ('' != $namespace) {
826  # Can this actually happen? Interwikis shouldn't be parsed.
827  # Yes! It can in interwiki transclusion. But... it probably shouldn't.
828  $namespace .= ':';
829  }
830  $url = str_replace('$1', $namespace . $this->mUrlform, $baseUrl);
831  $url = wfAppendQuery($url, $query);
832  }
833 
834  # Finally, add the fragment.
835  $url .= $this->getFragmentForURL();
836 
837  wfRunHooks('GetFullURL', array( &$this, &$url, $query ));
838  return $url;
839  }
840 
849  public function getLocalURL($query = '', $variant = false)
850  {
851  global $wgArticlePath, $wgScript, $wgServer, $wgRequest;
852  global $wgVariantArticlePath, $wgContLang, $wgUser;
853 
854  // internal links should point to same variant as current page (only anonymous users)
855  if ($variant == false && $wgContLang->hasVariants() && !$wgUser->isLoggedIn()) {
856  $pref = $wgContLang->getPreferredVariant(false);
857  if ($pref != $wgContLang->getCode()) {
858  $variant = $pref;
859  }
860  }
861 
862  if ($this->isExternal()) {
863  $url = $this->getFullURL();
864  if ($query) {
865  // This is currently only used for edit section links in the
866  // context of interwiki transclusion. In theory we should
867  // append the query to the end of any existing query string,
868  // but interwiki transclusion is already broken in that case.
869  $url .= "?$query";
870  }
871  } else {
872  $dbkey = wfUrlencode($this->getPrefixedDBkey());
873  if ($query == '') {
874  if ($variant != false && $wgContLang->hasVariants()) {
875  if ($wgVariantArticlePath == false) {
876  $variantArticlePath = "$wgScript?title=$1&variant=$2"; // default
877  } else {
878  $variantArticlePath = $wgVariantArticlePath;
879  }
880  $url = str_replace('$2', urlencode($variant), $variantArticlePath);
881  $url = str_replace('$1', $dbkey, $url);
882  } else {
883  $url = str_replace('$1', $dbkey, $wgArticlePath);
884  }
885  } else {
886  global $wgActionPaths;
887  $url = false;
888  $matches = array();
889  if (!empty($wgActionPaths) &&
890  preg_match('/^(.*&|)action=([^&]*)(&(.*)|)$/', $query, $matches)) {
891  $action = urldecode($matches[2]);
892  if (isset($wgActionPaths[$action])) {
893  $query = $matches[1];
894  if (isset($matches[4])) {
895  $query .= $matches[4];
896  }
897  $url = str_replace('$1', $dbkey, $wgActionPaths[$action]);
898  if ($query != '') {
899  $url .= '?' . $query;
900  }
901  }
902  }
903  if ($url === false) {
904  if ($query == '-') {
905  $query = '';
906  }
907  $url = "{$wgScript}?title={$dbkey}&{$query}";
908  }
909  }
910 
911  // FIXME: this causes breakage in various places when we
912  // actually expected a local URL and end up with dupe prefixes.
913  if ($wgRequest->getVal('action') == 'render') {
914  $url = $wgServer . $url;
915  }
916  }
917  wfRunHooks('GetLocalURL', array( &$this, &$url, $query ));
918  return $url;
919  }
920 
927  public function escapeLocalURL($query = '')
928  {
929  return htmlspecialchars($this->getLocalURL($query));
930  }
931 
939  public function escapeFullURL($query = '')
940  {
941  return htmlspecialchars($this->getFullURL($query));
942  }
943 
953  public function getInternalURL($query = '', $variant = false)
954  {
955  global $wgInternalServer;
956  $url = $wgInternalServer . $this->getLocalURL($query, $variant);
957  wfRunHooks('GetInternalURL', array( &$this, &$url, $query ));
958  return $url;
959  }
960 
966  public function getEditURL()
967  {
968  if ('' != $this->mInterwiki) {
969  return '';
970  }
971  $s = $this->getLocalURL('action=edit');
972 
973  return $s;
974  }
975 
981  public function getEscapedText()
982  {
983  return htmlspecialchars($this->getPrefixedText());
984  }
985 
990  public function isExternal()
991  {
992  return ('' != $this->mInterwiki);
993  }
994 
1001  public function isSemiProtected($action = 'edit')
1002  {
1003  if ($this->exists()) {
1004  $restrictions = $this->getRestrictions($action);
1005  if (count($restrictions) > 0) {
1006  foreach ($restrictions as $restriction) {
1007  if (strtolower($restriction) != 'autoconfirmed') {
1008  return false;
1009  }
1010  }
1011  } else {
1012  # Not protected
1013  return false;
1014  }
1015  return true;
1016  } else {
1017  # If it doesn't exist, it can't be protected
1018  return false;
1019  }
1020  }
1021 
1028  public function isProtected($action = '')
1029  {
1030  global $wgRestrictionLevels;
1031 
1032  # Special pages have inherent protection
1033  if ($this->getNamespace() == NS_SPECIAL) {
1034  return true;
1035  }
1036 
1037  # Check regular protection levels
1038  if ($action == 'edit' || $action == '') {
1039  $r = $this->getRestrictions('edit');
1040  foreach ($wgRestrictionLevels as $level) {
1041  if (in_array($level, $r) && $level != '') {
1042  return(true);
1043  }
1044  }
1045  }
1046 
1047  if ($action == 'move' || $action == '') {
1048  $r = $this->getRestrictions('move');
1049  foreach ($wgRestrictionLevels as $level) {
1050  if (in_array($level, $r) && $level != '') {
1051  return(true);
1052  }
1053  }
1054  }
1055 
1056  return false;
1057  }
1058 
1063  public function userIsWatching()
1064  {
1065  global $wgUser;
1066 
1067  if (is_null($this->mWatched)) {
1068  if (NS_SPECIAL == $this->mNamespace || !$wgUser->isLoggedIn()) {
1069  $this->mWatched = false;
1070  } else {
1071  $this->mWatched = $wgUser->isWatched($this);
1072  }
1073  }
1074  return $this->mWatched;
1075  }
1076 
1089  public function quickUserCan($action)
1090  {
1091  return $this->userCan($action, false);
1092  }
1093 
1100  public function userCan($action, $doExpensiveQueries = true)
1101  {
1102  $fname = 'Title::userCan';
1103  wfProfileIn($fname);
1104 
1105  global $wgUser, $wgNamespaceProtection;
1106 
1107  $result = null;
1108  wfRunHooks('userCan', array( &$this, &$wgUser, $action, &$result ));
1109  if ($result !== null) {
1110  wfProfileOut($fname);
1111  return $result;
1112  }
1113 
1114  if (NS_SPECIAL == $this->mNamespace) {
1115  wfProfileOut($fname);
1116  return false;
1117  }
1118 
1119  if (array_key_exists($this->mNamespace, $wgNamespaceProtection)) {
1120  $nsProt = $wgNamespaceProtection[ $this->mNamespace ];
1121  if (!is_array($nsProt)) {
1122  $nsProt = array($nsProt);
1123  }
1124  foreach ($nsProt as $right) {
1125  if ('' != $right && !$wgUser->isAllowed($right)) {
1126  wfProfileOut($fname);
1127  return false;
1128  }
1129  }
1130  }
1131 
1132  if ($this->mDbkeyform == '_') {
1133  # FIXME: Is this necessary? Shouldn't be allowed anyway...
1134  wfProfileOut($fname);
1135  return false;
1136  }
1137 
1138  # protect css/js subpages of user pages
1139  # XXX: this might be better using restrictions
1140  # XXX: Find a way to work around the php bug that prevents using $this->userCanEditCssJsSubpage() from working
1141  if ($this->isCssJsSubpage()
1142  && !$wgUser->isAllowed('editinterface')
1143  && !preg_match('/^' . preg_quote($wgUser->getName(), '/') . '\//', $this->mTextform)) {
1144  wfProfileOut($fname);
1145  return false;
1146  }
1147 
1148  if ($doExpensiveQueries && !$this->isCssJsSubpage()) {
1149  # We /could/ use the protection level on the source page, but it's fairly ugly
1150  # as we have to establish a precedence hierarchy for pages included by multiple
1151  # cascade-protected pages. So just restrict it to people with 'protect' permission,
1152  # as they could remove the protection anyway.
1153  list($cascadingSources, $restrictions) = $this->getCascadeProtectionSources();
1154  # Cascading protection depends on more than this page...
1155  # Several cascading protected pages may include this page...
1156  # Check each cascading level
1157  # This is only for protection restrictions, not for all actions
1158  if ($cascadingSources > 0 && isset($restrictions[$action])) {
1159  foreach ($restrictions[$action] as $right) {
1160  $right = ($right == 'sysop') ? 'protect' : $right;
1161  if ('' != $right && !$wgUser->isAllowed($right)) {
1162  wfProfileOut($fname);
1163  return false;
1164  }
1165  }
1166  }
1167  }
1168 
1169  foreach ($this->getRestrictions($action) as $right) {
1170  // Backwards compatibility, rewrite sysop -> protect
1171  if ($right == 'sysop') {
1172  $right = 'protect';
1173  }
1174  if ('' != $right && !$wgUser->isAllowed($right)) {
1175  wfProfileOut($fname);
1176  return false;
1177  }
1178  }
1179 
1180  if ($action == 'move' &&
1181  !($this->isMovable() && $wgUser->isAllowed('move'))) {
1182  wfProfileOut($fname);
1183  return false;
1184  }
1185 
1186  if ($action == 'create') {
1187  if (($this->isTalkPage() && !$wgUser->isAllowed('createtalk')) ||
1188  (!$this->isTalkPage() && !$wgUser->isAllowed('createpage'))) {
1189  wfProfileOut($fname);
1190  return false;
1191  }
1192  }
1193 
1194  wfProfileOut($fname);
1195  return true;
1196  }
1197 
1203  public function userCanEdit($doExpensiveQueries = true)
1204  {
1205  return $this->userCan('edit', $doExpensiveQueries);
1206  }
1207 
1213  public function userCanCreate($doExpensiveQueries = true)
1214  {
1215  return $this->userCan('create', $doExpensiveQueries);
1216  }
1217 
1223  public function userCanMove($doExpensiveQueries = true)
1224  {
1225  return $this->userCan('move', $doExpensiveQueries);
1226  }
1227 
1234  /* public function isMovable() {
1235  return Namespace::isMovable( $this->getNamespace() )
1236  && $this->getInterwiki() == '';
1237  }*/
1238 
1244  public function userCanRead()
1245  {
1246  global $wgUser;
1247 
1248  $result = null;
1249  wfRunHooks('userCan', array( &$this, &$wgUser, 'read', &$result ));
1250  if ($result !== null) {
1251  return $result;
1252  }
1253 
1254  if ($wgUser->isAllowed('read')) {
1255  return true;
1256  } else {
1257  global $wgWhitelistRead;
1258 
1263  if ($this->isSpecial('Userlogin') || $this->isSpecial('Resetpass')) {
1264  return true;
1265  }
1266 
1268  $name = $this->getPrefixedText();
1269  if ($wgWhitelistRead && in_array($name, $wgWhitelistRead)) {
1270  return true;
1271  }
1272 
1273  # Compatibility with old settings
1274  if ($wgWhitelistRead && $this->getNamespace() == NS_MAIN) {
1275  if (in_array(':' . $name, $wgWhitelistRead)) {
1276  return true;
1277  }
1278  }
1279  }
1280  return false;
1281  }
1282 
1287  /* public function isTalkPage() {
1288  return Namespace::isTalk( $this->getNamespace() );
1289  }*/
1290 
1295  public function isSubpage()
1296  {
1297  global $wgNamespacesWithSubpages;
1298 
1299  if (isset($wgNamespacesWithSubpages[ $this->mNamespace ])) {
1300  return (strpos($this->getText(), '/') !== false && $wgNamespacesWithSubpages[ $this->mNamespace ] == true);
1301  } else {
1302  return false;
1303  }
1304  }
1305 
1310  public function isCssJsSubpage()
1311  {
1312  return (NS_USER == $this->mNamespace and preg_match("/\\/.*\\.(?:css|js)$/", $this->mTextform));
1313  }
1318  public function isValidCssJsSubpage()
1319  {
1320  if ($this->isCssJsSubpage()) {
1321  $skinNames = Skin::getSkinNames();
1322  return array_key_exists($this->getSkinFromCssJsSubpage(), $skinNames);
1323  } else {
1324  return false;
1325  }
1326  }
1330  public function getSkinFromCssJsSubpage()
1331  {
1332  $subpage = explode('/', $this->mTextform);
1333  $subpage = $subpage[ count($subpage) - 1 ];
1334  return(str_replace(array( '.css', '.js' ), array( '', '' ), $subpage));
1335  }
1340  public function isCssSubpage()
1341  {
1342  return (NS_USER == $this->mNamespace and preg_match("/\\/.*\\.css$/", $this->mTextform));
1343  }
1348  public function isJsSubpage()
1349  {
1350  return (NS_USER == $this->mNamespace and preg_match("/\\/.*\\.js$/", $this->mTextform));
1351  }
1359  public function userCanEditCssJsSubpage()
1360  {
1361  global $wgUser;
1362  return ($wgUser->isAllowed('editinterface') or preg_match('/^' . preg_quote($wgUser->getName(), '/') . '\//', $this->mTextform));
1363  }
1364 
1370  public function isCascadeProtected()
1371  {
1372  list($sources, $restrictions) = $this->getCascadeProtectionSources(false);
1373  return ($sources > 0);
1374  }
1375 
1384  public function getCascadeProtectionSources($get_pages = true)
1385  {
1386  global $wgEnableCascadingProtection, $wgRestrictionTypes;
1387 
1388  # Define our dimension of restrictions types
1389  $pagerestrictions = array();
1390  foreach ($wgRestrictionTypes as $action) {
1391  $pagerestrictions[$action] = array();
1392  }
1393 
1394  if (!$wgEnableCascadingProtection) {
1395  return array( false, $pagerestrictions );
1396  }
1397 
1398  if (isset($this->mCascadeSources) && $get_pages) {
1399  return array( $this->mCascadeSources, $this->mCascadingRestrictions );
1400  } elseif (isset($this->mHasCascadingRestrictions) && !$get_pages) {
1401  return array( $this->mHasCascadingRestrictions, $pagerestrictions );
1402  }
1403 
1404  wfProfileIn(__METHOD__);
1405 
1406  $dbr = wfGetDb(DB_SLAVE);
1407 
1408  if ($this->getNamespace() == NS_IMAGE) {
1409  $tables = array('imagelinks', 'page_restrictions');
1410  $where_clauses = array(
1411  'il_to' => $this->getDBkey(),
1412  'il_from=pr_page',
1413  'pr_cascade' => 1 );
1414  } else {
1415  $tables = array('templatelinks', 'page_restrictions');
1416  $where_clauses = array(
1417  'tl_namespace' => $this->getNamespace(),
1418  'tl_title' => $this->getDBkey(),
1419  'tl_from=pr_page',
1420  'pr_cascade' => 1 );
1421  }
1422 
1423  if ($get_pages) {
1424  $cols = array('pr_page', 'page_namespace', 'page_title', 'pr_expiry', 'pr_type', 'pr_level' );
1425  $where_clauses[] = 'page_id=pr_page';
1426  $tables[] = 'page';
1427  } else {
1428  $cols = array( 'pr_expiry' );
1429  }
1430 
1431  $res = $dbr->select($tables, $cols, $where_clauses, __METHOD__);
1432 
1433  $sources = $get_pages ? array() : false;
1434  $now = wfTimestampNow();
1435  $purgeExpired = false;
1436 
1437  while ($row = $dbr->fetchObject($res)) {
1438  $expiry = Block::decodeExpiry($row->pr_expiry);
1439  if ($expiry > $now) {
1440  if ($get_pages) {
1441  $page_id = $row->pr_page;
1442  $page_ns = $row->page_namespace;
1443  $page_title = $row->page_title;
1444  $sources[$page_id] = Title::makeTitle($page_ns, $page_title);
1445  # Add groups needed for each restriction type if its not already there
1446  # Make sure this restriction type still exists
1447  if (isset($pagerestrictions[$row->pr_type]) && !in_array($row->pr_level, $pagerestrictions[$row->pr_type])) {
1448  $pagerestrictions[$row->pr_type][] = $row->pr_level;
1449  }
1450  } else {
1451  $sources = true;
1452  }
1453  } else {
1454  // Trigger lazy purge of expired restrictions from the db
1455  $purgeExpired = true;
1456  }
1457  }
1458  if ($purgeExpired) {
1460  }
1461 
1462  wfProfileOut(__METHOD__);
1463 
1464  if ($get_pages) {
1465  $this->mCascadeSources = $sources;
1466  $this->mCascadingRestrictions = $pagerestrictions;
1467  } else {
1468  $this->mHasCascadingRestrictions = $sources;
1469  }
1470 
1471  return array( $sources, $pagerestrictions );
1472  }
1473 
1474  public function areRestrictionsCascading()
1475  {
1476  if (!$this->mRestrictionsLoaded) {
1477  $this->loadRestrictions();
1478  }
1479 
1481  }
1482 
1487  private function loadRestrictionsFromRow($res, $oldFashionedRestrictions = null)
1488  {
1489  $dbr = wfGetDb(DB_SLAVE);
1490 
1491  $this->mRestrictions['edit'] = array();
1492  $this->mRestrictions['move'] = array();
1493 
1494  # Backwards-compatibility: also load the restrictions from the page record (old format).
1495 
1496  if ($oldFashionedRestrictions == null) {
1497  $oldFashionedRestrictions = $dbr->selectField('page', 'page_restrictions', array( 'page_id' => $this->getArticleId() ), __METHOD__);
1498  }
1499 
1500  if ($oldFashionedRestrictions != '') {
1501  foreach (explode(':', trim($oldFashionedRestrictions)) as $restrict) {
1502  $temp = explode('=', trim($restrict));
1503  if (count($temp) == 1) {
1504  // old old format should be treated as edit/move restriction
1505  $this->mRestrictions["edit"] = explode(',', trim($temp[0]));
1506  $this->mRestrictions["move"] = explode(',', trim($temp[0]));
1507  } else {
1508  $this->mRestrictions[$temp[0]] = explode(',', trim($temp[1]));
1509  }
1510  }
1511 
1512  $this->mOldRestrictions = true;
1513  $this->mCascadeRestriction = false;
1514  $this->mRestrictionsExpiry = Block::decodeExpiry('');
1515  }
1516 
1517  if ($dbr->numRows($res)) {
1518  # Current system - load second to make them override.
1519  $now = wfTimestampNow();
1520  $purgeExpired = false;
1521 
1522  while ($row = $dbr->fetchObject($res)) {
1523  # Cycle through all the restrictions.
1524 
1525  // This code should be refactored, now that it's being used more generally,
1526  // But I don't really see any harm in leaving it in Block for now -werdna
1527  $expiry = Block::decodeExpiry($row->pr_expiry);
1528 
1529  // Only apply the restrictions if they haven't expired!
1530  if (!$expiry || $expiry > $now) {
1531  $this->mRestrictionsExpiry = $expiry;
1532  $this->mRestrictions[$row->pr_type] = explode(',', trim($row->pr_level));
1533 
1534  $this->mCascadeRestriction |= $row->pr_cascade;
1535  } else {
1536  // Trigger a lazy purge of expired restrictions
1537  $purgeExpired = true;
1538  }
1539  }
1540 
1541  if ($purgeExpired) {
1543  }
1544  }
1545 
1546  $this->mRestrictionsLoaded = true;
1547  }
1548 
1549  public function loadRestrictions($oldFashionedRestrictions = null)
1550  {
1551  if (!$this->mRestrictionsLoaded) {
1552  $dbr = wfGetDB(DB_SLAVE);
1553 
1554  $res = $dbr->select(
1555  'page_restrictions',
1556  '*',
1557  array( 'pr_page' => $this->getArticleId() ),
1558  __METHOD__
1559  );
1560 
1561  $this->loadRestrictionsFromRow($res, $oldFashionedRestrictions);
1562  }
1563  }
1564 
1568  public static function purgeExpiredRestrictions()
1569  {
1570  $dbw = wfGetDB(DB_MASTER);
1571  $dbw->delete(
1572  'page_restrictions',
1573  array( 'pr_expiry < ' . $dbw->addQuotes($dbw->timestamp()) ),
1574  __METHOD__
1575  );
1576  }
1577 
1584  public function getRestrictions($action)
1585  {
1586  if ($this->exists()) {
1587  if (!$this->mRestrictionsLoaded) {
1588  $this->loadRestrictions();
1589  }
1590  return isset($this->mRestrictions[$action])
1591  ? $this->mRestrictions[$action]
1592  : array();
1593  } else {
1594  return array();
1595  }
1596  }
1597 
1602  public function isDeleted()
1603  {
1604  $fname = 'Title::isDeleted';
1605  if ($this->getNamespace() < 0) {
1606  $n = 0;
1607  } else {
1608  $dbr = wfGetDB(DB_SLAVE);
1609  $n = $dbr->selectField('archive', 'COUNT(*)', array( 'ar_namespace' => $this->getNamespace(),
1610  'ar_title' => $this->getDBkey() ), $fname);
1611  if ($this->getNamespace() == NS_IMAGE) {
1612  $n += $dbr->selectField(
1613  'filearchive',
1614  'COUNT(*)',
1615  array( 'fa_name' => $this->getDBkey() ),
1616  $fname
1617  );
1618  }
1619  }
1620  return (int) $n;
1621  }
1622 
1630  public function getArticleID($flags = 0)
1631  {
1632  $linkCache = &LinkCache::singleton();
1633  if ($flags & GAID_FOR_UPDATE) {
1634  $oldUpdate = $linkCache->forUpdate(true);
1635  $this->mArticleID = $linkCache->addLinkObj($this);
1636  $linkCache->forUpdate($oldUpdate);
1637  } else {
1638  if (-1 == $this->mArticleID) {
1639  $this->mArticleID = $linkCache->addLinkObj($this);
1640  }
1641  }
1642  return $this->mArticleID;
1643  }
1644 
1645  public function getLatestRevID()
1646  {
1647  if ($this->mLatestID !== false) {
1648  return $this->mLatestID;
1649  }
1650 
1651  $db = wfGetDB(DB_SLAVE);
1652  return $this->mLatestID = $db->selectField(
1653  'revision',
1654  "max(rev_id)",
1655  array('rev_page' => $this->getArticleID()),
1656  'Title::getLatestRevID'
1657  );
1658  }
1659 
1670  public function resetArticleID($newid)
1671  {
1672  $linkCache = &LinkCache::singleton();
1673  $linkCache->clearBadLink($this->getPrefixedDBkey());
1674 
1675  if (0 == $newid) {
1676  $this->mArticleID = -1;
1677  } else {
1678  $this->mArticleID = $newid;
1679  }
1680  $this->mRestrictionsLoaded = false;
1681  $this->mRestrictions = array();
1682  }
1683 
1688  public function invalidateCache()
1689  {
1690  global $wgUseFileCache;
1691 
1692  if (wfReadOnly()) {
1693  return;
1694  }
1695 
1696  $dbw = wfGetDB(DB_MASTER);
1697  $success = $dbw->update(
1698  'page',
1699  array( /* SET */
1700  'page_touched' => $dbw->timestamp()
1701  ),
1702  array( /* WHERE */
1703  'page_namespace' => $this->getNamespace() ,
1704  'page_title' => $this->getDBkey()
1705  ),
1706  'Title::invalidateCache'
1707  );
1708 
1709  if ($wgUseFileCache) {
1710  $cache = new HTMLFileCache($this);
1711  @unlink($cache->fileCacheName());
1712  }
1713 
1714  return $success;
1715  }
1716 
1725  /* private */ public function prefix($name)
1726  {
1727  $p = '';
1728  if ('' != $this->mInterwiki) {
1729  $p = $this->mInterwiki . ':';
1730  }
1731  if (0 != $this->mNamespace) {
1732  $p .= $this->getNsText() . ':';
1733  }
1734  return $p . $name;
1735  }
1736 
1747  private function secureAndSplit()
1748  {
1749  global $wgContLang, $wgLocalInterwiki, $wgCapitalLinks;
1750 
1751  # Initialisation
1752  static $rxTc = false;
1753  if (!$rxTc) {
1754  # % is needed as well
1755  $rxTc = '/[^' . Title::legalChars() . ']|%[0-9A-Fa-f]{2}/S';
1756  }
1757 
1758  $this->mInterwiki = $this->mFragment = '';
1759  $this->mNamespace = $this->mDefaultNamespace; # Usually NS_MAIN
1760 
1761  $dbkey = $this->mDbkeyform;
1762 
1763  # Strip Unicode bidi override characters.
1764  # Sometimes they slip into cut-n-pasted page titles, where the
1765  # override chars get included in list displays.
1766  $dbkey = str_replace("\xE2\x80\x8E", '', $dbkey); // 200E LEFT-TO-RIGHT MARK
1767  $dbkey = str_replace("\xE2\x80\x8F", '', $dbkey); // 200F RIGHT-TO-LEFT MARK
1768 
1769  # Clean up whitespace
1770  #
1771  $dbkey = preg_replace('/[ _]+/', '_', $dbkey);
1772  $dbkey = trim($dbkey, '_');
1773 
1774  if ('' == $dbkey) {
1775  return false;
1776  }
1777 
1778  if (false !== strpos($dbkey, UTF8_REPLACEMENT)) {
1779  # Contained illegal UTF-8 sequences or forbidden Unicode chars.
1780  return false;
1781  }
1782 
1783  $this->mDbkeyform = $dbkey;
1784 
1785  # Initial colon indicates main namespace rather than specified default
1786  # but should not create invalid {ns,title} pairs such as {0,Project:Foo}
1787  if (':' == $dbkey[0]) {
1788  $this->mNamespace = NS_MAIN;
1789  $dbkey = substr($dbkey, 1); # remove the colon but continue processing
1790  $dbkey = trim($dbkey, '_'); # remove any subsequent whitespace
1791  }
1792 
1793  # Namespace or interwiki prefix
1794  $firstPass = true;
1795  do {
1796  $m = array();
1797  if (preg_match("/^(.+?)_*:_*(.*)$/S", $dbkey, $m)) {
1798  $p = $m[1];
1799  if ($ns = $wgContLang->getNsIndex($p)) {
1800  # Ordinary namespace
1801  $dbkey = $m[2];
1802  $this->mNamespace = $ns;
1803  } elseif ($this->getInterwikiLink($p)) {
1804  if (!$firstPass) {
1805  # Can't make a local interwiki link to an interwiki link.
1806  # That's just crazy!
1807  return false;
1808  }
1809 
1810  # Interwiki link
1811  $dbkey = $m[2];
1812  $this->mInterwiki = $wgContLang->lc($p);
1813 
1814  # Redundant interwiki prefix to the local wiki
1815  if (0 == strcasecmp($this->mInterwiki, $wgLocalInterwiki)) {
1816  if ($dbkey == '') {
1817  # Can't have an empty self-link
1818  return false;
1819  }
1820  $this->mInterwiki = '';
1821  $firstPass = false;
1822  # Do another namespace split...
1823  continue;
1824  }
1825 
1826  # If there's an initial colon after the interwiki, that also
1827  # resets the default namespace
1828  if ($dbkey !== '' && $dbkey[0] == ':') {
1829  $this->mNamespace = NS_MAIN;
1830  $dbkey = substr($dbkey, 1);
1831  }
1832  }
1833  # If there's no recognized interwiki or namespace,
1834  # then let the colon expression be part of the title.
1835  }
1836  break;
1837  } while (true);
1838 
1839  # We already know that some pages won't be in the database!
1840  #
1841  if ('' != $this->mInterwiki || NS_SPECIAL == $this->mNamespace) {
1842  $this->mArticleID = 0;
1843  }
1844  $fragment = strstr($dbkey, '#');
1845  if (false !== $fragment) {
1846  $this->setFragment($fragment);
1847  $dbkey = substr($dbkey, 0, strlen($dbkey) - strlen($fragment));
1848  # remove whitespace again: prevents "Foo_bar_#"
1849  # becoming "Foo_bar_"
1850  $dbkey = preg_replace('/_*$/', '', $dbkey);
1851  }
1852 
1853  # Reject illegal characters.
1854  #
1855  if (preg_match($rxTc, $dbkey)) {
1856  return false;
1857  }
1858 
1864  if (strpos($dbkey, '.') !== false &&
1865  ($dbkey === '.' || $dbkey === '..' ||
1866  strpos($dbkey, './') === 0 ||
1867  strpos($dbkey, '../') === 0 ||
1868  strpos($dbkey, '/./') !== false ||
1869  strpos($dbkey, '/../') !== false)) {
1870  return false;
1871  }
1872 
1876  if (strpos($dbkey, '~~~') !== false) {
1877  return false;
1878  }
1879 
1887  if (($this->mNamespace != NS_SPECIAL && strlen($dbkey) > 255) ||
1888  strlen($dbkey) > 512) {
1889  return false;
1890  }
1891 
1900  if ($wgCapitalLinks && $this->mInterwiki == '') {
1901  $dbkey = $wgContLang->ucfirst($dbkey);
1902  }
1903 
1909  if ($dbkey == '' &&
1910  $this->mInterwiki == '' &&
1911  $this->mNamespace != NS_MAIN) {
1912  return false;
1913  }
1914 
1915  // Any remaining initial :s are illegal.
1916  if ($dbkey !== '' && ':' == $dbkey[0]) {
1917  return false;
1918  }
1919 
1920  # Fill fields
1921  $this->mDbkeyform = $dbkey;
1922  $this->mUrlform = ilWikiUtil::wfUrlencode($dbkey);
1923 
1924  $this->mTextform = str_replace('_', ' ', $dbkey);
1925 
1926  return true;
1927  }
1928 
1938  public function setFragment($fragment)
1939  {
1940  $this->mFragment = str_replace('_', ' ', substr($fragment, 1));
1941  }
1942 
1947  /* public function getTalkPage() {
1948  return Title::makeTitle( Namespace::getTalk( $this->getNamespace() ), $this->getDBkey() );
1949  }*/
1950 
1957  /* public function getSubjectPage() {
1958  return Title::makeTitle( Namespace::getSubject( $this->getNamespace() ), $this->getDBkey() );
1959  }*/
1960 
1971  public function getLinksTo($options = '', $table = 'pagelinks', $prefix = 'pl')
1972  {
1973  $linkCache = &LinkCache::singleton();
1974 
1975  if ($options) {
1976  $db = wfGetDB(DB_MASTER);
1977  } else {
1978  $db = wfGetDB(DB_SLAVE);
1979  }
1980 
1981  $res = $db->select(
1982  array( 'page', $table ),
1983  array( 'page_namespace', 'page_title', 'page_id' ),
1984  array(
1985  "{$prefix}_from=page_id",
1986  "{$prefix}_namespace" => $this->getNamespace(),
1987  "{$prefix}_title" => $this->getDbKey() ),
1988  'Title::getLinksTo',
1989  $options
1990  );
1991 
1992  $retVal = array();
1993  if ($db->numRows($res)) {
1994  while ($row = $db->fetchObject($res)) {
1995  if ($titleObj = Title::makeTitle($row->page_namespace, $row->page_title)) {
1996  $linkCache->addGoodLinkObj($row->page_id, $titleObj);
1997  $retVal[] = $titleObj;
1998  }
1999  }
2000  }
2001  $db->freeResult($res);
2002  return $retVal;
2003  }
2004 
2015  public function getTemplateLinksTo($options = '')
2016  {
2017  return $this->getLinksTo($options, 'templatelinks', 'tl');
2018  }
2019 
2026  public function getBrokenLinksFrom($options = '')
2027  {
2028  if ($options) {
2029  $db = wfGetDB(DB_MASTER);
2030  } else {
2031  $db = wfGetDB(DB_SLAVE);
2032  }
2033 
2034  $res = $db->safeQuery(
2035  "SELECT pl_namespace, pl_title
2036  FROM !
2037  LEFT JOIN !
2038  ON pl_namespace=page_namespace
2039  AND pl_title=page_title
2040  WHERE pl_from=?
2041  AND page_namespace IS NULL
2042  !",
2043  $db->tableName('pagelinks'),
2044  $db->tableName('page'),
2045  $this->getArticleId(),
2046  $options
2047  );
2048 
2049  $retVal = array();
2050  if ($db->numRows($res)) {
2051  while ($row = $db->fetchObject($res)) {
2052  $retVal[] = Title::makeTitle($row->pl_namespace, $row->pl_title);
2053  }
2054  }
2055  $db->freeResult($res);
2056  return $retVal;
2057  }
2058 
2059 
2066  public function getSquidURLs()
2067  {
2068  global $wgContLang;
2069 
2070  $urls = array(
2071  $this->getInternalURL(),
2072  $this->getInternalURL('action=history')
2073  );
2074 
2075  // purge variant urls as well
2076  if ($wgContLang->hasVariants()) {
2077  $variants = $wgContLang->getVariants();
2078  foreach ($variants as $vCode) {
2079  if ($vCode == $wgContLang->getCode()) {
2080  continue;
2081  } // we don't want default variant
2082  $urls[] = $this->getInternalURL('', $vCode);
2083  }
2084  }
2085 
2086  return $urls;
2087  }
2088 
2089  public function purgeSquid()
2090  {
2091  global $wgUseSquid;
2092  if ($wgUseSquid) {
2093  $urls = $this->getSquidURLs();
2094  $u = new SquidUpdate($urls);
2095  $u->doUpdate();
2096  }
2097  }
2098 
2103  public function moveNoAuth(&$nt)
2104  {
2105  return $this->moveTo($nt, false);
2106  }
2107 
2117  public function isValidMoveOperation(&$nt, $auth = true)
2118  {
2119  if (!$this or !$nt) {
2120  return 'badtitletext';
2121  }
2122  if ($this->equals($nt)) {
2123  return 'selfmove';
2124  }
2125  if (!$this->isMovable() || !$nt->isMovable()) {
2126  return 'immobile_namespace';
2127  }
2128 
2129  $oldid = $this->getArticleID();
2130  $newid = $nt->getArticleID();
2131 
2132  if (strlen($nt->getDBkey()) < 1) {
2133  return 'articleexists';
2134  }
2135  if (('' == $this->getDBkey()) ||
2136  (!$oldid) ||
2137  ('' == $nt->getDBkey())) {
2138  return 'badarticleerror';
2139  }
2140 
2141  if ($auth && (
2142  !$this->userCan('edit') || !$nt->userCan('edit') ||
2143  !$this->userCan('move') || !$nt->userCan('move')
2144  )) {
2145  return 'protectedpage';
2146  }
2147 
2148  # The move is allowed only if (1) the target doesn't exist, or
2149  # (2) the target is a redirect to the source, and has no history
2150  # (so we can undo bad moves right after they're done).
2151 
2152  if (0 != $newid) { # Target exists; check for validity
2153  if (!$this->isValidMoveTarget($nt)) {
2154  return 'articleexists';
2155  }
2156  }
2157  return true;
2158  }
2159 
2167  public function moveTo(&$nt, $auth = true, $reason = '')
2168  {
2169  $err = $this->isValidMoveOperation($nt, $auth);
2170  if (is_string($err)) {
2171  return $err;
2172  }
2173 
2174  $pageid = $this->getArticleID();
2175  if ($nt->exists()) {
2176  $this->moveOverExistingRedirect($nt, $reason);
2177  $pageCountChange = 0;
2178  } else { # Target didn't exist, do normal move.
2179  $this->moveToNewTitle($nt, $reason);
2180  $pageCountChange = 1;
2181  }
2182  $redirid = $this->getArticleID();
2183 
2184  # Fixing category links (those without piped 'alternate' names) to be sorted under the new title
2185  $dbw = wfGetDB(DB_MASTER);
2186  $categorylinks = $dbw->tableName('categorylinks');
2187  $sql = "UPDATE $categorylinks SET cl_sortkey=" . $dbw->addQuotes($nt->getPrefixedText()) .
2188  " WHERE cl_from=" . $dbw->addQuotes($pageid) .
2189  " AND cl_sortkey=" . $dbw->addQuotes($this->getPrefixedText());
2190  $dbw->query($sql, 'SpecialMovepage::doSubmit');
2191 
2192  # Update watchlists
2193 
2194  $oldnamespace = $this->getNamespace() & ~1;
2195  $newnamespace = $nt->getNamespace() & ~1;
2196  $oldtitle = $this->getDBkey();
2197  $newtitle = $nt->getDBkey();
2198 
2199  if ($oldnamespace != $newnamespace || $oldtitle != $newtitle) {
2200  WatchedItem::duplicateEntries($this, $nt);
2201  }
2202 
2203  # Update search engine
2204  $u = new SearchUpdate($pageid, $nt->getPrefixedDBkey());
2205  $u->doUpdate();
2206  $u = new SearchUpdate($redirid, $this->getPrefixedDBkey(), '');
2207  $u->doUpdate();
2208 
2209  # Update site_stats
2210  if ($this->isContentPage() && !$nt->isContentPage()) {
2211  # No longer a content page
2212  # Not viewed, edited, removing
2213  $u = new SiteStatsUpdate(0, 1, -1, $pageCountChange);
2214  } elseif (!$this->isContentPage() && $nt->isContentPage()) {
2215  # Now a content page
2216  # Not viewed, edited, adding
2217  $u = new SiteStatsUpdate(0, 1, +1, $pageCountChange);
2218  } elseif ($pageCountChange) {
2219  # Redirect added
2220  $u = new SiteStatsUpdate(0, 0, 0, 1);
2221  } else {
2222  # Nothing special
2223  $u = false;
2224  }
2225  if ($u) {
2226  $u->doUpdate();
2227  }
2228 
2229  global $wgUser;
2230  wfRunHooks('TitleMoveComplete', array( &$this, &$nt, &$wgUser, $pageid, $redirid ));
2231  return true;
2232  }
2233 
2241  private function moveOverExistingRedirect(&$nt, $reason = '')
2242  {
2243  global $wgUseSquid;
2245  $comment = wfMsgForContent('1movedto2_redir', $this->getPrefixedText(), $nt->getPrefixedText());
2246 
2247  if ($reason) {
2248  $comment .= ": $reason";
2249  }
2250 
2251  $now = wfTimestampNow();
2252  $newid = $nt->getArticleID();
2253  $oldid = $this->getArticleID();
2254  $dbw = wfGetDB(DB_MASTER);
2255  $linkCache = &LinkCache::singleton();
2256 
2257  # Delete the old redirect. We don't save it to history since
2258  # by definition if we've got here it's rather uninteresting.
2259  # We have to remove it so that the next step doesn't trigger
2260  # a conflict on the unique namespace+title index...
2261  $dbw->delete('page', array( 'page_id' => $newid ), $fname);
2262 
2263  # Save a null revision in the page's history notifying of the move
2264  $nullRevision = Revision::newNullRevision($dbw, $oldid, $comment, true);
2265  $nullRevId = $nullRevision->insertOn($dbw);
2266 
2267  # Change the name of the target page:
2268  $dbw->update(
2269  'page',
2270  /* SET */
2271  array(
2272  'page_touched' => $dbw->timestamp($now),
2273  'page_namespace' => $nt->getNamespace(),
2274  'page_title' => $nt->getDBkey(),
2275  'page_latest' => $nullRevId,
2276  ),
2277  /* WHERE */
2278  array( 'page_id' => $oldid ),
2279  $fname
2280  );
2281  $linkCache->clearLink($nt->getPrefixedDBkey());
2282 
2283  # Recreate the redirect, this time in the other direction.
2284  $mwRedir = MagicWord::get('redirect');
2285  $redirectText = $mwRedir->getSynonym(0) . ' [[' . $nt->getPrefixedText() . "]]\n";
2286  $redirectArticle = new Article($this);
2287  $newid = $redirectArticle->insertOn($dbw);
2288  $redirectRevision = new Revision(array(
2289  'page' => $newid,
2290  'comment' => $comment,
2291  'text' => $redirectText ));
2292  $redirectRevision->insertOn($dbw);
2293  $redirectArticle->updateRevisionOn($dbw, $redirectRevision, 0);
2294  $linkCache->clearLink($this->getPrefixedDBkey());
2295 
2296  # Log the move
2297  $log = new LogPage('move');
2298  $log->addEntry('move_redir', $this, $reason, array( 1 => $nt->getPrefixedText() ));
2299 
2300  # Now, we record the link from the redirect to the new title.
2301  # It should have no other outgoing links...
2302  $dbw->delete('pagelinks', array( 'pl_from' => $newid ), $fname);
2303  $dbw->insert(
2304  'pagelinks',
2305  array(
2306  'pl_from' => $newid,
2307  'pl_namespace' => $nt->getNamespace(),
2308  'pl_title' => $nt->getDbKey() ),
2309  $fname
2310  );
2311 
2312  # Purge squid
2313  if ($wgUseSquid) {
2314  $urls = array_merge($nt->getSquidURLs(), $this->getSquidURLs());
2315  $u = new SquidUpdate($urls);
2316  $u->doUpdate();
2317  }
2318  }
2319 
2324  private function moveToNewTitle(&$nt, $reason = '')
2325  {
2326  global $wgUseSquid;
2327  $fname = 'MovePageForm::moveToNewTitle';
2328  $comment = wfMsgForContent('1movedto2', $this->getPrefixedText(), $nt->getPrefixedText());
2329  if ($reason) {
2330  $comment .= ": $reason";
2331  }
2332 
2333  $newid = $nt->getArticleID();
2334  $oldid = $this->getArticleID();
2335  $dbw = wfGetDB(DB_MASTER);
2336  $now = $dbw->timestamp();
2337  $linkCache = &LinkCache::singleton();
2338 
2339  # Save a null revision in the page's history notifying of the move
2340  $nullRevision = Revision::newNullRevision($dbw, $oldid, $comment, true);
2341  $nullRevId = $nullRevision->insertOn($dbw);
2342 
2343  # Rename cur entry
2344  $dbw->update(
2345  'page',
2346  /* SET */
2347  array(
2348  'page_touched' => $now,
2349  'page_namespace' => $nt->getNamespace(),
2350  'page_title' => $nt->getDBkey(),
2351  'page_latest' => $nullRevId,
2352  ),
2353  /* WHERE */
2354  array( 'page_id' => $oldid ),
2355  $fname
2356  );
2357 
2358  $linkCache->clearLink($nt->getPrefixedDBkey());
2359 
2360  # Insert redirect
2361  $mwRedir = MagicWord::get('redirect');
2362  $redirectText = $mwRedir->getSynonym(0) . ' [[' . $nt->getPrefixedText() . "]]\n";
2363  $redirectArticle = new Article($this);
2364  $newid = $redirectArticle->insertOn($dbw);
2365  $redirectRevision = new Revision(array(
2366  'page' => $newid,
2367  'comment' => $comment,
2368  'text' => $redirectText ));
2369  $redirectRevision->insertOn($dbw);
2370  $redirectArticle->updateRevisionOn($dbw, $redirectRevision, 0);
2371  $linkCache->clearLink($this->getPrefixedDBkey());
2372 
2373  # Log the move
2374  $log = new LogPage('move');
2375  $log->addEntry('move', $this, $reason, array( 1 => $nt->getPrefixedText()));
2376 
2377  # Purge caches as per article creation
2378  Article::onArticleCreate($nt);
2379 
2380  # Record the just-created redirect's linking to the page
2381  $dbw->insert(
2382  'pagelinks',
2383  array(
2384  'pl_from' => $newid,
2385  'pl_namespace' => $nt->getNamespace(),
2386  'pl_title' => $nt->getDBkey() ),
2387  $fname
2388  );
2389 
2390  # Purge old title from squid
2391  # The new title, and links to the new title, are purged in Article::onArticleCreate()
2392  $this->purgeSquid();
2393  }
2394 
2401  public function isValidMoveTarget($nt)
2402  {
2403  $fname = 'Title::isValidMoveTarget';
2404  $dbw = wfGetDB(DB_MASTER);
2405 
2406  # Is it a redirect?
2407  $id = $nt->getArticleID();
2408  $obj = $dbw->selectRow(
2409  array( 'page', 'revision', 'text'),
2410  array( 'page_is_redirect','old_text','old_flags' ),
2411  array( 'page_id' => $id, 'page_latest=rev_id', 'rev_text_id=old_id' ),
2412  $fname,
2413  'FOR UPDATE'
2414  );
2415 
2416  if (!$obj || 0 == $obj->page_is_redirect) {
2417  # Not a redirect
2418  wfDebug(__METHOD__ . ": not a redirect\n");
2419  return false;
2420  }
2421  $text = Revision::getRevisionText($obj);
2422 
2423  # Does the redirect point to the source?
2424  # Or is it a broken self-redirect, usually caused by namespace collisions?
2425  $m = array();
2426  if (preg_match("/\\[\\[\\s*([^\\]\\|]*)]]/", $text, $m)) {
2427  $redirTitle = Title::newFromText($m[1]);
2428  if (!is_object($redirTitle) ||
2429  ($redirTitle->getPrefixedDBkey() != $this->getPrefixedDBkey() &&
2430  $redirTitle->getPrefixedDBkey() != $nt->getPrefixedDBkey())) {
2431  wfDebug(__METHOD__ . ": redirect points to other page\n");
2432  return false;
2433  }
2434  } else {
2435  # Fail safe
2436  wfDebug(__METHOD__ . ": failsafe\n");
2437  return false;
2438  }
2439 
2440  # Does the article have a history?
2441  $row = $dbw->selectRow(
2442  array( 'page', 'revision'),
2443  array( 'rev_id' ),
2444  array( 'page_namespace' => $nt->getNamespace(),
2445  'page_title' => $nt->getDBkey(),
2446  'page_id=rev_page AND page_latest != rev_id'
2447  ),
2448  $fname,
2449  'FOR UPDATE'
2450  );
2451 
2452  # Return true if there was no history
2453  return $row === false;
2454  }
2455 
2463  public function getParentCategories()
2464  {
2465  global $wgContLang;
2466 
2467  $titlekey = $this->getArticleId();
2468  $dbr = wfGetDB(DB_SLAVE);
2469  $categorylinks = $dbr->tableName('categorylinks');
2470 
2471  # NEW SQL
2472  $sql = "SELECT * FROM $categorylinks"
2473  . " WHERE cl_from='$titlekey'"
2474  . " AND cl_from <> '0'"
2475  . " ORDER BY cl_sortkey";
2476 
2477  $res = $dbr->query($sql) ;
2478 
2479  if ($dbr->numRows($res) > 0) {
2480  while ($x = $dbr->fetchObject($res)) {
2481  //$data[] = Title::newFromText($wgContLang->getNSText ( NS_CATEGORY ).':'.$x->cl_to);
2482  $data[$wgContLang->getNSText(NS_CATEGORY) . ':' . $x->cl_to] = $this->getFullText();
2483  }
2484  $dbr->freeResult($res) ;
2485  } else {
2486  $data = '';
2487  }
2488  return $data;
2489  }
2490 
2496  public function getParentCategoryTree($children = array())
2497  {
2498  $parents = $this->getParentCategories();
2499 
2500  if ($parents != '') {
2501  foreach ($parents as $parent => $current) {
2502  if (array_key_exists($parent, $children)) {
2503  # Circular reference
2504  $stack[$parent] = array();
2505  } else {
2506  $nt = Title::newFromText($parent);
2507  if ($nt) {
2508  $stack[$parent] = $nt->getParentCategoryTree($children + array($parent => 1));
2509  }
2510  }
2511  }
2512  return $stack;
2513  } else {
2514  return array();
2515  }
2516  }
2517 
2518 
2525  public function pageCond()
2526  {
2527  return array( 'page_namespace' => $this->mNamespace, 'page_title' => $this->mDbkeyform );
2528  }
2529 
2536  public function getPreviousRevisionID($revision)
2537  {
2538  $dbr = wfGetDB(DB_SLAVE);
2539  return $dbr->selectField(
2540  'revision',
2541  'rev_id',
2542  'rev_page=' . intval($this->getArticleId()) .
2543  ' AND rev_id<' . intval($revision) . ' ORDER BY rev_id DESC'
2544  );
2545  }
2546 
2553  public function getNextRevisionID($revision)
2554  {
2555  $dbr = wfGetDB(DB_SLAVE);
2556  return $dbr->selectField(
2557  'revision',
2558  'rev_id',
2559  'rev_page=' . intval($this->getArticleId()) .
2560  ' AND rev_id>' . intval($revision) . ' ORDER BY rev_id'
2561  );
2562  }
2563 
2571  public function countRevisionsBetween($old, $new)
2572  {
2573  $dbr = wfGetDB(DB_SLAVE);
2574  return $dbr->selectField(
2575  'revision',
2576  'count(*)',
2577  'rev_page = ' . intval($this->getArticleId()) .
2578  ' AND rev_id > ' . intval($old) .
2579  ' AND rev_id < ' . intval($new)
2580  );
2581  }
2582 
2589  public function equals($title)
2590  {
2591  // Note: === is necessary for proper matching of number-like titles.
2592  return $this->getInterwiki() === $title->getInterwiki()
2593  && $this->getNamespace() == $title->getNamespace()
2594  && $this->getDbkey() === $title->getDbkey();
2595  }
2596 
2601  public function exists()
2602  {
2603  return $this->getArticleId() != 0;
2604  }
2605 
2612  public function isAlwaysKnown()
2613  {
2614  return $this->isExternal() || (0 == $this->mNamespace && "" == $this->mDbkeyform)
2615  || NS_SPECIAL == $this->mNamespace;
2616  }
2617 
2623  public function touchLinks()
2624  {
2625  $u = new HTMLCacheUpdate($this, 'pagelinks');
2626  $u->doUpdate();
2627 
2628  if ($this->getNamespace() == NS_CATEGORY) {
2629  $u = new HTMLCacheUpdate($this, 'categorylinks');
2630  $u->doUpdate();
2631  }
2632  }
2633 
2637  public function getTouched()
2638  {
2639  $dbr = wfGetDB(DB_SLAVE);
2640  $touched = $dbr->selectField(
2641  'page',
2642  'page_touched',
2643  array(
2644  'page_namespace' => $this->getNamespace(),
2645  'page_title' => $this->getDBkey()
2646  ),
2647  __METHOD__
2648  );
2649  return $touched;
2650  }
2651 
2652  public function trackbackURL()
2653  {
2654  global $wgTitle, $wgScriptPath, $wgServer;
2655 
2656  return "$wgServer$wgScriptPath/trackback.php?article="
2657  . htmlspecialchars(urlencode($wgTitle->getPrefixedDBkey()));
2658  }
2659 
2660  public function trackbackRDF()
2661  {
2662  $url = htmlspecialchars($this->getFullURL());
2663  $title = htmlspecialchars($this->getText());
2664  $tburl = $this->trackbackURL();
2665 
2666  return "
2667 <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"
2668  xmlns:dc=\"http://purl.org/dc/elements/1.1/\"
2669  xmlns:trackback=\"http://madskills.com/public/xml/rss/module/trackback/\">
2670 <rdf:Description
2671  rdf:about=\"$url\"
2672  dc:identifier=\"$url\"
2673  dc:title=\"$title\"
2674  trackback:ping=\"$tburl\" />
2675 </rdf:RDF>";
2676  }
2677 
2682  public function getNamespaceKey()
2683  {
2684  global $wgContLang;
2685  switch ($this->getNamespace()) {
2686  case NS_MAIN:
2687  case NS_TALK:
2688  return 'nstab-main';
2689  case NS_USER:
2690  case NS_USER_TALK:
2691  return 'nstab-user';
2692  case NS_MEDIA:
2693  return 'nstab-media';
2694  case NS_SPECIAL:
2695  return 'nstab-special';
2696  case NS_PROJECT:
2697  case NS_PROJECT_TALK:
2698  return 'nstab-project';
2699  case NS_IMAGE:
2700  case NS_IMAGE_TALK:
2701  return 'nstab-image';
2702  case NS_MEDIAWIKI:
2703  case NS_MEDIAWIKI_TALK:
2704  return 'nstab-mediawiki';
2705  case NS_TEMPLATE:
2706  case NS_TEMPLATE_TALK:
2707  return 'nstab-template';
2708  case NS_HELP:
2709  case NS_HELP_TALK:
2710  return 'nstab-help';
2711  case NS_CATEGORY:
2712  case NS_CATEGORY_TALK:
2713  return 'nstab-category';
2714  default:
2715  return 'nstab-' . $wgContLang->lc($this->getSubjectNsText());
2716  }
2717  }
2718 
2723  public function isSpecial($name)
2724  {
2725  if ($this->getNamespace() == NS_SPECIAL) {
2726  list($thisName, /* $subpage */ ) = SpecialPage::resolveAliasWithSubpage($this->getDBkey());
2727  if ($name == $thisName) {
2728  return true;
2729  }
2730  }
2731  return false;
2732  }
2733 
2738  public function fixSpecialName()
2739  {
2740  if ($this->getNamespace() == NS_SPECIAL) {
2741  $canonicalName = SpecialPage::resolveAlias($this->mDbkeyform);
2742  if ($canonicalName) {
2743  $localName = SpecialPage::getLocalNameFor($canonicalName);
2744  if ($localName != $this->mDbkeyform) {
2745  return Title::makeTitle(NS_SPECIAL, $localName);
2746  }
2747  }
2748  }
2749  return $this;
2750  }
2751 
2759 /* public function isContentPage() {
2760  return Namespace::isContent( $this->getNamespace() );
2761  }*/
2762 }
isAlwaysKnown()
Should a link should be displayed as a known link, just based on its title?
Definition: Title.php:2612
static getInterwikiCached($key)
Fetch interwiki prefix data from local cache in constant database.
Definition: Title.php:474
static purgeExpiredRestrictions()
Purge expired restrictions from the page_restrictions table.
Definition: Title.php:1568
if($err=$client->getError()) $namespace
setFragment($fragment)
Set the fragment for this title This is kind of bad, since except for this rarely-used function...
Definition: Title.php:1938
loadRestrictionsFromRow($res, $oldFashionedRestrictions=null)
Loads a string into mRestrictions array.
Definition: Title.php:1487
touchLinks()
Update page_touched timestamps and send squid purge messages for pages linking to this title...
Definition: Title.php:2623
userCanEditCssJsSubpage()
Protect css/js subpages of user pages: can $wgUser edit this page?
Definition: Title.php:1359
getFragment()
Get the Title fragment (i.e.
Definition: Title.php:659
countRevisionsBetween($old, $new)
Get the number of revisions between the given revision IDs.
Definition: Title.php:2571
getLinksTo($options='', $table='pagelinks', $prefix='pl')
Get a Title object associated with the talk page of this article.
Definition: Title.php:1971
$mNamespace
Definition: Title.php:57
isJsSubpage()
Is this a .js subpage of a user page?
Definition: Title.php:1348
getSquidURLs()
Get a list of URLs to purge from the Squid cache when this page changes.
Definition: Title.php:2066
invalidateCache()
Updates page_touched for this page; called from LinksUpdate.php.
Definition: Title.php:1688
isSpecial($name)
Returns true if this title resolves to the named special page.
Definition: Title.php:2723
getLatestRevID()
Definition: Title.php:1645
getArticleID($flags=0)
Get the article ID for this Title from the link cache, adding it if necessary.
Definition: Title.php:1630
getText()
Simple accessors.
Definition: Title.php:572
getSubpageText()
Get the lowest-level subpage name, i.e.
Definition: Title.php:759
getBaseText()
Get the base name, i.e.
Definition: Title.php:740
nameOf($id)
Get the prefixed DB key associated with an ID.
Definition: Title.php:337
static newMainPage()
Create a new Title for the Main Page.
Definition: Title.php:290
const NS_MAIN(!class_exists('UtfNormal'))
See title.txt.
Definition: Title.php:15
$result
$mArticleID
Definition: Title.php:60
$mRestrictionsExpiry
Definition: Title.php:64
$action
isCssJsSubpage()
Is this a .css or .js subpage of a user page?
Definition: Title.php:1310
$mInterwiki
Definition: Title.php:58
trackbackRDF()
Definition: Title.php:2660
secureAndSplit()
Secure and split - main initialisation function for this object.
Definition: Title.php:1747
moveNoAuth(&$nt)
Move this page without authentication.
Definition: Title.php:2103
getInterwikiLink($key)
Returns the URL associated with an interwiki prefix.
Definition: Title.php:415
up()
Definition: up.php:2
prefix($name)
Prefix some arbitrary text with the namespace or interwiki prefix of this object. ...
Definition: Title.php:1725
isProtected($action='')
Does the title correspond to a protected article?
Definition: Title.php:1028
static $interwikiCache
Definition: Title.php:42
static escapeFragmentForURL($fragment)
Escape a text fragment, say from a link, for a URL.
Definition: Title.php:552
isValidMoveTarget($nt)
Checks if $this can be moved to a given Title.
Definition: Title.php:2401
getLocalURL($query='', $variant=false)
Get a URL with no fragment or server name.
Definition: Title.php:849
escapeLocalURL($query='')
Get an HTML-escaped version of the URL form, suitable for using in a link, without a server name or f...
Definition: Title.php:927
if(!array_key_exists('StateId', $_REQUEST)) $id
$mHasCascadingRestrictions
Definition: Title.php:65
exists()
Check if page exists.
Definition: Title.php:2601
getPrefixedText()
Get the prefixed title with spaces.
Definition: Title.php:711
on($eventName, callable $callBack, $priority=100)
Subscribe to an event.
static newFromText($text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:124
$mRestrictionsLoaded
Definition: Title.php:67
getIndexTitle()
Get title for search index.
Definition: Title.php:689
Title class.
Definition: Title.php:36
getParentCategories()
Get categories to which this Title belongs and return an array of categories&#39; names.
Definition: Title.php:2463
const GAID_FOR_UPDATE
Definition: Title.php:18
$s
Definition: pwgen.php:45
getSkinFromCssJsSubpage()
Trim down a .css or .js subpage title to get the corresponding skin name.
Definition: Title.php:1330
static makeName($ns, $title)
Definition: Title.php:400
$log
Definition: sabredav.php:21
$mCascadeRestriction
Definition: Title.php:63
getNamespaceKey()
Generate strings used for xml &#39;id&#39; names in monobook tabs.
Definition: Title.php:2682
fixSpecialName()
If the Title refers to a special page alias which is not the local default, returns a new Title which...
Definition: Title.php:2738
$mDefaultNamespace
Definition: Title.php:69
userCanCreate($doExpensiveQueries=true)
Can $wgUser create this page?
Definition: Title.php:1213
userCanMove($doExpensiveQueries=true)
Can $wgUser move this page?
Definition: Title.php:1223
static decodeCharReferences($text)
Decode any character references, numeric or named entities, in the text and return a UTF-8 string...
Definition: Sanitizer.php:1018
$auth
Definition: fileserver.php:48
userCanRead()
Would anybody with sufficient privileges be able to move this page? Some pages just aren&#39;t movable...
Definition: Title.php:1244
getNsText()
Get the namespace text.
Definition: Title.php:604
__construct()
#-
Definition: Title.php:79
const MW_TITLECACHE_MAX
Definition: Title.php:25
isExternal()
Is this Title interwiki?
Definition: Title.php:990
static newFromIDs($ids)
Make an array of titles from an array of IDs.
Definition: Title.php:225
escapeFullURL($query='')
Get an HTML-escaped version of the URL form, suitable for using in a link, including the server name ...
Definition: Title.php:939
getDBkey()
Get the main part with underscores.
Definition: Title.php:588
getPrefixedURL()
Get a URL-encoded title (not an actual URL) including interwiki.
Definition: Title.php:786
$urls
Definition: croninfo.php:28
$mFragment
Definition: Title.php:59
areRestrictionsCascading()
Definition: Title.php:1474
static newFromDBkey($key)
Create a new Title from a prefixed DB key.
Definition: Title.php:102
getFullURL($query='', $variant=false)
Get a real URL referring to this title, with interwiki link and fragment.
Definition: Title.php:809
$r
Definition: example_031.php:79
getBrokenLinksFrom($options='')
Get an array of Title objects referring to non-existent articles linked from this page...
Definition: Title.php:2026
$success
Definition: Utf8Test.php:86
foreach($_POST as $key=> $value) $res
isCascadeProtected()
Cascading protection: Return true if cascading restrictions apply to this page, false if not...
Definition: Title.php:1370
getDefaultNamespace()
Get the default namespace index, for when there is no namespace.
Definition: Title.php:679
getNextRevisionID($revision)
Get the revision ID of the next revision.
Definition: Title.php:2553
$mRestrictions
Definition: Title.php:62
getFullText()
Get the prefixed title with spaces, plus any fragment (part beginning with &#39;#&#39;)
Definition: Title.php:727
const NS_SPECIAL
Definition: Title.php:16
isCssSubpage()
Is this a .css subpage of a user page?
Definition: Title.php:1340
static $titleCache
Static cache variables.
Definition: Title.php:41
static indexTitle($ns, $title)
Get a string representation of a title suitable for including in a search index.
Definition: Title.php:373
getTouched()
Get the last touched timestamp.
Definition: Title.php:2637
getNamespace()
Get the namespace index, i.e.
Definition: Title.php:596
get(string $class_name)
$text
Definition: errorreport.php:18
getCascadeProtectionSources($get_pages=true)
Cascading protection: Get the source of any cascading restrictions on this page.
Definition: Title.php:1384
const UTF8_REPLACEMENT
Definition: UtfNormal.php:68
static newFromRedirect($text)
Create a new Title for a redirect.
Definition: Title.php:301
getPreviousRevisionID($revision)
Get the revision ID of the previous revision.
Definition: Title.php:2536
isSubpage()
Is this a talk page of some sort?
Definition: Title.php:1295
getInterwiki()
Get the namespace text of the subject (rather than talk) page.
Definition: Title.php:651
static newFromID($id)
Create a new Title from an article ID.
Definition: Title.php:204
$query
getRestrictions($action)
Accessor/initialisation for mRestrictions.
Definition: Title.php:1584
quickUserCan($action)
Can $wgUser perform $action on this page? This skips potentially expensive cascading permission check...
Definition: Title.php:1089
$n
Definition: RandomTest.php:85
getEditURL()
Get the edit URL for this Title.
Definition: Title.php:966
$comment
Definition: buildRTE.php:83
static makeTitleSafe($ns, $title)
Create a new Title from a namespace index and a DB key.
Definition: Title.php:275
loadRestrictions($oldFashionedRestrictions=null)
Definition: Title.php:1549
$mDbkeyform
Definition: Title.php:56
$mLatestID
Definition: Title.php:61
$row
getSubpageUrlForm()
Get a URL-encoded form of the subpage text.
Definition: Title.php:774
moveTo(&$nt, $auth=true, $reason='')
Move a title to a new location.
Definition: Title.php:2167
getEscapedText()
Get the HTML-escaped displayable text form.
Definition: Title.php:981
moveToNewTitle(&$nt, $reason='')
Move page to non-existing title.
Definition: Title.php:2324
isTrans()
Determine whether the object refers to a page within this project and is transcludable.
Definition: Title.php:538
static newFromURL($url)
Create a new Title from URL-encoded text.
Definition: Title.php:175
getTemplateLinksTo($options='')
Get an array of Title objects using this Title as a template Also stores the IDs in the link cache...
Definition: Title.php:2015
moveOverExistingRedirect(&$nt, $reason='')
Move page to a title which is at present a redirect to the source page.
Definition: Title.php:2241
getFragmentForURL()
Get the fragment in URL form, including the "#" character if there is one.
Definition: Title.php:667
trackbackURL()
Definition: Title.php:2652
$lc
Definition: date.php:267
pageCond()
Get an associative array for selecting this title from the "page" table.
Definition: Title.php:2525
$ret
Definition: parser.php:6
$mTextform
All member variables should be considered private Please use the accessor functions.
Definition: Title.php:54
isValidMoveOperation(&$nt, $auth=true)
Check whether a given move operation would be valid.
Definition: Title.php:2117
static legalChars()
Get a regex character class describing the legal characters in a link.
Definition: Title.php:355
static wfUrlencode($s)
From GlobalFunctions.php.
$mPrefixedText
Definition: Title.php:68
isDeleted()
Is there a version of this page in the deletion archive?
Definition: Title.php:1602
isLocal()
Determine whether the object refers to a page within this project.
Definition: Title.php:520
static & makeTitle($ns, $title)
Create a new Title from a namespace index and a DB key.
Definition: Title.php:253
$url
isValidCssJsSubpage()
Is this a valid .css or .js subpage of a user page? Check that the corresponding skin exists...
Definition: Title.php:1318
userCanEdit($doExpensiveQueries=true)
Can $wgUser edit this page?
Definition: Title.php:1203
getParentCategoryTree($children=array())
Get a tree of parent categories.
Definition: Title.php:2496
if(empty($password)) $table
Definition: pwgen.php:24
getInternalURL($query='', $variant=false)
Get the URL form for an internal link.
Definition: Title.php:953
userCan($action, $doExpensiveQueries=true)
Can $wgUser perform $action on this page?
Definition: Title.php:1100
isSemiProtected($action='edit')
Is this page "semi-protected" - the only protection is autoconfirm?
Definition: Title.php:1001
$mWatched
Definition: Title.php:71
$key
Definition: croninfo.php:18
userIsWatching()
Is $wgUser is watching this page?
Definition: Title.php:1063
$cols
Definition: xhr_table.php:11
$x
Definition: complexTest.php:9
$mCascadeRestrictionSources
Definition: Title.php:66
$mUrlform
Definition: Title.php:55
equals($title)
Compare with another title.
Definition: Title.php:2589
purgeSquid()
Definition: Title.php:2089
resetArticleID($newid)
This clears some fields in this object, and clears any associated keys in the "bad links" section of ...
Definition: Title.php:1670
getPartialURL()
Get the URL-encoded form of the main part.
Definition: Title.php:580
getPrefixedDBkey()
Get the prefixed database key form.
Definition: Title.php:699
$data
Definition: bench.php:6