Index: trunk/phase3/includes/Title.php |
— | — | @@ -480,6 +480,33 @@ |
481 | 481 | } |
482 | 482 | |
483 | 483 | /** |
| 484 | + * Returns a simple regex that will match on characters and sequences invalid in titles. |
| 485 | + * Note that this doesn't pick up many things that could be wrong with titles, but that |
| 486 | + * replacing this regex with something valid will make many titles valid. |
| 487 | + * |
| 488 | + * @return String regex string |
| 489 | + */ |
| 490 | + static function getTitleInvalidRegex() { |
| 491 | + static $rxTc = false; |
| 492 | + if ( !$rxTc ) { |
| 493 | + # Matching titles will be held as illegal. |
| 494 | + $rxTc = '/' . |
| 495 | + # Any character not allowed is forbidden... |
| 496 | + '[^' . self::legalChars() . ']' . |
| 497 | + # URL percent encoding sequences interfere with the ability |
| 498 | + # to round-trip titles -- you can't link to them consistently. |
| 499 | + '|%[0-9A-Fa-f]{2}' . |
| 500 | + # XML/HTML character references produce similar issues. |
| 501 | + '|&[A-Za-z0-9\x80-\xff]+;' . |
| 502 | + '|&#[0-9]+;' . |
| 503 | + '|&#x[0-9A-Fa-f]+;' . |
| 504 | + '/S'; |
| 505 | + } |
| 506 | + |
| 507 | + return $rxTc; |
| 508 | + } |
| 509 | + |
| 510 | + /** |
484 | 511 | * Get a string representation of a title suitable for |
485 | 512 | * including in a search index |
486 | 513 | * |
— | — | @@ -545,6 +572,22 @@ |
546 | 573 | } |
547 | 574 | |
548 | 575 | /** |
| 576 | + * Callback for usort() to do title sorts by (namespace, title) |
| 577 | + * |
| 578 | + * @param $a Title |
| 579 | + * @param $b Title |
| 580 | + * |
| 581 | + * @return Integer: result of string comparison, or namespace comparison |
| 582 | + */ |
| 583 | + public static function compare( $a, $b ) { |
| 584 | + if ( $a->getNamespace() == $b->getNamespace() ) { |
| 585 | + return strcmp( $a->getText(), $b->getText() ); |
| 586 | + } else { |
| 587 | + return $a->getNamespace() - $b->getNamespace(); |
| 588 | + } |
| 589 | + } |
| 590 | + |
| 591 | + /** |
549 | 592 | * Determine whether the object refers to a page within |
550 | 593 | * this project. |
551 | 594 | * |
— | — | @@ -631,6 +674,15 @@ |
632 | 675 | } |
633 | 676 | |
634 | 677 | /** |
| 678 | + * Get the DB key with the initial letter case as specified by the user |
| 679 | + * |
| 680 | + * @return String DB key |
| 681 | + */ |
| 682 | + function getUserCaseDBKey() { |
| 683 | + return $this->mUserCaseDBKey; |
| 684 | + } |
| 685 | + |
| 686 | + /** |
635 | 687 | * Get the namespace index, i.e. one of the NS_xxxx constants. |
636 | 688 | * |
637 | 689 | * @return Integer: Namespace index |
— | — | @@ -677,15 +729,6 @@ |
678 | 730 | } |
679 | 731 | |
680 | 732 | /** |
681 | | - * Get the DB key with the initial letter case as specified by the user |
682 | | - * |
683 | | - * @return String DB key |
684 | | - */ |
685 | | - function getUserCaseDBKey() { |
686 | | - return $this->mUserCaseDBKey; |
687 | | - } |
688 | | - |
689 | | - /** |
690 | 733 | * Get the namespace text of the subject (rather than talk) page |
691 | 734 | * |
692 | 735 | * @return String Namespace text |
— | — | @@ -715,25 +758,290 @@ |
716 | 759 | } |
717 | 760 | |
718 | 761 | /** |
719 | | - * Get the Title fragment (i.e.\ the bit after the #) in text form |
| 762 | + * Is this in a namespace that allows actual pages? |
720 | 763 | * |
721 | | - * @return String Title fragment |
| 764 | + * @return Bool |
| 765 | + * @internal note -- uses hardcoded namespace index instead of constants |
722 | 766 | */ |
723 | | - public function getFragment() { return $this->mFragment; } |
| 767 | + public function canExist() { |
| 768 | + return $this->mNamespace >= 0 && $this->mNamespace != NS_MEDIA; |
| 769 | + } |
724 | 770 | |
725 | 771 | /** |
726 | | - * Get the fragment in URL form, including the "#" character if there is one |
727 | | - * @return String Fragment in URL form |
| 772 | + * Can this title be added to a user's watchlist? |
| 773 | + * |
| 774 | + * @return Bool TRUE or FALSE |
728 | 775 | */ |
729 | | - public function getFragmentForURL() { |
730 | | - if ( $this->mFragment == '' ) { |
731 | | - return ''; |
732 | | - } else { |
733 | | - return '#' . Title::escapeFragmentForURL( $this->mFragment ); |
| 776 | + public function isWatchable() { |
| 777 | + return !$this->isExternal() && MWNamespace::isWatchable( $this->getNamespace() ); |
| 778 | + } |
| 779 | + |
| 780 | + /** |
| 781 | + * Returns true if this is a special page. |
| 782 | + * |
| 783 | + * @return boolean |
| 784 | + */ |
| 785 | + public function isSpecialPage() { |
| 786 | + return $this->getNamespace() == NS_SPECIAL; |
| 787 | + } |
| 788 | + |
| 789 | + /** |
| 790 | + * Returns true if this title resolves to the named special page |
| 791 | + * |
| 792 | + * @param $name String The special page name |
| 793 | + * @return boolean |
| 794 | + */ |
| 795 | + public function isSpecial( $name ) { |
| 796 | + if ( $this->isSpecialPage() ) { |
| 797 | + list( $thisName, /* $subpage */ ) = SpecialPageFactory::resolveAlias( $this->getDBkey() ); |
| 798 | + if ( $name == $thisName ) { |
| 799 | + return true; |
| 800 | + } |
734 | 801 | } |
| 802 | + return false; |
735 | 803 | } |
736 | 804 | |
737 | 805 | /** |
| 806 | + * If the Title refers to a special page alias which is not the local default, resolve |
| 807 | + * the alias, and localise the name as necessary. Otherwise, return $this |
| 808 | + * |
| 809 | + * @return Title |
| 810 | + */ |
| 811 | + public function fixSpecialName() { |
| 812 | + if ( $this->isSpecialPage() ) { |
| 813 | + list( $canonicalName, $par ) = SpecialPageFactory::resolveAlias( $this->mDbkeyform ); |
| 814 | + if ( $canonicalName ) { |
| 815 | + $localName = SpecialPageFactory::getLocalNameFor( $canonicalName, $par ); |
| 816 | + if ( $localName != $this->mDbkeyform ) { |
| 817 | + return Title::makeTitle( NS_SPECIAL, $localName ); |
| 818 | + } |
| 819 | + } |
| 820 | + } |
| 821 | + return $this; |
| 822 | + } |
| 823 | + |
| 824 | + /** |
| 825 | + * Returns true if the title is inside the specified namespace. |
| 826 | + * |
| 827 | + * Please make use of this instead of comparing to getNamespace() |
| 828 | + * This function is much more resistant to changes we may make |
| 829 | + * to namespaces than code that makes direct comparisons. |
| 830 | + * @param $ns int The namespace |
| 831 | + * @return bool |
| 832 | + * @since 1.19 |
| 833 | + */ |
| 834 | + public function inNamespace( $ns ) { |
| 835 | + return MWNamespace::equals( $this->getNamespace(), $ns ); |
| 836 | + } |
| 837 | + |
| 838 | + /** |
| 839 | + * Returns true if the title is inside one of the specified namespaces. |
| 840 | + * |
| 841 | + * @param ...$namespaces The namespaces to check for |
| 842 | + * @return bool |
| 843 | + * @since 1.19 |
| 844 | + */ |
| 845 | + public function inNamespaces( /* ... */ ) { |
| 846 | + $namespaces = func_get_args(); |
| 847 | + if ( count( $namespaces ) > 0 && is_array( $namespaces[0] ) ) { |
| 848 | + $namespaces = $namespaces[0]; |
| 849 | + } |
| 850 | + |
| 851 | + foreach ( $namespaces as $ns ) { |
| 852 | + if ( $this->inNamespace( $ns ) ) { |
| 853 | + return true; |
| 854 | + } |
| 855 | + } |
| 856 | + |
| 857 | + return false; |
| 858 | + } |
| 859 | + |
| 860 | + /** |
| 861 | + * Returns true if the title has the same subject namespace as the |
| 862 | + * namespace specified. |
| 863 | + * For example this method will take NS_USER and return true if namespace |
| 864 | + * is either NS_USER or NS_USER_TALK since both of them have NS_USER |
| 865 | + * as their subject namespace. |
| 866 | + * |
| 867 | + * This is MUCH simpler than individually testing for equivilance |
| 868 | + * against both NS_USER and NS_USER_TALK, and is also forward compatible. |
| 869 | + * @since 1.19 |
| 870 | + */ |
| 871 | + public function hasSubjectNamespace( $ns ) { |
| 872 | + return MWNamespace::subjectEquals( $this->getNamespace(), $ns ); |
| 873 | + } |
| 874 | + |
| 875 | + /** |
| 876 | + * Is this Title in a namespace which contains content? |
| 877 | + * In other words, is this a content page, for the purposes of calculating |
| 878 | + * statistics, etc? |
| 879 | + * |
| 880 | + * @return Boolean |
| 881 | + */ |
| 882 | + public function isContentPage() { |
| 883 | + return MWNamespace::isContent( $this->getNamespace() ); |
| 884 | + } |
| 885 | + |
| 886 | + /** |
| 887 | + * Would anybody with sufficient privileges be able to move this page? |
| 888 | + * Some pages just aren't movable. |
| 889 | + * |
| 890 | + * @return Bool TRUE or FALSE |
| 891 | + */ |
| 892 | + public function isMovable() { |
| 893 | + if ( !MWNamespace::isMovable( $this->getNamespace() ) || $this->getInterwiki() != '' ) { |
| 894 | + // Interwiki title or immovable namespace. Hooks don't get to override here |
| 895 | + return false; |
| 896 | + } |
| 897 | + |
| 898 | + $result = true; |
| 899 | + wfRunHooks( 'TitleIsMovable', array( $this, &$result ) ); |
| 900 | + return $result; |
| 901 | + } |
| 902 | + |
| 903 | + /** |
| 904 | + * Is this the mainpage? |
| 905 | + * @note Title::newFromText seams to be sufficiently optimized by the title |
| 906 | + * cache that we don't need to over-optimize by doing direct comparisons and |
| 907 | + * acidentally creating new bugs where $title->equals( Title::newFromText() ) |
| 908 | + * ends up reporting something differently than $title->isMainPage(); |
| 909 | + * |
| 910 | + * @since 1.18 |
| 911 | + * @return Bool |
| 912 | + */ |
| 913 | + public function isMainPage() { |
| 914 | + return $this->equals( Title::newMainPage() ); |
| 915 | + } |
| 916 | + |
| 917 | + /** |
| 918 | + * Is this a subpage? |
| 919 | + * |
| 920 | + * @return Bool |
| 921 | + */ |
| 922 | + public function isSubpage() { |
| 923 | + return MWNamespace::hasSubpages( $this->mNamespace ) |
| 924 | + ? strpos( $this->getText(), '/' ) !== false |
| 925 | + : false; |
| 926 | + } |
| 927 | + |
| 928 | + /** |
| 929 | + * Is this a conversion table for the LanguageConverter? |
| 930 | + * |
| 931 | + * @return Bool |
| 932 | + */ |
| 933 | + public function isConversionTable() { |
| 934 | + return $this->getNamespace() == NS_MEDIAWIKI && |
| 935 | + strpos( $this->getText(), 'Conversiontable' ) !== false; |
| 936 | + } |
| 937 | + |
| 938 | + /** |
| 939 | + * Does that page contain wikitext, or it is JS, CSS or whatever? |
| 940 | + * |
| 941 | + * @return Bool |
| 942 | + */ |
| 943 | + public function isWikitextPage() { |
| 944 | + $retval = !$this->isCssOrJsPage() && !$this->isCssJsSubpage(); |
| 945 | + wfRunHooks( 'TitleIsWikitextPage', array( $this, &$retval ) ); |
| 946 | + return $retval; |
| 947 | + } |
| 948 | + |
| 949 | + /** |
| 950 | + * Could this page contain custom CSS or JavaScript, based |
| 951 | + * on the title? |
| 952 | + * |
| 953 | + * @return Bool |
| 954 | + */ |
| 955 | + public function isCssOrJsPage() { |
| 956 | + $retval = $this->mNamespace == NS_MEDIAWIKI |
| 957 | + && preg_match( '!\.(?:css|js)$!u', $this->mTextform ) > 0; |
| 958 | + wfRunHooks( 'TitleIsCssOrJsPage', array( $this, &$retval ) ); |
| 959 | + return $retval; |
| 960 | + } |
| 961 | + |
| 962 | + /** |
| 963 | + * Is this a .css or .js subpage of a user page? |
| 964 | + * @return Bool |
| 965 | + */ |
| 966 | + public function isCssJsSubpage() { |
| 967 | + return ( NS_USER == $this->mNamespace and preg_match( "/\\/.*\\.(?:css|js)$/", $this->mTextform ) ); |
| 968 | + } |
| 969 | + |
| 970 | + /** |
| 971 | + * Is this a *valid* .css or .js subpage of a user page? |
| 972 | + * |
| 973 | + * @return Bool |
| 974 | + * @deprecated since 1.17 |
| 975 | + */ |
| 976 | + public function isValidCssJsSubpage() { |
| 977 | + return $this->isCssJsSubpage(); |
| 978 | + } |
| 979 | + |
| 980 | + /** |
| 981 | + * Trim down a .css or .js subpage title to get the corresponding skin name |
| 982 | + * |
| 983 | + * @return string containing skin name from .css or .js subpage title |
| 984 | + */ |
| 985 | + public function getSkinFromCssJsSubpage() { |
| 986 | + $subpage = explode( '/', $this->mTextform ); |
| 987 | + $subpage = $subpage[ count( $subpage ) - 1 ]; |
| 988 | + $lastdot = strrpos( $subpage, '.' ); |
| 989 | + if ( $lastdot === false ) |
| 990 | + return $subpage; # Never happens: only called for names ending in '.css' or '.js' |
| 991 | + return substr( $subpage, 0, $lastdot ); |
| 992 | + } |
| 993 | + |
| 994 | + /** |
| 995 | + * Is this a .css subpage of a user page? |
| 996 | + * |
| 997 | + * @return Bool |
| 998 | + */ |
| 999 | + public function isCssSubpage() { |
| 1000 | + return ( NS_USER == $this->mNamespace && preg_match( "/\\/.*\\.css$/", $this->mTextform ) ); |
| 1001 | + } |
| 1002 | + |
| 1003 | + /** |
| 1004 | + * Is this a .js subpage of a user page? |
| 1005 | + * |
| 1006 | + * @return Bool |
| 1007 | + */ |
| 1008 | + public function isJsSubpage() { |
| 1009 | + return ( NS_USER == $this->mNamespace && preg_match( "/\\/.*\\.js$/", $this->mTextform ) ); |
| 1010 | + } |
| 1011 | + |
| 1012 | + /** |
| 1013 | + * Is this a talk page of some sort? |
| 1014 | + * |
| 1015 | + * @return Bool |
| 1016 | + */ |
| 1017 | + public function isTalkPage() { |
| 1018 | + return MWNamespace::isTalk( $this->getNamespace() ); |
| 1019 | + } |
| 1020 | + |
| 1021 | + /** |
| 1022 | + * Get a Title object associated with the talk page of this article |
| 1023 | + * |
| 1024 | + * @return Title the object for the talk page |
| 1025 | + */ |
| 1026 | + public function getTalkPage() { |
| 1027 | + return Title::makeTitle( MWNamespace::getTalk( $this->getNamespace() ), $this->getDBkey() ); |
| 1028 | + } |
| 1029 | + |
| 1030 | + /** |
| 1031 | + * Get a title object associated with the subject page of this |
| 1032 | + * talk page |
| 1033 | + * |
| 1034 | + * @return Title the object for the subject page |
| 1035 | + */ |
| 1036 | + public function getSubjectPage() { |
| 1037 | + // Is this the same title? |
| 1038 | + $subjectNS = MWNamespace::getSubject( $this->getNamespace() ); |
| 1039 | + if ( $this->getNamespace() == $subjectNS ) { |
| 1040 | + return $this; |
| 1041 | + } |
| 1042 | + return Title::makeTitle( $subjectNS, $this->getDBkey() ); |
| 1043 | + } |
| 1044 | + |
| 1045 | + /** |
738 | 1046 | * Get the default namespace index, for when there is no namespace |
739 | 1047 | * |
740 | 1048 | * @return Int Default namespace index |
— | — | @@ -753,6 +1061,61 @@ |
754 | 1062 | } |
755 | 1063 | |
756 | 1064 | /** |
| 1065 | + * Get the Title fragment (i.e.\ the bit after the #) in text form |
| 1066 | + * |
| 1067 | + * @return String Title fragment |
| 1068 | + */ |
| 1069 | + public function getFragment() { |
| 1070 | + return $this->mFragment; |
| 1071 | + } |
| 1072 | + |
| 1073 | + /** |
| 1074 | + * Get the fragment in URL form, including the "#" character if there is one |
| 1075 | + * @return String Fragment in URL form |
| 1076 | + */ |
| 1077 | + public function getFragmentForURL() { |
| 1078 | + if ( $this->mFragment == '' ) { |
| 1079 | + return ''; |
| 1080 | + } else { |
| 1081 | + return '#' . Title::escapeFragmentForURL( $this->mFragment ); |
| 1082 | + } |
| 1083 | + } |
| 1084 | + |
| 1085 | + /** |
| 1086 | + * Set the fragment for this title. Removes the first character from the |
| 1087 | + * specified fragment before setting, so it assumes you're passing it with |
| 1088 | + * an initial "#". |
| 1089 | + * |
| 1090 | + * Deprecated for public use, use Title::makeTitle() with fragment parameter. |
| 1091 | + * Still in active use privately. |
| 1092 | + * |
| 1093 | + * @param $fragment String text |
| 1094 | + */ |
| 1095 | + public function setFragment( $fragment ) { |
| 1096 | + $this->mFragment = str_replace( '_', ' ', substr( $fragment, 1 ) ); |
| 1097 | + } |
| 1098 | + |
| 1099 | + /** |
| 1100 | + * Prefix some arbitrary text with the namespace or interwiki prefix |
| 1101 | + * of this object |
| 1102 | + * |
| 1103 | + * @param $name String the text |
| 1104 | + * @return String the prefixed text |
| 1105 | + * @private |
| 1106 | + */ |
| 1107 | + private function prefix( $name ) { |
| 1108 | + $p = ''; |
| 1109 | + if ( $this->mInterwiki != '' ) { |
| 1110 | + $p = $this->mInterwiki . ':'; |
| 1111 | + } |
| 1112 | + |
| 1113 | + if ( 0 != $this->mNamespace ) { |
| 1114 | + $p .= $this->getNsText() . ':'; |
| 1115 | + } |
| 1116 | + return $p . $name; |
| 1117 | + } |
| 1118 | + |
| 1119 | + /** |
757 | 1120 | * Get the prefixed database key form |
758 | 1121 | * |
759 | 1122 | * @return String the prefixed title, with underscores and |
— | — | @@ -781,6 +1144,14 @@ |
782 | 1145 | } |
783 | 1146 | |
784 | 1147 | /** |
| 1148 | + * Return a string representation of this title |
| 1149 | + * |
| 1150 | + * @return String representation of this title |
| 1151 | + */ |
| 1152 | + public function __toString() { |
| 1153 | + return $this->getPrefixedText(); |
| 1154 | + } |
| 1155 | + |
785 | 1156 | /** |
786 | 1157 | * Get the prefixed title with spaces, plus any fragment |
787 | 1158 | * (part beginning with '#') |
— | — | @@ -1083,101 +1454,6 @@ |
1084 | 1455 | } |
1085 | 1456 | |
1086 | 1457 | /** |
1087 | | - * Is this page "semi-protected" - the *only* protection is autoconfirm? |
1088 | | - * |
1089 | | - * @param $action String Action to check (default: edit) |
1090 | | - * @return Bool |
1091 | | - */ |
1092 | | - public function isSemiProtected( $action = 'edit' ) { |
1093 | | - if ( $this->exists() ) { |
1094 | | - $restrictions = $this->getRestrictions( $action ); |
1095 | | - if ( count( $restrictions ) > 0 ) { |
1096 | | - foreach ( $restrictions as $restriction ) { |
1097 | | - if ( strtolower( $restriction ) != 'autoconfirmed' ) { |
1098 | | - return false; |
1099 | | - } |
1100 | | - } |
1101 | | - } else { |
1102 | | - # Not protected |
1103 | | - return false; |
1104 | | - } |
1105 | | - return true; |
1106 | | - } else { |
1107 | | - # If it doesn't exist, it can't be protected |
1108 | | - return false; |
1109 | | - } |
1110 | | - } |
1111 | | - |
1112 | | - /** |
1113 | | - * Does the title correspond to a protected article? |
1114 | | - * |
1115 | | - * @param $action String the action the page is protected from, |
1116 | | - * by default checks all actions. |
1117 | | - * @return Bool |
1118 | | - */ |
1119 | | - public function isProtected( $action = '' ) { |
1120 | | - global $wgRestrictionLevels; |
1121 | | - |
1122 | | - $restrictionTypes = $this->getRestrictionTypes(); |
1123 | | - |
1124 | | - # Special pages have inherent protection |
1125 | | - if( $this->isSpecialPage() ) { |
1126 | | - return true; |
1127 | | - } |
1128 | | - |
1129 | | - # Check regular protection levels |
1130 | | - foreach ( $restrictionTypes as $type ) { |
1131 | | - if ( $action == $type || $action == '' ) { |
1132 | | - $r = $this->getRestrictions( $type ); |
1133 | | - foreach ( $wgRestrictionLevels as $level ) { |
1134 | | - if ( in_array( $level, $r ) && $level != '' ) { |
1135 | | - return true; |
1136 | | - } |
1137 | | - } |
1138 | | - } |
1139 | | - } |
1140 | | - |
1141 | | - return false; |
1142 | | - } |
1143 | | - |
1144 | | - /** |
1145 | | - * Determines if $user is unable to edit this page because it has been protected |
1146 | | - * by $wgNamespaceProtection. |
1147 | | - * |
1148 | | - * @param $user User object to check permissions |
1149 | | - * @return Bool |
1150 | | - */ |
1151 | | - public function isNamespaceProtected( User $user ) { |
1152 | | - global $wgNamespaceProtection; |
1153 | | - |
1154 | | - if ( isset( $wgNamespaceProtection[$this->mNamespace] ) ) { |
1155 | | - foreach ( (array)$wgNamespaceProtection[$this->mNamespace] as $right ) { |
1156 | | - if ( $right != '' && !$user->isAllowed( $right ) ) { |
1157 | | - return true; |
1158 | | - } |
1159 | | - } |
1160 | | - } |
1161 | | - return false; |
1162 | | - } |
1163 | | - |
1164 | | - /** |
1165 | | - * Is this a conversion table for the LanguageConverter? |
1166 | | - * |
1167 | | - * @return Bool |
1168 | | - */ |
1169 | | - public function isConversionTable() { |
1170 | | - if( |
1171 | | - $this->getNamespace() == NS_MEDIAWIKI && |
1172 | | - strpos( $this->getText(), 'Conversiontable' ) !== false |
1173 | | - ) |
1174 | | - { |
1175 | | - return true; |
1176 | | - } |
1177 | | - |
1178 | | - return false; |
1179 | | - } |
1180 | | - |
1181 | | - /** |
1182 | 1458 | * Is $wgUser watching this page? |
1183 | 1459 | * |
1184 | 1460 | * @return Bool |
— | — | @@ -1796,6 +2072,79 @@ |
1797 | 2073 | } |
1798 | 2074 | |
1799 | 2075 | /** |
| 2076 | + * Protect css subpages of user pages: can $wgUser edit |
| 2077 | + * this page? |
| 2078 | + * |
| 2079 | + * @deprecated in 1.19; will be removed in 1.20. Use getUserPermissionsErrors() instead. |
| 2080 | + * @return Bool |
| 2081 | + */ |
| 2082 | + public function userCanEditCssSubpage() { |
| 2083 | + global $wgUser; |
| 2084 | + wfDeprecated( __METHOD__ ); |
| 2085 | + return ( ( $wgUser->isAllowedAll( 'editusercssjs', 'editusercss' ) ) |
| 2086 | + || preg_match( '/^' . preg_quote( $wgUser->getName(), '/' ) . '\//', $this->mTextform ) ); |
| 2087 | + } |
| 2088 | + |
| 2089 | + /** |
| 2090 | + * Protect js subpages of user pages: can $wgUser edit |
| 2091 | + * this page? |
| 2092 | + * |
| 2093 | + * @deprecated in 1.19; will be removed in 1.20. Use getUserPermissionsErrors() instead. |
| 2094 | + * @return Bool |
| 2095 | + */ |
| 2096 | + public function userCanEditJsSubpage() { |
| 2097 | + global $wgUser; |
| 2098 | + wfDeprecated( __METHOD__ ); |
| 2099 | + return ( ( $wgUser->isAllowedAll( 'editusercssjs', 'edituserjs' ) ) |
| 2100 | + || preg_match( '/^' . preg_quote( $wgUser->getName(), '/' ) . '\//', $this->mTextform ) ); |
| 2101 | + } |
| 2102 | + |
| 2103 | + /** |
| 2104 | + * Get a filtered list of all restriction types supported by this wiki. |
| 2105 | + * @param bool $exists True to get all restriction types that apply to |
| 2106 | + * titles that do exist, False for all restriction types that apply to |
| 2107 | + * titles that do not exist |
| 2108 | + * @return array |
| 2109 | + */ |
| 2110 | + public static function getFilteredRestrictionTypes( $exists = true ) { |
| 2111 | + global $wgRestrictionTypes; |
| 2112 | + $types = $wgRestrictionTypes; |
| 2113 | + if ( $exists ) { |
| 2114 | + # Remove the create restriction for existing titles |
| 2115 | + $types = array_diff( $types, array( 'create' ) ); |
| 2116 | + } else { |
| 2117 | + # Only the create and upload restrictions apply to non-existing titles |
| 2118 | + $types = array_intersect( $types, array( 'create', 'upload' ) ); |
| 2119 | + } |
| 2120 | + return $types; |
| 2121 | + } |
| 2122 | + |
| 2123 | + /** |
| 2124 | + * Returns restriction types for the current Title |
| 2125 | + * |
| 2126 | + * @return array applicable restriction types |
| 2127 | + */ |
| 2128 | + public function getRestrictionTypes() { |
| 2129 | + if ( $this->isSpecialPage() ) { |
| 2130 | + return array(); |
| 2131 | + } |
| 2132 | + |
| 2133 | + $types = self::getFilteredRestrictionTypes( $this->exists() ); |
| 2134 | + |
| 2135 | + if ( $this->getNamespace() != NS_FILE ) { |
| 2136 | + # Remove the upload restriction for non-file titles |
| 2137 | + $types = array_diff( $types, array( 'upload' ) ); |
| 2138 | + } |
| 2139 | + |
| 2140 | + wfRunHooks( 'TitleGetRestrictionTypes', array( $this, &$types ) ); |
| 2141 | + |
| 2142 | + wfDebug( __METHOD__ . ': applicable restrictions to [[' . |
| 2143 | + $this->getPrefixedText() . ']] are {' . implode( ',', $types ) . "}\n" ); |
| 2144 | + |
| 2145 | + return $types; |
| 2146 | + } |
| 2147 | + |
| 2148 | + /** |
1800 | 2149 | * Is this title subject to title protection? |
1801 | 2150 | * Title protection is the one applied against creation of such title. |
1802 | 2151 | * |
— | — | @@ -1905,86 +2254,57 @@ |
1906 | 2255 | } |
1907 | 2256 | |
1908 | 2257 | /** |
1909 | | - * Would anybody with sufficient privileges be able to move this page? |
1910 | | - * Some pages just aren't movable. |
| 2258 | + * Is this page "semi-protected" - the *only* protection is autoconfirm? |
1911 | 2259 | * |
1912 | | - * @return Bool TRUE or FALSE |
| 2260 | + * @param $action String Action to check (default: edit) |
| 2261 | + * @return Bool |
1913 | 2262 | */ |
1914 | | - public function isMovable() { |
1915 | | - if ( !MWNamespace::isMovable( $this->getNamespace() ) || $this->getInterwiki() != '' ) { |
1916 | | - // Interwiki title or immovable namespace. Hooks don't get to override here |
| 2263 | + public function isSemiProtected( $action = 'edit' ) { |
| 2264 | + if ( $this->exists() ) { |
| 2265 | + $restrictions = $this->getRestrictions( $action ); |
| 2266 | + if ( count( $restrictions ) > 0 ) { |
| 2267 | + foreach ( $restrictions as $restriction ) { |
| 2268 | + if ( strtolower( $restriction ) != 'autoconfirmed' ) { |
| 2269 | + return false; |
| 2270 | + } |
| 2271 | + } |
| 2272 | + } else { |
| 2273 | + # Not protected |
| 2274 | + return false; |
| 2275 | + } |
| 2276 | + return true; |
| 2277 | + } else { |
| 2278 | + # If it doesn't exist, it can't be protected |
1917 | 2279 | return false; |
1918 | 2280 | } |
1919 | | - |
1920 | | - $result = true; |
1921 | | - wfRunHooks( 'TitleIsMovable', array( $this, &$result ) ); |
1922 | | - return $result; |
1923 | 2281 | } |
1924 | 2282 | |
1925 | 2283 | /** |
1926 | | - * Is this the mainpage? |
1927 | | - * @note Title::newFromText seams to be sufficiently optimized by the title |
1928 | | - * cache that we don't need to over-optimize by doing direct comparisons and |
1929 | | - * acidentally creating new bugs where $title->equals( Title::newFromText() ) |
1930 | | - * ends up reporting something differently than $title->isMainPage(); |
| 2284 | + * Does the title correspond to a protected article? |
1931 | 2285 | * |
1932 | | - * @since 1.18 |
| 2286 | + * @param $action String the action the page is protected from, |
| 2287 | + * by default checks all actions. |
1933 | 2288 | * @return Bool |
1934 | 2289 | */ |
1935 | | - public function isMainPage() { |
1936 | | - return $this->equals( Title::newMainPage() ); |
1937 | | - } |
| 2290 | + public function isProtected( $action = '' ) { |
| 2291 | + global $wgRestrictionLevels; |
1938 | 2292 | |
1939 | | - /** |
1940 | | - * Is this a talk page of some sort? |
1941 | | - * |
1942 | | - * @return Bool |
1943 | | - */ |
1944 | | - public function isTalkPage() { |
1945 | | - return MWNamespace::isTalk( $this->getNamespace() ); |
1946 | | - } |
| 2293 | + $restrictionTypes = $this->getRestrictionTypes(); |
1947 | 2294 | |
1948 | | - /** |
1949 | | - * Is this a subpage? |
1950 | | - * |
1951 | | - * @return Bool |
1952 | | - */ |
1953 | | - public function isSubpage() { |
1954 | | - return MWNamespace::hasSubpages( $this->mNamespace ) |
1955 | | - ? strpos( $this->getText(), '/' ) !== false |
1956 | | - : false; |
1957 | | - } |
1958 | | - |
1959 | | - /** |
1960 | | - * Returns true if the title is inside the specified namespace. |
1961 | | - * |
1962 | | - * Please make use of this instead of comparing to getNamespace() |
1963 | | - * This function is much more resistant to changes we may make |
1964 | | - * to namespaces than code that makes direct comparisons. |
1965 | | - * @param $ns int The namespace |
1966 | | - * @return bool |
1967 | | - * @since 1.19 |
1968 | | - */ |
1969 | | - public function inNamespace( $ns ) { |
1970 | | - return MWNamespace::equals( $this->getNamespace(), $ns ); |
1971 | | - } |
1972 | | - |
1973 | | - /** |
1974 | | - * Returns true if the title is inside one of the specified namespaces. |
1975 | | - * |
1976 | | - * @param ...$namespaces The namespaces to check for |
1977 | | - * @return bool |
1978 | | - * @since 1.19 |
1979 | | - */ |
1980 | | - public function inNamespaces( /* ... */ ) { |
1981 | | - $namespaces = func_get_args(); |
1982 | | - if ( count( $namespaces ) > 0 && is_array( $namespaces[0] ) ) { |
1983 | | - $namespaces = $namespaces[0]; |
| 2295 | + # Special pages have inherent protection |
| 2296 | + if( $this->isSpecialPage() ) { |
| 2297 | + return true; |
1984 | 2298 | } |
1985 | 2299 | |
1986 | | - foreach ( $namespaces as $ns ) { |
1987 | | - if ( $this->inNamespace( $ns ) ) { |
1988 | | - return true; |
| 2300 | + # Check regular protection levels |
| 2301 | + foreach ( $restrictionTypes as $type ) { |
| 2302 | + if ( $action == $type || $action == '' ) { |
| 2303 | + $r = $this->getRestrictions( $type ); |
| 2304 | + foreach ( $wgRestrictionLevels as $level ) { |
| 2305 | + if ( in_array( $level, $r ) && $level != '' ) { |
| 2306 | + return true; |
| 2307 | + } |
| 2308 | + } |
1989 | 2309 | } |
1990 | 2310 | } |
1991 | 2311 | |
— | — | @@ -1992,178 +2312,26 @@ |
1993 | 2313 | } |
1994 | 2314 | |
1995 | 2315 | /** |
1996 | | - * Returns true if the title has the same subject namespace as the |
1997 | | - * namespace specified. |
1998 | | - * For example this method will take NS_USER and return true if namespace |
1999 | | - * is either NS_USER or NS_USER_TALK since both of them have NS_USER |
2000 | | - * as their subject namespace. |
| 2316 | + * Determines if $user is unable to edit this page because it has been protected |
| 2317 | + * by $wgNamespaceProtection. |
2001 | 2318 | * |
2002 | | - * This is MUCH simpler than individually testing for equivilance |
2003 | | - * against both NS_USER and NS_USER_TALK, and is also forward compatible. |
2004 | | - * @since 1.19 |
2005 | | - */ |
2006 | | - public function hasSubjectNamespace( $ns ) { |
2007 | | - return MWNamespace::subjectEquals( $this->getNamespace(), $ns ); |
2008 | | - } |
2009 | | - |
2010 | | - /** |
2011 | | - * Does this have subpages? (Warning, usually requires an extra DB query.) |
2012 | | - * |
| 2319 | + * @param $user User object to check permissions |
2013 | 2320 | * @return Bool |
2014 | 2321 | */ |
2015 | | - public function hasSubpages() { |
2016 | | - if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) { |
2017 | | - # Duh |
2018 | | - return false; |
2019 | | - } |
| 2322 | + public function isNamespaceProtected( User $user ) { |
| 2323 | + global $wgNamespaceProtection; |
2020 | 2324 | |
2021 | | - # We dynamically add a member variable for the purpose of this method |
2022 | | - # alone to cache the result. There's no point in having it hanging |
2023 | | - # around uninitialized in every Title object; therefore we only add it |
2024 | | - # if needed and don't declare it statically. |
2025 | | - if ( isset( $this->mHasSubpages ) ) { |
2026 | | - return $this->mHasSubpages; |
| 2325 | + if ( isset( $wgNamespaceProtection[$this->mNamespace] ) ) { |
| 2326 | + foreach ( (array)$wgNamespaceProtection[$this->mNamespace] as $right ) { |
| 2327 | + if ( $right != '' && !$user->isAllowed( $right ) ) { |
| 2328 | + return true; |
| 2329 | + } |
| 2330 | + } |
2027 | 2331 | } |
2028 | | - |
2029 | | - $subpages = $this->getSubpages( 1 ); |
2030 | | - if ( $subpages instanceof TitleArray ) { |
2031 | | - return $this->mHasSubpages = (bool)$subpages->count(); |
2032 | | - } |
2033 | | - return $this->mHasSubpages = false; |
| 2332 | + return false; |
2034 | 2333 | } |
2035 | 2334 | |
2036 | 2335 | /** |
2037 | | - * Get all subpages of this page. |
2038 | | - * |
2039 | | - * @param $limit Int maximum number of subpages to fetch; -1 for no limit |
2040 | | - * @return mixed TitleArray, or empty array if this page's namespace |
2041 | | - * doesn't allow subpages |
2042 | | - */ |
2043 | | - public function getSubpages( $limit = -1 ) { |
2044 | | - if ( !MWNamespace::hasSubpages( $this->getNamespace() ) ) { |
2045 | | - return array(); |
2046 | | - } |
2047 | | - |
2048 | | - $dbr = wfGetDB( DB_SLAVE ); |
2049 | | - $conds['page_namespace'] = $this->getNamespace(); |
2050 | | - $conds[] = 'page_title ' . $dbr->buildLike( $this->getDBkey() . '/', $dbr->anyString() ); |
2051 | | - $options = array(); |
2052 | | - if ( $limit > -1 ) { |
2053 | | - $options['LIMIT'] = $limit; |
2054 | | - } |
2055 | | - return $this->mSubpages = TitleArray::newFromResult( |
2056 | | - $dbr->select( 'page', |
2057 | | - array( 'page_id', 'page_namespace', 'page_title', 'page_is_redirect' ), |
2058 | | - $conds, |
2059 | | - __METHOD__, |
2060 | | - $options |
2061 | | - ) |
2062 | | - ); |
2063 | | - } |
2064 | | - |
2065 | | - /** |
2066 | | - * Does that page contain wikitext, or it is JS, CSS or whatever? |
2067 | | - * |
2068 | | - * @return Bool |
2069 | | - */ |
2070 | | - public function isWikitextPage() { |
2071 | | - $retval = !$this->isCssOrJsPage() && !$this->isCssJsSubpage(); |
2072 | | - wfRunHooks( 'TitleIsWikitextPage', array( $this, &$retval ) ); |
2073 | | - return $retval; |
2074 | | - } |
2075 | | - |
2076 | | - /** |
2077 | | - * Could this page contain custom CSS or JavaScript, based |
2078 | | - * on the title? |
2079 | | - * |
2080 | | - * @return Bool |
2081 | | - */ |
2082 | | - public function isCssOrJsPage() { |
2083 | | - $retval = $this->mNamespace == NS_MEDIAWIKI |
2084 | | - && preg_match( '!\.(?:css|js)$!u', $this->mTextform ) > 0; |
2085 | | - wfRunHooks( 'TitleIsCssOrJsPage', array( $this, &$retval ) ); |
2086 | | - return $retval; |
2087 | | - } |
2088 | | - |
2089 | | - /** |
2090 | | - * Is this a .css or .js subpage of a user page? |
2091 | | - * @return Bool |
2092 | | - */ |
2093 | | - public function isCssJsSubpage() { |
2094 | | - return ( NS_USER == $this->mNamespace and preg_match( "/\\/.*\\.(?:css|js)$/", $this->mTextform ) ); |
2095 | | - } |
2096 | | - |
2097 | | - /** |
2098 | | - * Is this a *valid* .css or .js subpage of a user page? |
2099 | | - * |
2100 | | - * @return Bool |
2101 | | - * @deprecated since 1.17 |
2102 | | - */ |
2103 | | - public function isValidCssJsSubpage() { |
2104 | | - return $this->isCssJsSubpage(); |
2105 | | - } |
2106 | | - |
2107 | | - /** |
2108 | | - * Trim down a .css or .js subpage title to get the corresponding skin name |
2109 | | - * |
2110 | | - * @return string containing skin name from .css or .js subpage title |
2111 | | - */ |
2112 | | - public function getSkinFromCssJsSubpage() { |
2113 | | - $subpage = explode( '/', $this->mTextform ); |
2114 | | - $subpage = $subpage[ count( $subpage ) - 1 ]; |
2115 | | - $lastdot = strrpos( $subpage, '.' ); |
2116 | | - if ( $lastdot === false ) |
2117 | | - return $subpage; # Never happens: only called for names ending in '.css' or '.js' |
2118 | | - return substr( $subpage, 0, $lastdot ); |
2119 | | - } |
2120 | | - |
2121 | | - /** |
2122 | | - * Is this a .css subpage of a user page? |
2123 | | - * |
2124 | | - * @return Bool |
2125 | | - */ |
2126 | | - public function isCssSubpage() { |
2127 | | - return ( NS_USER == $this->mNamespace && preg_match( "/\\/.*\\.css$/", $this->mTextform ) ); |
2128 | | - } |
2129 | | - |
2130 | | - /** |
2131 | | - * Is this a .js subpage of a user page? |
2132 | | - * |
2133 | | - * @return Bool |
2134 | | - */ |
2135 | | - public function isJsSubpage() { |
2136 | | - return ( NS_USER == $this->mNamespace && preg_match( "/\\/.*\\.js$/", $this->mTextform ) ); |
2137 | | - } |
2138 | | - |
2139 | | - /** |
2140 | | - * Protect css subpages of user pages: can $wgUser edit |
2141 | | - * this page? |
2142 | | - * |
2143 | | - * @deprecated in 1.19; will be removed in 1.20. Use getUserPermissionsErrors() instead. |
2144 | | - * @return Bool |
2145 | | - */ |
2146 | | - public function userCanEditCssSubpage() { |
2147 | | - global $wgUser; |
2148 | | - wfDeprecated( __METHOD__ ); |
2149 | | - return ( ( $wgUser->isAllowedAll( 'editusercssjs', 'editusercss' ) ) |
2150 | | - || preg_match( '/^' . preg_quote( $wgUser->getName(), '/' ) . '\//', $this->mTextform ) ); |
2151 | | - } |
2152 | | - |
2153 | | - /** |
2154 | | - * Protect js subpages of user pages: can $wgUser edit |
2155 | | - * this page? |
2156 | | - * |
2157 | | - * @deprecated in 1.19; will be removed in 1.20. Use getUserPermissionsErrors() instead. |
2158 | | - * @return Bool |
2159 | | - */ |
2160 | | - public function userCanEditJsSubpage() { |
2161 | | - global $wgUser; |
2162 | | - wfDeprecated( __METHOD__ ); |
2163 | | - return ( ( $wgUser->isAllowedAll( 'editusercssjs', 'edituserjs' ) ) |
2164 | | - || preg_match( '/^' . preg_quote( $wgUser->getName(), '/' ) . '\//', $this->mTextform ) ); |
2165 | | - } |
2166 | | - |
2167 | | - /** |
2168 | 2336 | * Cascading protection: Return true if cascading restrictions apply to this page, false if not. |
2169 | 2337 | * |
2170 | 2338 | * @return Bool If the page is subject to cascading restrictions. |
— | — | @@ -2272,6 +2440,34 @@ |
2273 | 2441 | } |
2274 | 2442 | |
2275 | 2443 | /** |
| 2444 | + * Accessor/initialisation for mRestrictions |
| 2445 | + * |
| 2446 | + * @param $action String action that permission needs to be checked for |
| 2447 | + * @return Array of Strings the array of groups allowed to edit this article |
| 2448 | + */ |
| 2449 | + public function getRestrictions( $action ) { |
| 2450 | + if ( !$this->mRestrictionsLoaded ) { |
| 2451 | + $this->loadRestrictions(); |
| 2452 | + } |
| 2453 | + return isset( $this->mRestrictions[$action] ) |
| 2454 | + ? $this->mRestrictions[$action] |
| 2455 | + : array(); |
| 2456 | + } |
| 2457 | + |
| 2458 | + /** |
| 2459 | + * Get the expiry time for the restriction against a given action |
| 2460 | + * |
| 2461 | + * @return String|Bool 14-char timestamp, or 'infinity' if the page is protected forever |
| 2462 | + * or not protected at all, or false if the action is not recognised. |
| 2463 | + */ |
| 2464 | + public function getRestrictionExpiry( $action ) { |
| 2465 | + if ( !$this->mRestrictionsLoaded ) { |
| 2466 | + $this->loadRestrictions(); |
| 2467 | + } |
| 2468 | + return isset( $this->mRestrictionsExpiry[$action] ) ? $this->mRestrictionsExpiry[$action] : false; |
| 2469 | + } |
| 2470 | + |
| 2471 | + /** |
2276 | 2472 | * Returns cascading restrictions for the current article |
2277 | 2473 | * |
2278 | 2474 | * @return Boolean |
— | — | @@ -2445,31 +2641,58 @@ |
2446 | 2642 | } |
2447 | 2643 | |
2448 | 2644 | /** |
2449 | | - * Accessor/initialisation for mRestrictions |
| 2645 | + * Does this have subpages? (Warning, usually requires an extra DB query.) |
2450 | 2646 | * |
2451 | | - * @param $action String action that permission needs to be checked for |
2452 | | - * @return Array of Strings the array of groups allowed to edit this article |
| 2647 | + * @return Bool |
2453 | 2648 | */ |
2454 | | - public function getRestrictions( $action ) { |
2455 | | - if ( !$this->mRestrictionsLoaded ) { |
2456 | | - $this->loadRestrictions(); |
| 2649 | + public function hasSubpages() { |
| 2650 | + if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) { |
| 2651 | + # Duh |
| 2652 | + return false; |
2457 | 2653 | } |
2458 | | - return isset( $this->mRestrictions[$action] ) |
2459 | | - ? $this->mRestrictions[$action] |
2460 | | - : array(); |
| 2654 | + |
| 2655 | + # We dynamically add a member variable for the purpose of this method |
| 2656 | + # alone to cache the result. There's no point in having it hanging |
| 2657 | + # around uninitialized in every Title object; therefore we only add it |
| 2658 | + # if needed and don't declare it statically. |
| 2659 | + if ( isset( $this->mHasSubpages ) ) { |
| 2660 | + return $this->mHasSubpages; |
| 2661 | + } |
| 2662 | + |
| 2663 | + $subpages = $this->getSubpages( 1 ); |
| 2664 | + if ( $subpages instanceof TitleArray ) { |
| 2665 | + return $this->mHasSubpages = (bool)$subpages->count(); |
| 2666 | + } |
| 2667 | + return $this->mHasSubpages = false; |
2461 | 2668 | } |
2462 | 2669 | |
2463 | 2670 | /** |
2464 | | - * Get the expiry time for the restriction against a given action |
| 2671 | + * Get all subpages of this page. |
2465 | 2672 | * |
2466 | | - * @return String|Bool 14-char timestamp, or 'infinity' if the page is protected forever |
2467 | | - * or not protected at all, or false if the action is not recognised. |
| 2673 | + * @param $limit Int maximum number of subpages to fetch; -1 for no limit |
| 2674 | + * @return mixed TitleArray, or empty array if this page's namespace |
| 2675 | + * doesn't allow subpages |
2468 | 2676 | */ |
2469 | | - public function getRestrictionExpiry( $action ) { |
2470 | | - if ( !$this->mRestrictionsLoaded ) { |
2471 | | - $this->loadRestrictions(); |
| 2677 | + public function getSubpages( $limit = -1 ) { |
| 2678 | + if ( !MWNamespace::hasSubpages( $this->getNamespace() ) ) { |
| 2679 | + return array(); |
2472 | 2680 | } |
2473 | | - return isset( $this->mRestrictionsExpiry[$action] ) ? $this->mRestrictionsExpiry[$action] : false; |
| 2681 | + |
| 2682 | + $dbr = wfGetDB( DB_SLAVE ); |
| 2683 | + $conds['page_namespace'] = $this->getNamespace(); |
| 2684 | + $conds[] = 'page_title ' . $dbr->buildLike( $this->getDBkey() . '/', $dbr->anyString() ); |
| 2685 | + $options = array(); |
| 2686 | + if ( $limit > -1 ) { |
| 2687 | + $options['LIMIT'] = $limit; |
| 2688 | + } |
| 2689 | + return $this->mSubpages = TitleArray::newFromResult( |
| 2690 | + $dbr->select( 'page', |
| 2691 | + array( 'page_id', 'page_namespace', 'page_title', 'page_is_redirect' ), |
| 2692 | + $conds, |
| 2693 | + __METHOD__, |
| 2694 | + $options |
| 2695 | + ) |
| 2696 | + ); |
2474 | 2697 | } |
2475 | 2698 | |
2476 | 2699 | /** |
— | — | @@ -2658,73 +2881,6 @@ |
2659 | 2882 | } |
2660 | 2883 | |
2661 | 2884 | /** |
2662 | | - * Updates page_touched for this page; called from LinksUpdate.php |
2663 | | - * |
2664 | | - * @return Bool true if the update succeded |
2665 | | - */ |
2666 | | - public function invalidateCache() { |
2667 | | - if ( wfReadOnly() ) { |
2668 | | - return false; |
2669 | | - } |
2670 | | - $dbw = wfGetDB( DB_MASTER ); |
2671 | | - $success = $dbw->update( |
2672 | | - 'page', |
2673 | | - array( 'page_touched' => $dbw->timestamp() ), |
2674 | | - $this->pageCond(), |
2675 | | - __METHOD__ |
2676 | | - ); |
2677 | | - HTMLFileCache::clearFileCache( $this ); |
2678 | | - return $success; |
2679 | | - } |
2680 | | - |
2681 | | - /** |
2682 | | - * Prefix some arbitrary text with the namespace or interwiki prefix |
2683 | | - * of this object |
2684 | | - * |
2685 | | - * @param $name String the text |
2686 | | - * @return String the prefixed text |
2687 | | - * @private |
2688 | | - */ |
2689 | | - private function prefix( $name ) { |
2690 | | - $p = ''; |
2691 | | - if ( $this->mInterwiki != '' ) { |
2692 | | - $p = $this->mInterwiki . ':'; |
2693 | | - } |
2694 | | - |
2695 | | - if ( 0 != $this->mNamespace ) { |
2696 | | - $p .= $this->getNsText() . ':'; |
2697 | | - } |
2698 | | - return $p . $name; |
2699 | | - } |
2700 | | - |
2701 | | - /** |
2702 | | - * Returns a simple regex that will match on characters and sequences invalid in titles. |
2703 | | - * Note that this doesn't pick up many things that could be wrong with titles, but that |
2704 | | - * replacing this regex with something valid will make many titles valid. |
2705 | | - * |
2706 | | - * @return String regex string |
2707 | | - */ |
2708 | | - static function getTitleInvalidRegex() { |
2709 | | - static $rxTc = false; |
2710 | | - if ( !$rxTc ) { |
2711 | | - # Matching titles will be held as illegal. |
2712 | | - $rxTc = '/' . |
2713 | | - # Any character not allowed is forbidden... |
2714 | | - '[^' . Title::legalChars() . ']' . |
2715 | | - # URL percent encoding sequences interfere with the ability |
2716 | | - # to round-trip titles -- you can't link to them consistently. |
2717 | | - '|%[0-9A-Fa-f]{2}' . |
2718 | | - # XML/HTML character references produce similar issues. |
2719 | | - '|&[A-Za-z0-9\x80-\xff]+;' . |
2720 | | - '|&#[0-9]+;' . |
2721 | | - '|&#x[0-9A-Fa-f]+;' . |
2722 | | - '/S'; |
2723 | | - } |
2724 | | - |
2725 | | - return $rxTc; |
2726 | | - } |
2727 | | - |
2728 | | - /** |
2729 | 2885 | * Capitalize a text string for a title if it belongs to a namespace that capitalizes |
2730 | 2886 | * |
2731 | 2887 | * @param $text String containing title to capitalize |
— | — | @@ -2939,44 +3095,6 @@ |
2940 | 3096 | } |
2941 | 3097 | |
2942 | 3098 | /** |
2943 | | - * Set the fragment for this title. Removes the first character from the |
2944 | | - * specified fragment before setting, so it assumes you're passing it with |
2945 | | - * an initial "#". |
2946 | | - * |
2947 | | - * Deprecated for public use, use Title::makeTitle() with fragment parameter. |
2948 | | - * Still in active use privately. |
2949 | | - * |
2950 | | - * @param $fragment String text |
2951 | | - */ |
2952 | | - public function setFragment( $fragment ) { |
2953 | | - $this->mFragment = str_replace( '_', ' ', substr( $fragment, 1 ) ); |
2954 | | - } |
2955 | | - |
2956 | | - /** |
2957 | | - * Get a Title object associated with the talk page of this article |
2958 | | - * |
2959 | | - * @return Title the object for the talk page |
2960 | | - */ |
2961 | | - public function getTalkPage() { |
2962 | | - return Title::makeTitle( MWNamespace::getTalk( $this->getNamespace() ), $this->getDBkey() ); |
2963 | | - } |
2964 | | - |
2965 | | - /** |
2966 | | - * Get a title object associated with the subject page of this |
2967 | | - * talk page |
2968 | | - * |
2969 | | - * @return Title the object for the subject page |
2970 | | - */ |
2971 | | - public function getSubjectPage() { |
2972 | | - // Is this the same title? |
2973 | | - $subjectNS = MWNamespace::getSubject( $this->getNamespace() ); |
2974 | | - if ( $this->getNamespace() == $subjectNS ) { |
2975 | | - return $this; |
2976 | | - } |
2977 | | - return Title::makeTitle( $subjectNS, $this->getDBkey() ); |
2978 | | - } |
2979 | | - |
2980 | | - /** |
2981 | 3099 | * Get an array of Title objects linking to this Title |
2982 | 3100 | * Also stores the IDs in the link cache. |
2983 | 3101 | * |
— | — | @@ -3714,15 +3832,6 @@ |
3715 | 3833 | } |
3716 | 3834 | |
3717 | 3835 | /** |
3718 | | - * Can this title be added to a user's watchlist? |
3719 | | - * |
3720 | | - * @return Bool TRUE or FALSE |
3721 | | - */ |
3722 | | - public function isWatchable() { |
3723 | | - return !$this->isExternal() && MWNamespace::isWatchable( $this->getNamespace() ); |
3724 | | - } |
3725 | | - |
3726 | | - /** |
3727 | 3836 | * Get categories to which this Title belongs and return an array of |
3728 | 3837 | * categories' names. |
3729 | 3838 | * |
— | — | @@ -3968,31 +4077,6 @@ |
3969 | 4078 | } |
3970 | 4079 | |
3971 | 4080 | /** |
3972 | | - * Callback for usort() to do title sorts by (namespace, title) |
3973 | | - * |
3974 | | - * @param $a Title |
3975 | | - * @param $b Title |
3976 | | - * |
3977 | | - * @return Integer: result of string comparison, or namespace comparison |
3978 | | - */ |
3979 | | - public static function compare( $a, $b ) { |
3980 | | - if ( $a->getNamespace() == $b->getNamespace() ) { |
3981 | | - return strcmp( $a->getText(), $b->getText() ); |
3982 | | - } else { |
3983 | | - return $a->getNamespace() - $b->getNamespace(); |
3984 | | - } |
3985 | | - } |
3986 | | - |
3987 | | - /** |
3988 | | - * Return a string representation of this title |
3989 | | - * |
3990 | | - * @return String representation of this title |
3991 | | - */ |
3992 | | - public function __toString() { |
3993 | | - return $this->getPrefixedText(); |
3994 | | - } |
3995 | | - |
3996 | | - /** |
3997 | 4081 | * Check if page exists. For historical reasons, this function simply |
3998 | 4082 | * checks for the existence of the title in the page table, and will |
3999 | 4083 | * thus return false for interwiki links, special pages and the like. |
— | — | @@ -4103,13 +4187,23 @@ |
4104 | 4188 | } |
4105 | 4189 | |
4106 | 4190 | /** |
4107 | | - * Is this in a namespace that allows actual pages? |
| 4191 | + * Updates page_touched for this page; called from LinksUpdate.php |
4108 | 4192 | * |
4109 | | - * @return Bool |
4110 | | - * @internal note -- uses hardcoded namespace index instead of constants |
| 4193 | + * @return Bool true if the update succeded |
4111 | 4194 | */ |
4112 | | - public function canExist() { |
4113 | | - return $this->mNamespace >= 0 && $this->mNamespace != NS_MEDIA; |
| 4195 | + public function invalidateCache() { |
| 4196 | + if ( wfReadOnly() ) { |
| 4197 | + return false; |
| 4198 | + } |
| 4199 | + $dbw = wfGetDB( DB_MASTER ); |
| 4200 | + $success = $dbw->update( |
| 4201 | + 'page', |
| 4202 | + array( 'page_touched' => $dbw->timestamp() ), |
| 4203 | + $this->pageCond(), |
| 4204 | + __METHOD__ |
| 4205 | + ); |
| 4206 | + HTMLFileCache::clearFileCache( $this ); |
| 4207 | + return $success; |
4114 | 4208 | } |
4115 | 4209 | |
4116 | 4210 | /** |
— | — | @@ -4208,61 +4302,6 @@ |
4209 | 4303 | } |
4210 | 4304 | |
4211 | 4305 | /** |
4212 | | - * Returns true if this is a special page. |
4213 | | - * |
4214 | | - * @return boolean |
4215 | | - */ |
4216 | | - public function isSpecialPage() { |
4217 | | - return $this->getNamespace() == NS_SPECIAL; |
4218 | | - } |
4219 | | - |
4220 | | - /** |
4221 | | - * Returns true if this title resolves to the named special page |
4222 | | - * |
4223 | | - * @param $name String The special page name |
4224 | | - * @return boolean |
4225 | | - */ |
4226 | | - public function isSpecial( $name ) { |
4227 | | - if ( $this->isSpecialPage() ) { |
4228 | | - list( $thisName, /* $subpage */ ) = SpecialPageFactory::resolveAlias( $this->getDBkey() ); |
4229 | | - if ( $name == $thisName ) { |
4230 | | - return true; |
4231 | | - } |
4232 | | - } |
4233 | | - return false; |
4234 | | - } |
4235 | | - |
4236 | | - /** |
4237 | | - * If the Title refers to a special page alias which is not the local default, resolve |
4238 | | - * the alias, and localise the name as necessary. Otherwise, return $this |
4239 | | - * |
4240 | | - * @return Title |
4241 | | - */ |
4242 | | - public function fixSpecialName() { |
4243 | | - if ( $this->isSpecialPage() ) { |
4244 | | - list( $canonicalName, $par ) = SpecialPageFactory::resolveAlias( $this->mDbkeyform ); |
4245 | | - if ( $canonicalName ) { |
4246 | | - $localName = SpecialPageFactory::getLocalNameFor( $canonicalName, $par ); |
4247 | | - if ( $localName != $this->mDbkeyform ) { |
4248 | | - return Title::makeTitle( NS_SPECIAL, $localName ); |
4249 | | - } |
4250 | | - } |
4251 | | - } |
4252 | | - return $this; |
4253 | | - } |
4254 | | - |
4255 | | - /** |
4256 | | - * Is this Title in a namespace which contains content? |
4257 | | - * In other words, is this a content page, for the purposes of calculating |
4258 | | - * statistics, etc? |
4259 | | - * |
4260 | | - * @return Boolean |
4261 | | - */ |
4262 | | - public function isContentPage() { |
4263 | | - return MWNamespace::isContent( $this->getNamespace() ); |
4264 | | - } |
4265 | | - |
4266 | | - /** |
4267 | 4306 | * Get all extant redirects to this Title |
4268 | 4307 | * |
4269 | 4308 | * @param $ns Int|Null Single namespace to consider; NULL to consider all namespaces |
— | — | @@ -4345,50 +4384,6 @@ |
4346 | 4385 | } |
4347 | 4386 | |
4348 | 4387 | /** |
4349 | | - * Returns restriction types for the current Title |
4350 | | - * |
4351 | | - * @return array applicable restriction types |
4352 | | - */ |
4353 | | - public function getRestrictionTypes() { |
4354 | | - if ( $this->isSpecialPage() ) { |
4355 | | - return array(); |
4356 | | - } |
4357 | | - |
4358 | | - $types = self::getFilteredRestrictionTypes( $this->exists() ); |
4359 | | - |
4360 | | - if ( $this->getNamespace() != NS_FILE ) { |
4361 | | - # Remove the upload restriction for non-file titles |
4362 | | - $types = array_diff( $types, array( 'upload' ) ); |
4363 | | - } |
4364 | | - |
4365 | | - wfRunHooks( 'TitleGetRestrictionTypes', array( $this, &$types ) ); |
4366 | | - |
4367 | | - wfDebug( __METHOD__ . ': applicable restrictions to [[' . |
4368 | | - $this->getPrefixedText() . ']] are {' . implode( ',', $types ) . "}\n" ); |
4369 | | - |
4370 | | - return $types; |
4371 | | - } |
4372 | | - /** |
4373 | | - * Get a filtered list of all restriction types supported by this wiki. |
4374 | | - * @param bool $exists True to get all restriction types that apply to |
4375 | | - * titles that do exist, False for all restriction types that apply to |
4376 | | - * titles that do not exist |
4377 | | - * @return array |
4378 | | - */ |
4379 | | - public static function getFilteredRestrictionTypes( $exists = true ) { |
4380 | | - global $wgRestrictionTypes; |
4381 | | - $types = $wgRestrictionTypes; |
4382 | | - if ( $exists ) { |
4383 | | - # Remove the create restriction for existing titles |
4384 | | - $types = array_diff( $types, array( 'create' ) ); |
4385 | | - } else { |
4386 | | - # Only the create and upload restrictions apply to non-existing titles |
4387 | | - $types = array_intersect( $types, array( 'create', 'upload' ) ); |
4388 | | - } |
4389 | | - return $types; |
4390 | | - } |
4391 | | - |
4392 | | - /** |
4393 | 4388 | * Returns the raw sort key to be used for categories, with the specified |
4394 | 4389 | * prefix. This will be fed to Collation::getSortKey() to get a |
4395 | 4390 | * binary sortkey that can be used for actual sorting. |