Index: branches/wikidata/maintenance/archives/patch-namespace.sql |
— | — | @@ -0,0 +1,15 @@ |
| 2 | +-- New namespace system |
| 3 | + |
| 4 | +DROP TABLE IF EXISTS /*$wgDBprefix*/namespace; |
| 5 | +CREATE TABLE /*$wgDBprefix*/namespace ( |
| 6 | + `ns_id` int(8) NOT NULL default '0', |
| 7 | + `ns_system` varchar(80) default '', |
| 8 | + `ns_subpages` tinyint(1) NOT NULL default '0', |
| 9 | + `ns_search_default` tinyint(1) NOT NULL default '0', |
| 10 | + `ns_target` varchar(200) default NULL, |
| 11 | + `ns_parent` int(8) default NULL, |
| 12 | + `ns_hidden` tinyint(1) default NULL, |
| 13 | + `ns_count` tinyint(1) default NULL, |
| 14 | + `ns_class` varchar(255) default '', |
| 15 | + PRIMARY KEY (`ns_id`) |
| 16 | +) TYPE=InnoDB; |
Property changes on: branches/wikidata/maintenance/archives/patch-namespace.sql |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 17 | + native |
Index: branches/wikidata/maintenance/archives/patch-namespace_names.sql |
— | — | @@ -0,0 +1,9 @@ |
| 2 | +-- New namespace system |
| 3 | + |
| 4 | +DROP TABLE IF EXISTS /*$wgDBprefix*/namespace_names; |
| 5 | +CREATE TABLE /*$wgDBprefix*/namespace_names ( |
| 6 | + `ns_id` int(8) NOT NULL default '0', |
| 7 | + `ns_name` varchar(200) NOT NULL default '', |
| 8 | + `ns_default` tinyint(1) NOT NULL default '0', |
| 9 | + `ns_canonical` tinyint(1) default NULL |
| 10 | +) TYPE=InnoDB; |
Property changes on: branches/wikidata/maintenance/archives/patch-namespace_names.sql |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 11 | + native |
Index: branches/wikidata/maintenance/nsBootstrap.php |
— | — | @@ -0,0 +1,233 @@ |
| 2 | +<?php |
| 3 | +// Purpose: Create entries in the namespace and namespace_names tables, |
| 4 | +// based on the configured language. Take into account existing (deprecated) |
| 5 | +// namespace settings in an update. |
| 6 | +// |
| 7 | +// We can't depend on commandLine.inc because this has to be runnable from the installer |
| 8 | + |
| 9 | + |
| 10 | +global $wgLanguageCode; |
| 11 | +$wgContLanguageCode = $wgLanguageCode; |
| 12 | +$wgContLangClass = 'Language' . str_replace( '-', '_', ucfirst( $wgContLanguageCode ) ); |
| 13 | + |
| 14 | +$wgContLang = new StubContLang; |
| 15 | +$wgContLang->initEncoding(); |
| 16 | + |
| 17 | +class NamespaceBootstrap { |
| 18 | + var $mStdNs; |
| 19 | + var $mExtraNs; |
| 20 | + var $dbw; |
| 21 | + |
| 22 | + var $mContLangNs; |
| 23 | + var $mContLangNsSynonyms; |
| 24 | + var $mLanguageCode; |
| 25 | + |
| 26 | + function NamespaceBootstrap() { |
| 27 | + global $wgExtraNamespaces, $wgContLang, $wgNamespaceSynonymsEn; |
| 28 | + |
| 29 | + $this->mStdNs = array( |
| 30 | + 'NS_MEDIA' => NS_MEDIA, |
| 31 | + 'NS_SPECIAL' => NS_SPECIAL, |
| 32 | + 'NS_MAIN' => NS_MAIN, |
| 33 | + 'NS_MAIN' => NS_MAIN, |
| 34 | + 'NS_TALK' => NS_TALK, |
| 35 | + 'NS_USER' => NS_USER, |
| 36 | + 'NS_USER_TALK' => NS_USER_TALK, |
| 37 | + 'NS_PROJECT' => NS_PROJECT, |
| 38 | + 'NS_PROJECT_TALK' => NS_PROJECT_TALK, |
| 39 | + 'NS_IMAGE' => NS_IMAGE, |
| 40 | + 'NS_IMAGE_TALK' => NS_IMAGE_TALK, |
| 41 | + 'NS_MEDIAWIKI' => NS_MEDIAWIKI, |
| 42 | + 'NS_MEDIAWIKI_TALK' => NS_MEDIAWIKI_TALK, |
| 43 | + 'NS_TEMPLATE' => NS_TEMPLATE, |
| 44 | + 'NS_TEMPLATE_TALK' => NS_TEMPLATE_TALK, |
| 45 | + 'NS_HELP' => NS_HELP, |
| 46 | + 'NS_HELP_TALK' => NS_HELP_TALK, |
| 47 | + 'NS_CATEGORY' => NS_CATEGORY, |
| 48 | + 'NS_CATEGORY_TALK' => NS_CATEGORY_TALK |
| 49 | + ); |
| 50 | + |
| 51 | + $this->mExtraNs = isset( $wgExtraNamespaces ) ? $wgExtraNamespaces : array(); |
| 52 | + $this->mContLangNs = $wgContLang->getNamespacesBootstrap(); |
| 53 | + $this->mLanguageCode = $wgContLang->getCode(); |
| 54 | + |
| 55 | + if ( $wgNamespaceSynonymsEn == $wgContLang->getNamespaceSynonymsBootstrap() && $this->mLanguageCode !== 'en' ) |
| 56 | + $this->mContLangNsSynonyms = array(); |
| 57 | + else |
| 58 | + $this->mContLangNsSynonyms = $wgContLang->getNamespaceSynonymsBootstrap(); |
| 59 | + |
| 60 | + |
| 61 | + $this->dbw =& wfGetDB( DB_MASTER ); |
| 62 | + } |
| 63 | + |
| 64 | + function initialize() { |
| 65 | + global $wgContLang; |
| 66 | + |
| 67 | + $fname = 'NamespaceBootstrap::initialize'; |
| 68 | + |
| 69 | + // namespace table, standard namespaces |
| 70 | + foreach ( $this->mStdNs as $system => $id ) { |
| 71 | + $subject = $this->getSubject( $id ); |
| 72 | + |
| 73 | + $this->dbw->insert( 'namespace', |
| 74 | + array( |
| 75 | + 'ns_id' => $id, |
| 76 | + 'ns_system' => $system, |
| 77 | + 'ns_subpages' => $this->getSubpages( $id ), |
| 78 | + 'ns_search_default' => $this->getSearch( $id ), |
| 79 | + 'ns_target' => null, |
| 80 | + 'ns_parent' => $subject === $id ? null : $subject, |
| 81 | + 'ns_hidden' => null, |
| 82 | + 'ns_count' => $id == NS_MAIN ? true : false, |
| 83 | + 'ns_class' => null, |
| 84 | + ), |
| 85 | + $fname |
| 86 | + ); |
| 87 | + } |
| 88 | + |
| 89 | + // namespace table, extra namespaces |
| 90 | + foreach ( $this->mExtraNs as $id => $name ) { |
| 91 | + $subject = $this->getSubject( $id ); |
| 92 | + |
| 93 | + $this->dbw->insert( 'namespace', |
| 94 | + array( |
| 95 | + 'ns_id' => $id, |
| 96 | + 'ns_system' => null, // extra namespaces are null |
| 97 | + 'ns_subpages' => $this->getSubpages( $id ), |
| 98 | + 'ns_search_default' => $this->getSearch( $id ), |
| 99 | + 'ns_target' => null, |
| 100 | + 'ns_parent' => $subject === $id ? null : $subject, |
| 101 | + 'ns_hidden' => null, |
| 102 | + 'ns_count' => false, |
| 103 | + 'ns_class' => null, |
| 104 | + ), |
| 105 | + $fname |
| 106 | + ); |
| 107 | + } |
| 108 | + |
| 109 | + // Cache already inserted results so we won't get a case where |
| 110 | + // we'll do a bogus insert because namespaces haven't been |
| 111 | + // translated or the translation equals the original. |
| 112 | + $nscache = array(); |
| 113 | + |
| 114 | + // namespace_names, English fallbacks |
| 115 | + |
| 116 | + //FIXME: need to use proper language code |
| 117 | + $langobj = Language::factory( 'en' ); |
| 118 | + $langobj->initEncoding(); |
| 119 | + $langobj->initContLang(); |
| 120 | + |
| 121 | + foreach ( $langobj->getNamespaces() as $id => $text ) { |
| 122 | + if ( @$nscache[$id] === $text || $text === '' ) |
| 123 | + continue; |
| 124 | + |
| 125 | + $nscache[$id] = $text; |
| 126 | + |
| 127 | + $this->dbw->insert( 'namespace_names', |
| 128 | + array( |
| 129 | + 'ns_id' => $id, |
| 130 | + 'ns_name' => $text, |
| 131 | + 'ns_default' => $this->mLanguageCode == 'en' || $this->mContLangNs[$id] === $text ? 1 : 0, |
| 132 | + 'ns_canonical' => 1, |
| 133 | + ), |
| 134 | + $fname |
| 135 | + ); |
| 136 | + } |
| 137 | + |
| 138 | + |
| 139 | + // namespace_names, content language |
| 140 | + foreach ( $this->mContLangNs as $ns => $text ) { |
| 141 | + if ( $text === '' || @$nscache[$ns] === $text ) |
| 142 | + continue; |
| 143 | + |
| 144 | + $nscache[$ns] = $text; |
| 145 | + |
| 146 | + $this->dbw->insert( 'namespace_names', |
| 147 | + array( |
| 148 | + 'ns_id' => $ns, |
| 149 | + 'ns_name' => $text, |
| 150 | + 'ns_default' => 1, |
| 151 | + 'ns_canonical' => 0 |
| 152 | + ), |
| 153 | + $fname |
| 154 | + ); |
| 155 | + |
| 156 | + } |
| 157 | + |
| 158 | + // namespace_names, synonyms |
| 159 | + foreach ( $this->mContLangNsSynonyms as $id => $synonyms ) |
| 160 | + foreach ( $synonyms as $synonym ) { |
| 161 | + if ( $nscache[$id] === $synonym ) |
| 162 | + continue; |
| 163 | + |
| 164 | + $this->dbw->insert( 'namespace_names', |
| 165 | + array( |
| 166 | + 'ns_id' => $id, |
| 167 | + 'ns_name' => $synonym, |
| 168 | + 'ns_default' => 0, |
| 169 | + 'ns_canonical' => 0 |
| 170 | + ), |
| 171 | + $fname |
| 172 | + ); |
| 173 | + } |
| 174 | + |
| 175 | + // namespace_names, Project and Project_talk are special cases, should be canonical |
| 176 | + foreach ( array( NS_PROJECT => 'Project', NS_PROJECT_TALK => 'Project_talk' ) as $id => $text ) { |
| 177 | + if ( $nscache[$id] === $text ) |
| 178 | + continue; |
| 179 | + |
| 180 | + $this->dbw->insert( 'namespace_names', |
| 181 | + array( |
| 182 | + 'ns_id' => $id, |
| 183 | + 'ns_name' => $text, |
| 184 | + 'ns_default' => 0, |
| 185 | + 'ns_canonical' => 1 |
| 186 | + ), |
| 187 | + $fname |
| 188 | + ); |
| 189 | + } |
| 190 | + |
| 191 | + // namespace_names, Import extra namespaces specified using |
| 192 | + // legacy syntax. |
| 193 | + foreach ( $this->mExtraNs as $id => $name ) { |
| 194 | + $this->dbw->insert( 'namespace_names', |
| 195 | + array( |
| 196 | + 'ns_id' => "$id", |
| 197 | + 'ns_name' => $name, |
| 198 | + 'ns_default' => 1, |
| 199 | + 'ns_canonical' => 0, |
| 200 | + ), |
| 201 | + $fname |
| 202 | + ); |
| 203 | + } |
| 204 | + } |
| 205 | + |
| 206 | + function getSubpages( $ns ) { |
| 207 | + global $wgNamespacesWithSubpages; |
| 208 | + |
| 209 | + return @$wgNamespacesWithSubpages[$ns] ? 1 : 0; |
| 210 | + |
| 211 | + } |
| 212 | + |
| 213 | + function getSearch( $ns ) { |
| 214 | + global $wgNamespacesToBeSearchedDefault; |
| 215 | + |
| 216 | + return @$wgNamespacesToBeSearchedDefault[$ns] ? 1 : 0; |
| 217 | + } |
| 218 | + |
| 219 | + function isTalk( $index ) { |
| 220 | + return ($index > 0) // Special namespaces are negative |
| 221 | + && ($index % 2); // Talk namespaces are odd-numbered |
| 222 | + } |
| 223 | + |
| 224 | + function getSubject( $index ) { |
| 225 | + if ( $this->isTalk( $index ) ) { |
| 226 | + return $index - 1; |
| 227 | + } else { |
| 228 | + return $index; |
| 229 | + } |
| 230 | + } |
| 231 | +} |
| 232 | + |
| 233 | +$nb = new NamespaceBootstrap; |
| 234 | +$nb->initialize(); |
Property changes on: branches/wikidata/maintenance/nsBootstrap.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 235 | + native |
Index: branches/wikidata/maintenance/updaters.inc |
— | — | @@ -35,6 +35,8 @@ |
36 | 36 | array( 'querycache_info', 'patch-querycacheinfo.sql' ), |
37 | 37 | array( 'filearchive', 'patch-filearchive.sql' ), |
38 | 38 | array( 'querycachetwo', 'patch-querycachetwo.sql' ), |
| 39 | + array( 'namespace', 'patch-namespace.sql' ), |
| 40 | + array( 'namespace_names', 'patch-namespace_names.sql' ), |
39 | 41 | ); |
40 | 42 | |
41 | 43 | $wgNewFields = array( |
Index: branches/wikidata/maintenance/mysql5/tables.sql |
— | — | @@ -1110,3 +1110,52 @@ |
1111 | 1111 | KEY pr_level (pr_level), |
1112 | 1112 | KEY pr_cascade (pr_cascade) |
1113 | 1113 | ) ENGINE=InnoDB, DEFAULT CHARSET=utf8; |
| 1114 | + |
| 1115 | +CREATE TABLE /*$wgDBprefix*/namespace ( |
| 1116 | + -- This ID appears in all tables where this namespace is referenced. |
| 1117 | + -- Note that the constants for system namespaces are currently |
| 1118 | + -- hardcoded (Defines.php), and special namespaces start at -2. |
| 1119 | + `ns_id` int(8) NOT NULL default '0', |
| 1120 | + -- If this is a system namespace, this field contains the constant |
| 1121 | + -- name identifying it (e.g. 'NS_MAIN', 'NS_CATEGORY'). |
| 1122 | + `ns_system` varchar(80) default '', |
| 1123 | + -- Should this namespace allow subpages ([[Foo/Bar]])? |
| 1124 | + `ns_subpages` tinyint(1) NOT NULL default '0', |
| 1125 | + -- Should pages in this namespace be included in a full-text search |
| 1126 | + -- by default? |
| 1127 | + `ns_search_default` tinyint(1) NOT NULL default '0', |
| 1128 | + -- If not empty, unprefixed links from this namespace (e.g. [[Foo]]) |
| 1129 | + -- will be treated as if they had this prefix. This could be a |
| 1130 | + -- namespace prefix, a language prefix, or an interwiki prefix. |
| 1131 | + `ns_target` varchar(200) default NULL, |
| 1132 | + -- If this is a discussion namespace, this field refers to its subject |
| 1133 | + -- namespace. |
| 1134 | + `ns_parent` int(8) default NULL, |
| 1135 | + -- Should this namespace be hidden by default from namespace selectors? |
| 1136 | + -- [[Special:Allpages]] and [[Special:Namaespaces]] are excepted |
| 1137 | + -- from this. |
| 1138 | + `ns_hidden` tinyint(1) default NULL, |
| 1139 | + -- Should an external class be used to override edit, view and delete? |
| 1140 | + -- It will be expected in extensions/ClassName/ClassName.php |
| 1141 | + -- Should pages in this namespace be counted as content? |
| 1142 | + `ns_count` tinyint(1) default NULL, |
| 1143 | + `ns_class` varchar(255) default '', |
| 1144 | + PRIMARY KEY (`ns_id`) |
| 1145 | +) TYPE=InnoDB, DEFAULT CHARSET=utf8; |
| 1146 | + |
| 1147 | +CREATE TABLE /*$wgDBprefix*/namespace_names ( |
| 1148 | + -- A reference to the namespace to which this name belongs. |
| 1149 | + -- Any namespace can have multiple names, so this is not |
| 1150 | + -- a primary key. |
| 1151 | + `ns_id` int(8) NOT NULL default '0', |
| 1152 | + -- The name of this namespace. Spaces are underscores here. |
| 1153 | + `ns_name` varchar(200) NOT NULL default '', |
| 1154 | + -- Is this the default name to which all others redirect? |
| 1155 | + `ns_default` tinyint(1) NOT NULL default '0', |
| 1156 | + -- Is this the canonical English name which is expected to |
| 1157 | + -- exist, and which cannot be deleted? (Mostly for system |
| 1158 | + -- namespaces.) |
| 1159 | + `ns_canonical` tinyint(1) default NULL, |
| 1160 | + UNIQUE INDEX ns_name (ns_name), |
| 1161 | + INDEX ns_id (ns_id) |
| 1162 | +) TYPE=InnoDB, DEFAULT CHARSET=utf8; |
Index: branches/wikidata/maintenance/tables.sql |
— | — | @@ -1105,4 +1105,54 @@ |
1106 | 1106 | KEY pr_cascade (pr_cascade) |
1107 | 1107 | ) TYPE=InnoDB; |
1108 | 1108 | |
| 1109 | + |
| 1110 | +CREATE TABLE /*$wgDBprefix*/namespace ( |
| 1111 | + -- This ID appears in all tables where this namespace is referenced. |
| 1112 | + -- Note that the constants for system namespaces are currently |
| 1113 | + -- hardcoded (Defines.php), and special namespaces start at -2. |
| 1114 | + `ns_id` int(8) NOT NULL default '0', |
| 1115 | + -- If this is a system namespace, this field contains the constant |
| 1116 | + -- name identifying it (e.g. 'NS_MAIN', 'NS_CATEGORY'). |
| 1117 | + `ns_system` varchar(80) default '', |
| 1118 | + -- Should this namespace allow subpages ([[Foo/Bar]])? |
| 1119 | + `ns_subpages` tinyint(1) NOT NULL default '0', |
| 1120 | + -- Should pages in this namespace be included in a full-text search |
| 1121 | + -- by default? |
| 1122 | + `ns_search_default` tinyint(1) NOT NULL default '0', |
| 1123 | + -- If not empty, unprefixed links from this namespace (e.g. [[Foo]]) |
| 1124 | + -- will be treated as if they had this prefix. This could be a |
| 1125 | + -- namespace prefix, a language prefix, or an interwiki prefix. |
| 1126 | + `ns_target` varchar(200) default NULL, |
| 1127 | + -- If this is a discussion namespace, this field refers to its subject |
| 1128 | + -- namespace. |
| 1129 | + `ns_parent` int(8) default NULL, |
| 1130 | + -- Should this namespace be hidden by default from namespace selectors? |
| 1131 | + -- [[Special:Allpages]] and [[Special:Namaespaces]] are excepted |
| 1132 | + -- from this. |
| 1133 | + `ns_hidden` tinyint(1) default NULL, |
| 1134 | + -- Should pages in this namespace be counted as content? |
| 1135 | + `ns_count` tinyint(1) default NULL, |
| 1136 | + -- Should an external class be used to override edit, view and delete? |
| 1137 | + -- It will be expected in extensions/ClassName/ClassName.php |
| 1138 | + `ns_class` varchar(255) default '', |
| 1139 | + PRIMARY KEY (`ns_id`) |
| 1140 | +) TYPE=InnoDB; |
| 1141 | + |
| 1142 | +CREATE TABLE /*$wgDBprefix*/namespace_names ( |
| 1143 | + -- A reference to the namespace to which this name belongs. |
| 1144 | + -- Any namespace can have multiple names, so this is not |
| 1145 | + -- a primary key. |
| 1146 | + `ns_id` int(8) NOT NULL default '0', |
| 1147 | + -- The name of this namespace. Spaces are underscores here. |
| 1148 | + `ns_name` varchar(200) NOT NULL default '', |
| 1149 | + -- Is this the default name to which all others redirect? |
| 1150 | + `ns_default` tinyint(1) NOT NULL default '0', |
| 1151 | + -- Is this the canonical English name which is expected to |
| 1152 | + -- exist, and which cannot be deleted? (Mostly for system |
| 1153 | + -- namespaces.) |
| 1154 | + `ns_canonical` tinyint(1) default NULL, |
| 1155 | + UNIQUE INDEX ns_name (ns_name), |
| 1156 | + INDEX ns_id (ns_id) |
| 1157 | +) TYPE=InnoDB; |
| 1158 | + |
1109 | 1159 | -- vim: sw=2 sts=2 et |
Index: branches/wikidata/includes/Defines.php |
— | — | @@ -205,5 +205,44 @@ |
206 | 206 | define( 'LIST_NAMES', 3); |
207 | 207 | define( 'LIST_OR', 4); |
208 | 208 | |
| 209 | +/**#@+ |
| 210 | + * Namespace changes result codes |
| 211 | + * See Namespace.php |
| 212 | + */ |
| 213 | +define('NS_RESULT',1); |
| 214 | +define('NS_SAVE_ID',2); |
| 215 | +define('NS_ILLEGAL_NAMES',3); |
| 216 | +define('NS_DUPLICATE_NAMES',4); |
| 217 | +define('NS_INTERWIKI_NAMES',5); |
| 218 | +define('NS_PREFIX_NAMES',6); |
| 219 | +/**#@-*/ |
209 | 220 | |
| 221 | +/**#@+ |
| 222 | + * Namespace changes success codes |
| 223 | + */ |
| 224 | +define('NS_MODIFIED',1); |
| 225 | +define('NS_CREATED',2); |
| 226 | +define('NS_NAME_ISSUES',3); |
| 227 | +define('NS_MISSING',4); |
| 228 | +define('NS_IDENTICAL',5); |
| 229 | +define('NS_DELETED',6); |
| 230 | +define('NS_PROTECTED',7); |
| 231 | +define('NS_HAS_PAGES',8); |
| 232 | +/**#@-*/ |
| 233 | + |
| 234 | +/**#@+ |
| 235 | + * Pseudonamespace conversions |
| 236 | + */ |
| 237 | +define('NS_PSEUDO_NOT_FOUND',1); |
| 238 | +define('NS_PSEUDO_CONVERTED',2); |
| 239 | +define('NS_NON_EMPTY',3); |
| 240 | +define('NS_DUPLICATE_TITLES',4); |
| 241 | +define('NS_DUPLICATE_TITLE_LIST',5); |
| 242 | + |
| 243 | +/**#@+ |
| 244 | + * Valid namespace names character class |
| 245 | + */ |
| 246 | +define('NS_CHAR','[ _0-9A-Za-z\x80-\xff]'); |
| 247 | + |
| 248 | + |
210 | 249 | ?> |
Index: branches/wikidata/includes/GlobalFunctions.php |
— | — | @@ -1565,6 +1565,20 @@ |
1566 | 1566 | return MimeMagic::singleton(); |
1567 | 1567 | } |
1568 | 1568 | |
| 1569 | + /** |
| 1570 | + * Class factory for NamespaceStore singleton. NamespaceStore.php |
| 1571 | + * is autoloaded thanks to definition in AutoLoader.php |
| 1572 | + */ |
| 1573 | + |
| 1574 | +function &wfGetNamespaceStore() { |
| 1575 | + static $nsstore = null; |
| 1576 | + if($nsstore == null) { |
| 1577 | + $nsstore = new NamespaceStore(); |
| 1578 | + $nsstore->load(); |
| 1579 | + } |
| 1580 | + return $nsstore; |
| 1581 | +} |
| 1582 | + |
1569 | 1583 | /** |
1570 | 1584 | * Tries to get the system directory for temporary files. |
1571 | 1585 | * The TMPDIR, TMP, and TEMP environment variables are checked in sequence, |
Index: branches/wikidata/includes/NamespaceStore.php |
— | — | @@ -0,0 +1,671 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Class for storing and retrieving namespace objects |
| 5 | + * |
| 6 | +*/ |
| 7 | +class NamespaceStore { |
| 8 | + function NamespaceStore() { |
| 9 | + global $wgNamespaces; |
| 10 | + $this->nsarray = $wgNamespaces; |
| 11 | + } |
| 12 | + |
| 13 | + function getTalk($parentns) { |
| 14 | + if($parentns->isTalk()) return $parentns->getIndex(); |
| 15 | + |
| 16 | + foreach($this->nsarray as $ns) { |
| 17 | + if($ns->hasParent() && $ns->parentIndex==$parentns->index) { |
| 18 | + return $ns->index; |
| 19 | + } |
| 20 | + } |
| 21 | + |
| 22 | + return null; |
| 23 | + } |
| 24 | + |
| 25 | + /** |
| 26 | + * Serialize this namespace to the database. |
| 27 | + * No part of the operation will be completed |
| 28 | + * unless it cannot be fully done. |
| 29 | + * |
| 30 | + * If the namespace index is NULL, a new namespace |
| 31 | + * will be created. |
| 32 | + * |
| 33 | + * @param boolean $testSave |
| 34 | + * If this is set to true, no actual changes |
| 35 | + * will be made. This is useful for testing |
| 36 | + * transactions on a number of namespaces, |
| 37 | + * and not completing any of them unless |
| 38 | + * all of them will succeed. |
| 39 | + * @param boolean $overrideInterwiki |
| 40 | + * If a namespace name overlaps with an Interwiki |
| 41 | + * prefix, should it be created anyway? |
| 42 | + * |
| 43 | + * @return array() |
| 44 | + * An array that describes the results of the |
| 45 | + * operation, as follows: |
| 46 | + * |
| 47 | + * array( |
| 48 | + * NS_RESULT=> |
| 49 | + * NS_MODIFIED | NS_CREATED | NS_NAME_ISSUES |
| 50 | + * NS_MISSING | NS_IDENTICAL |
| 51 | + * NS_SAVE_ID=>namespace ID or NULL |
| 52 | + * NS_ILLEGAL_NAMES=>array(names) |
| 53 | + * NS_DUPLICATE_NAMES=>array(names) |
| 54 | + * NS_INTERWIKI_NAMES=>array(names) |
| 55 | + * NS_PREFIX_NAMES=>array(names) |
| 56 | + * ) |
| 57 | + * |
| 58 | + * NS_RESULT can be: |
| 59 | + * |
| 60 | + * NS_MODIFIED - existing namespace successfully changed |
| 61 | + * NS_CREATED - new namespace successfully created |
| 62 | + * NS_IDENTICAL - the version in the database is |
| 63 | + * identical with the one to be saved |
| 64 | + * NS_NAME_ISSUES - operation failed due to issues with |
| 65 | + * name changes |
| 66 | + * NS_MISSING - namespace with $nsobj->index not found |
| 67 | + * in DB, cannot be altered. |
| 68 | + * |
| 69 | + * In order to show useful result information, we record |
| 70 | + * exactly which names have been added, removed or changed |
| 71 | + * (NS_NAMES_ADDED, NS_NAMES_MODIFIED, NS_NAMES_DELETED). |
| 72 | + * If the save fails, we record which name change(s) caused |
| 73 | + * the problem: |
| 74 | + * |
| 75 | + * NS_ILLEGAL_NAMES |
| 76 | + * names which contain illegal characters |
| 77 | + * |
| 78 | + * NS_DUPLICATE_NAMES |
| 79 | + * names which already exist |
| 80 | + * |
| 81 | + * NS_INTERWIKI_NAMES |
| 82 | + * names which are also used as Interwiki prefixes |
| 83 | + * (cf. $overrideInterwiki parameter) |
| 84 | + * |
| 85 | + * NS_PREFIX_NAMES |
| 86 | + * names which are used as hardcoded title prefixes |
| 87 | + * |
| 88 | + * How this function works: |
| 89 | + * ------------------------ |
| 90 | + * Check if the namespace has a valid ID (not null) |
| 91 | + * |
| 92 | + * NO: We will try to create it. |
| 93 | + * |
| 94 | + * 1.1) Obtain an index from $this->nsarray |
| 95 | + * 1.2) Make a list of the names that are going |
| 96 | + * to be added. |
| 97 | + * 1.3) Proceed to 2.2) |
| 98 | + * |
| 99 | + * YES: We will try to modify it. |
| 100 | + * |
| 101 | + * 2.1) Compare this object with the corresponding |
| 102 | + * one in $this->nsarray and make a list of |
| 103 | + * the names that are going to be removed |
| 104 | + * (set to NULL), changed, or added. |
| 105 | + * 2.2) Verify whether all namespace name operations |
| 106 | + * are possible. If not, return the appropriate |
| 107 | + * error codes. |
| 108 | + * 2.3) If all operations are possible, update or |
| 109 | + * update the namespace and return the appropriate |
| 110 | + * result array. |
| 111 | + */ |
| 112 | + function saveNamespace($nsobj, |
| 113 | + $overrideInterwiki=false, |
| 114 | + $testSave=false) { |
| 115 | + |
| 116 | + $fname='NamespaceStore::saveNamespace'; |
| 117 | + $rv=array( |
| 118 | + NS_RESULT=>null, |
| 119 | + NS_ILLEGAL_NAMES=>array(), |
| 120 | + NS_DUPLICATE_NAMES=>array(), |
| 121 | + NS_INTERWIKI_NAMES=>array(), |
| 122 | + NS_PREFIX_NAMES=>array() |
| 123 | + ); |
| 124 | + $nameOperations=array(); |
| 125 | + $dbs =& wfGetDB( DB_SLAVE ); |
| 126 | + $index=$nsobj->getIndex(); |
| 127 | + if(is_null($index)) { |
| 128 | + $create = true; |
| 129 | + end($this->nsarray); |
| 130 | + $index=$this->nsarray[key($this->nsarray)]->getIndex()+1; |
| 131 | + $nsobj->setIndex($index); |
| 132 | + foreach($nsobj->names as $name) { |
| 133 | + $nameOperations[$name]=NS_NAME_ADD; |
| 134 | + } |
| 135 | + } else { |
| 136 | + $create = false; |
| 137 | + # Does this namespace exist? |
| 138 | + if(!array_key_exists($index,$this->nsarray)) { |
| 139 | + $rv[NS_RESULT]=NS_MISSING; |
| 140 | + return $rv; |
| 141 | + } |
| 142 | + # Has anything actually been changed? |
| 143 | + if($nsobj===$this->nsarray[$nsobj->getIndex()]) { |
| 144 | + $rv[NS_RESULT]=NS_IDENTICAL; |
| 145 | + return $rv; |
| 146 | + } |
| 147 | + $oldcount=count($this->nsarray[$index]->names); |
| 148 | + $newcount=count($nsobj->names); |
| 149 | + for($i=0;$i<$oldcount || $i<$newcount;$i++) { |
| 150 | + $existsOld=array_key_exists($i, $this->nsarray[$index]->names); |
| 151 | + $existsNew=array_key_exists($i, $nsobj->names); |
| 152 | + if($existsOld && $existsNew) { |
| 153 | + if(strcasecmp($this->nsarray[$index]->names[$i], $nsobj->names[$i])!=0) { |
| 154 | + $nameOperations[$nsobj->names[$i]]=NS_NAME_MODIFY; |
| 155 | + } |
| 156 | + } elseif($existsOld && !$existsNew) { |
| 157 | + $nameOperations[$this->nsarray[$index]->names[$i]]=NS_NAME_DELETE; |
| 158 | + } elseif(!$existsOld && $existsNew) { |
| 159 | + $nameOperations[$nsobj->names[$i]]=NS_NAME_ADD; |
| 160 | + } |
| 161 | + } |
| 162 | + |
| 163 | + } |
| 164 | + |
| 165 | + # Are there any name operations to do? If so, check |
| 166 | + # whether they are possible before doing anything else. |
| 167 | + foreach($nameOperations as $name=>$operation) { |
| 168 | + if($operation==NS_NAME_ADD || $operation==NS_NAME_MODIFY) { |
| 169 | + |
| 170 | + # Illegal characters? |
| 171 | + # This should never happen if the setters |
| 172 | + # are used. |
| 173 | + if(!$nsobj->isValidName($name)) { |
| 174 | + $rv[NS_RESULT]=NS_NAME_ISSUES; |
| 175 | + $rv[NS_ILLEGAL_NAMES][]=$name; |
| 176 | + } |
| 177 | + |
| 178 | + # Duplicate names |
| 179 | + foreach($this->nsarray as $exns) { |
| 180 | + $dupes=array_keys($exns->names,$name); |
| 181 | + if(count($dupes)) { |
| 182 | + $rv[NS_RESULT] = NS_NAME_ISSUES; |
| 183 | + $rv[NS_DUPLICATE_NAMES][]=$name; |
| 184 | + } |
| 185 | + } |
| 186 | + |
| 187 | + # Interwiki |
| 188 | + if(Title::getInterwikiLink( $name)) { |
| 189 | + $rv[NS_RESULT]=NS_NAME_ISSUES; |
| 190 | + $rv[NS_INTERWIKI_NAMES][]=$name; |
| 191 | + } |
| 192 | + |
| 193 | + # Pseudo-namespaces (title prefixes) |
| 194 | + $likename = str_replace( '_', '\\_', $name); |
| 195 | + $likename = str_replace( '%', '\\%', $likename); |
| 196 | + $match = $dbs->addQuotes($likename.":%"); |
| 197 | + $res = $dbs->select( |
| 198 | + 'page', |
| 199 | + array('page_title'), |
| 200 | + array('page_namespace'=>0, |
| 201 | + 'page_title LIKE '.$match, |
| 202 | + ), |
| 203 | + $fname, |
| 204 | + array('LIMIT'=>1) |
| 205 | + ); |
| 206 | + if($dbs->numRows($res) > 0) { |
| 207 | + $rv[NS_RESULT]=NS_NAME_ISSUES; |
| 208 | + $rv[NS_PREFIX_NAMES][]=$name; |
| 209 | + } |
| 210 | + $dbs->freeResult($res); |
| 211 | + } |
| 212 | + } |
| 213 | + |
| 214 | + # If there are problems, return the array |
| 215 | + if($rv[NS_RESULT]==NS_NAME_ISSUES) { |
| 216 | + return $rv; |
| 217 | + } |
| 218 | + |
| 219 | + $dbm =& wfGetDB(DB_MASTER); |
| 220 | + $nsasdb=array( |
| 221 | + 'ns_id'=>$index, |
| 222 | + 'ns_system'=>$nsobj->getSystemType(), |
| 223 | + 'ns_subpages'=>$nsobj->allowsSubpages(), |
| 224 | + 'ns_search_default'=>$nsobj->isSearchedByDefault(), |
| 225 | + 'ns_target'=>$nsobj->getTarget(), |
| 226 | + 'ns_parent'=>$nsobj->getParentIndex(), |
| 227 | + 'ns_hidden'=>$nsobj->isHidden() |
| 228 | + ); |
| 229 | + if($create) { |
| 230 | + # testSave checks should always be placed |
| 231 | + # right before a transaction that alters |
| 232 | + # the database or memory state. |
| 233 | + if(!$testSave) { |
| 234 | + $dbm->insert( |
| 235 | + 'namespace', |
| 236 | + $nsasdb, |
| 237 | + $fname, |
| 238 | + array() |
| 239 | + ); |
| 240 | + } |
| 241 | + } |
| 242 | + foreach($nameOperations as $name=>$operation) { |
| 243 | + if($operation==NS_NAME_ADD) { |
| 244 | + $isDefault = ($name==$nsobj->getDefaultName()); |
| 245 | + $isCanonical = ($name==$nsobj->getCanonicalName()); |
| 246 | + if(!$testSave) { |
| 247 | + $dbm->insert( |
| 248 | + 'namespace_names', |
| 249 | + array( |
| 250 | + 'ns_id'=>$nsobj->getIndex(), |
| 251 | + 'ns_name'=>$name, |
| 252 | + 'ns_default'=>$isDefault, |
| 253 | + 'ns_canonical'=>$isCanonical |
| 254 | + ), |
| 255 | + $fname, |
| 256 | + array() |
| 257 | + ); |
| 258 | + } |
| 259 | + } elseif($operation==NS_NAME_MODIFY) { $oldname = $this->nsarray[$index]->names[$nsobj->getNameIndexForName($name)]; |
| 260 | + if(!$testSave) { |
| 261 | + $dbm->update( |
| 262 | + 'namespace_names', |
| 263 | + array( /* SET */ |
| 264 | + 'ns_name'=>$name, |
| 265 | + ), |
| 266 | + array( |
| 267 | + 'ns_name'=>$oldname), |
| 268 | + $fname); |
| 269 | + } |
| 270 | + } elseif($operation==NS_NAME_DELETE) { |
| 271 | + $dbm->delete( |
| 272 | + 'namespace_names', |
| 273 | + array('ns_name'=>$name), |
| 274 | + '*'); |
| 275 | + } |
| 276 | + } |
| 277 | + if($create) { |
| 278 | + $rv[NS_RESULT]=NS_CREATED; |
| 279 | + |
| 280 | + # If this was just a test for a new |
| 281 | + # namespace, reset the index to NULL so |
| 282 | + # it will be created for real |
| 283 | + # if save() is called on the same object. |
| 284 | + if($testSave) { |
| 285 | + $nsobj->setIndex(NULL); |
| 286 | + } |
| 287 | + } else { |
| 288 | + # Set canonical and default names. |
| 289 | + # This needs to happen after other name operations |
| 290 | + # because we can't operate on the new names until |
| 291 | + # they exist. :-) |
| 292 | + $oldDefaultName=$this->nsarray[$index]->getDefaultName(); |
| 293 | + $newDefaultName=$nsobj->getDefaultName(); |
| 294 | + |
| 295 | + # Note that canonical names normally should NEVER change, |
| 296 | + # but we provide the functionality just in case it's needed |
| 297 | + # by some maintenance scripts. |
| 298 | + $oldCanonical=$this->nsarray[$index]->getCanonicalName(); |
| 299 | + $newCanonical=$nsobj->getCanonicalName(); |
| 300 | + if(!$testSave) { |
| 301 | + $dbm->update( |
| 302 | + 'namespace', |
| 303 | + $nsasdb, /* SET */ |
| 304 | + array('ns_id'=>$index), |
| 305 | + $fname |
| 306 | + ); |
| 307 | + if($oldDefaultName != $newDefaultName) { |
| 308 | + $dbm->update( |
| 309 | + 'namespace_names', |
| 310 | + array('ns_default'=>0), /* SET */ |
| 311 | + array('ns_name'=>$oldDefaultName), |
| 312 | + $fname |
| 313 | + ); |
| 314 | + $dbm->update( |
| 315 | + 'namespace_names', |
| 316 | + array('ns_default'=>1), /* SET */ |
| 317 | + array('ns_name'=>$newDefaultName), |
| 318 | + $fname |
| 319 | + ); |
| 320 | + } |
| 321 | + if($oldCanonical != $newCanonical) { |
| 322 | + $dbm->update( |
| 323 | + 'namespace_names', |
| 324 | + array('ns_canonical'=>0), /* SET */ |
| 325 | + array('ns_name'=>$oldCanonical), |
| 326 | + $fname |
| 327 | + ); |
| 328 | + $dbm->update( |
| 329 | + 'namespace_names', |
| 330 | + array('ns_canonical'=>1), /* SET */ |
| 331 | + array('ns_name'=>$newCanonical), |
| 332 | + $fname |
| 333 | + ); |
| 334 | + } |
| 335 | + } |
| 336 | + |
| 337 | + $rv[NS_RESULT]=NS_MODIFIED; |
| 338 | + } |
| 339 | + $rv[NS_SAVE_ID]=$index; |
| 340 | + |
| 341 | + # Note that it may be desirable to call Namespace::load() |
| 342 | + # in addition to this since the name (not namespace) indexes in |
| 343 | + # the database can be different from the one in the array. |
| 344 | + if(!$testSave) { |
| 345 | + $this->nsarray[$index]=$nsobj; |
| 346 | + } |
| 347 | + $this->refreshReverseIndex(); |
| 348 | + return $rv; |
| 349 | + } |
| 350 | + /** |
| 351 | + * Delete a namespace from the database and the namespace array. |
| 352 | + * Only use this on clone()s of objects in the $this->nsarray array. |
| 353 | + * This function allows deleting system namespaces if explicitly |
| 354 | + * specified; this should however not be possible through the |
| 355 | + * user interface. |
| 356 | + * |
| 357 | + * @param $deleteSystem bool Override system namespace protection |
| 358 | + */ |
| 359 | + function deleteNamespace($nsobj, $deleteSystem=false) { |
| 360 | + if(is_null($nsobj->getIndex())) { |
| 361 | + return array(NS_RESULT=>NS_MISSING); |
| 362 | + } |
| 363 | + |
| 364 | + if($nsobj->isSystemNamespace() && !$deleteSystem) { |
| 365 | + return array(NS_RESULT=>NS_PROTECTED); |
| 366 | + } |
| 367 | + if($nsobj->countPages()>0) { |
| 368 | + return array(NS_RESULT=>NS_HAS_PAGES); |
| 369 | + } |
| 370 | + # Remove all names |
| 371 | + $nsobj->removeAllNames(); |
| 372 | + # Try saving |
| 373 | + $trv=$nsobj->testSave(); |
| 374 | + if($trv[NS_RESULT]!=NS_MODIFIED) { |
| 375 | + return $trv; |
| 376 | + } |
| 377 | + |
| 378 | + # As we just have to delete everything, we go |
| 379 | + # right into the database if the test succeeds. |
| 380 | + $dbm =& wfGetDB( DB_MASTER ); |
| 381 | + $dbm->delete( |
| 382 | + 'namespace', |
| 383 | + array('ns_id'=>$nsobj->getIndex()), |
| 384 | + '*'); |
| 385 | + $dbm->delete( |
| 386 | + 'namespace_names', |
| 387 | + array('ns_id'=>$nsobj->getIndex()), |
| 388 | + '*'); |
| 389 | + # Don't forget it's still in memory. |
| 390 | + unset($this->nsarray[$nsobj->getIndex()]); |
| 391 | + $this->refreshReverseIndex(); |
| 392 | + return array(NS_RESULT=>NS_DELETED); |
| 393 | + } |
| 394 | + |
| 395 | + |
| 396 | + /** |
| 397 | + * Maintain index used by getIndexByName |
| 398 | + */ |
| 399 | + function refreshReverseIndex() { |
| 400 | + $this->reverseindex = array(); |
| 401 | + foreach ($this->nsarray as $ns) { |
| 402 | + foreach($ns->names as $name) { |
| 403 | + $this->reverseindex[strtolower($name)]=$ns->getIndex(); |
| 404 | + } |
| 405 | + } |
| 406 | + } |
| 407 | + |
| 408 | + /** |
| 409 | + * For _any_ name (among all namespaces), return |
| 410 | + * the index of the namespace to which it belongs. |
| 411 | + * |
| 412 | + * @param string Name to search for |
| 413 | + * @return int Index of the namespace associated |
| 414 | + * with this name (or NULL) |
| 415 | + * |
| 416 | + * call refreshReverseIndex() first if there's any doubt whether |
| 417 | + * the reverse index is up-to-date (though for performance |
| 418 | + * reasons, don't do it if it isn't necessary. |
| 419 | + * |
| 420 | + * @static |
| 421 | + */ |
| 422 | + function getIndexForName ( $name ) { |
| 423 | + $index = @$this->reverseindex[strtolower($name)]; |
| 424 | + return isset($index) ? $index : NULL; |
| 425 | + } |
| 426 | + |
| 427 | + /** |
| 428 | + * Return the default name for any namespace name |
| 429 | + * given as a parameter, even if it is the default |
| 430 | + * name already. Searches all namespaces. |
| 431 | + * |
| 432 | + * @param string Any namespace name |
| 433 | + * @return string The name (may be identical) |
| 434 | + * @static |
| 435 | + */ |
| 436 | + function getDefaultNameForName ( $name ) { |
| 437 | + $index=$this->getIndexForName($name); |
| 438 | + if(!is_null($index)) { |
| 439 | + return $this->nsarray[$index]->getDefaultName(); |
| 440 | + } else { |
| 441 | + return null; |
| 442 | + } |
| 443 | + } |
| 444 | + |
| 445 | + /** |
| 446 | + * Return an array of the default names of all |
| 447 | + * namespaces. Resets array pointer of $this->nsarray. |
| 448 | + * @param $includeHidden Should hidden namespaces |
| 449 | + * be part of the array? |
| 450 | + * @return array |
| 451 | + * @static |
| 452 | + */ |
| 453 | + function getDefaultNamespaces($includeHidden=false) { |
| 454 | + $dns=array(); |
| 455 | + foreach($this->nsarray as $ns) { |
| 456 | + if(!$ns->isHidden() || $includeHidden) { |
| 457 | + $dn=$ns->getDefaultName(); |
| 458 | + if(!is_null($dn)) { |
| 459 | + $dns[$ns->getIndex()]=$dn; |
| 460 | + } else { |
| 461 | + $dns[$ns->getIndex()]=''; |
| 462 | + } |
| 463 | + } |
| 464 | + } |
| 465 | + return $dns; |
| 466 | + } |
| 467 | + |
| 468 | + function getAllNamespaceArray() { |
| 469 | + $allns=array(); |
| 470 | + foreach($this->nsarray as $ns) { |
| 471 | + if(!$ns->isHidden() || $includeHidden) { |
| 472 | + $allns[$ns->getIndex()]=$ns->getNames(); |
| 473 | + } |
| 474 | + } |
| 475 | + return $allns; |
| 476 | + } |
| 477 | + |
| 478 | + /** |
| 479 | + * A convenience function that returns the same thing as |
| 480 | + * getDefaultNamespaces() except with the array values changed to ' ' |
| 481 | + * where it found '_', useful for producing output to be displayed |
| 482 | + * e.g. in <select> forms. |
| 483 | + * |
| 484 | + * @static |
| 485 | + * @param $includeHidden |
| 486 | + * @return array |
| 487 | + */ |
| 488 | + function &getFormattedDefaultNamespaces($includeHidden=false) { |
| 489 | + $ns = $this->getDefaultNamespaces($includeHidden); |
| 490 | + foreach($ns as $k => $v) { |
| 491 | + $ns[$k] = strtr($v, '_', ' '); |
| 492 | + } |
| 493 | + return $ns; |
| 494 | + } |
| 495 | + |
| 496 | + /** |
| 497 | + * Load or reload namespace definitions from the database |
| 498 | + * into a global array. |
| 499 | + * |
| 500 | + * @param $purgeCache If definitions exist in memory, should |
| 501 | + * they be reloaded anyway? |
| 502 | + * |
| 503 | + * @static |
| 504 | + */ |
| 505 | + function load($purgeCache=false) { |
| 506 | + |
| 507 | + global $wgMemc, $wgDBname; |
| 508 | + $key="$wgDBname:namespaces:list"; |
| 509 | + if(!$purgeCache) { |
| 510 | + $fromMemory = $wgMemc->get($key); |
| 511 | + if(is_array($fromMemory)) { |
| 512 | + # Cached definitions found |
| 513 | + $this->nsarray=$fromMemory; |
| 514 | + # TODO: cache reverse index too |
| 515 | + $this->refreshReverseIndex(); |
| 516 | + return true; |
| 517 | + } |
| 518 | + } |
| 519 | + $this->nsarray = array(); |
| 520 | + $dbr =& wfGetDB( DB_SLAVE ); |
| 521 | + $res = $dbr->select( 'namespace', |
| 522 | + array('ns_id','ns_search_default','ns_subpages', 'ns_parent', 'ns_target', 'ns_system', 'ns_hidden', 'ns_count', 'ns_class'), |
| 523 | + array(), |
| 524 | + 'Setup', |
| 525 | + array('ORDER BY'=>'ns_id ASC') |
| 526 | + ); |
| 527 | + while( $row = $dbr->fetchObject( $res ) ){ |
| 528 | + # See Namespace.php for documentation on all namespace |
| 529 | + # properties which are accessed below. |
| 530 | + $id=$row->ns_id; |
| 531 | + $this->nsarray[$id]=new Namespace(); |
| 532 | + |
| 533 | + # Cannot currently be changed through the UI - is |
| 534 | + # there a need for it to be changeable? |
| 535 | + $this->nsarray[$id]->setMovable( |
| 536 | + $id < NS_MAIN || $id==NS_IMAGE || |
| 537 | + $id==NS_CATEGORY ? false : true ); |
| 538 | + $this->nsarray[$id]->setIndex($id); |
| 539 | + $this->nsarray[$id]->setSystemType($row->ns_system); |
| 540 | + $this->nsarray[$id]->setSearchedByDefault($row->ns_search_default); |
| 541 | + $this->nsarray[$id]->setSubpages($row->ns_subpages); |
| 542 | + $this->nsarray[$id]->setHidden($row->ns_hidden); |
| 543 | + $this->nsarray[$id]->setTarget($row->ns_target); |
| 544 | + $this->nsarray[$id]->setHandlerClass($row->ns_class); |
| 545 | + $this->nsarray[$id]->setCountable($row->ns_count); |
| 546 | + $this->nsarray[$id]->setParentIndex($row->ns_parent); |
| 547 | + $res2 = $dbr->select( 'namespace_names', array('ns_name','ns_default,ns_canonical'), |
| 548 | + array('ns_id = '. $row->ns_id), |
| 549 | + 'Setup', array('order by'=>'ns_default desc,ns_canonical desc,ns_id asc')); |
| 550 | + |
| 551 | + # Add the list of valid names |
| 552 | + while($row2 = $dbr->fetchObject($res2) ) { |
| 553 | + $nsi=$this->nsarray[$id]->addName($row2->ns_name); |
| 554 | + if($row2->ns_default) { |
| 555 | + $this->nsarray[$id]->setDefaultNameIndex($nsi); |
| 556 | + } |
| 557 | + if($row2->ns_canonical) { |
| 558 | + $this->nsarray[$id]->setCanonicalNameIndex($nsi); |
| 559 | + } |
| 560 | + } |
| 561 | + } |
| 562 | + $dbr->freeResult( $res ); |
| 563 | + $wgMemc->set($key,$this->nsarray); |
| 564 | + $this->refreshReverseIndex(); |
| 565 | + } |
| 566 | + |
| 567 | + /** |
| 568 | + * Convert a "pseudonamespace" (just prefixed titles) into a real |
| 569 | + * one. |
| 570 | + * |
| 571 | + * @param string $prefix - The pseudonamespace prefix string |
| 572 | + * @param Namespace $target - the target namespace object |
| 573 | + * @param Namespace $source - the source namespace object (should |
| 574 | + * usually be $this->nsarray[NS_MAIN] or ..[NS_TALK]). This is the |
| 575 | + * one we expect the prefixed titles to be stored in. |
| 576 | + * @param boolean $merge - Is it acceptable to merge into a namespace |
| 577 | + * which does already contain pages? This is potentially irreversible! |
| 578 | + * |
| 579 | + * Why pass around Namespace objects? This saves us some validation, |
| 580 | + * since the indexes can be assumed to exist. |
| 581 | + * |
| 582 | + * @static |
| 583 | + */ |
| 584 | + function convertPseudonamespace($prefix,$target,$source,$merge=false) { |
| 585 | + $dbm =& wfGetDB(DB_MASTER); |
| 586 | + $dbs =& wfGetDB(DB_SLAVE); |
| 587 | + $fname="Namespace::convertPseudonamespace"; |
| 588 | + $targetcount=$target->countPages(); |
| 589 | + if(!$merge && $targetcount>0) { |
| 590 | + return array(NS_RESULT=>NS_NON_EMPTY); |
| 591 | + } |
| 592 | + $table = $dbs->tableName( 'page' ); |
| 593 | + $eprefix = $dbs->strencode( $prefix ); |
| 594 | + $likeprefix = str_replace( '_', '\\_', $eprefix); |
| 595 | + $targetid=$target->getIndex(); |
| 596 | + $sourceid=$source->getIndex(); |
| 597 | + |
| 598 | + $sql = "SELECT page_id AS id, |
| 599 | + page_title AS oldtitle, |
| 600 | + TRIM(LEADING '$eprefix:' FROM page_title) AS title |
| 601 | + FROM {$table} |
| 602 | + WHERE page_namespace=$sourceid |
| 603 | + AND page_title LIKE '$likeprefix:%'"; |
| 604 | + |
| 605 | + $result = $dbs->query( $sql, $fname ); |
| 606 | + $set = array(); |
| 607 | + while( $row = $dbs->fetchObject( $result ) ) { |
| 608 | + $set[] = $row; |
| 609 | + } |
| 610 | + $dbs->freeResult( $result ); |
| 611 | + |
| 612 | + if(!count($set)) { |
| 613 | + return array(NS_RESULT=>NS_PSEUDO_NOT_FOUND); |
| 614 | + } else { |
| 615 | + # Check duplicates |
| 616 | + if($targetcount) { |
| 617 | + $dupeTitles=array(); |
| 618 | + foreach($set as $row) { |
| 619 | + $pageExists=$dbs->selectField( |
| 620 | + 'page', |
| 621 | + 'count(*)', |
| 622 | + array('page_title'=>$row->title, |
| 623 | + 'page_namespace'=>$targetid) |
| 624 | + ); |
| 625 | + if($pageExists) { |
| 626 | + $dupeTitles[]=$row->title; |
| 627 | + } |
| 628 | + } |
| 629 | + if(count($dupeTitles)) { |
| 630 | + return(array( |
| 631 | + NS_RESULT => NS_DUPLICATE_TITLES, |
| 632 | + NS_DUPLICATE_TITLE_LIST => $dupeTitles)); |
| 633 | + |
| 634 | + } |
| 635 | + } |
| 636 | + foreach($set as $row) { |
| 637 | + $dbm->update( $table, |
| 638 | + array( |
| 639 | + "page_namespace" => $targetid, |
| 640 | + "page_title" => $row->title, |
| 641 | + ), |
| 642 | + array( |
| 643 | + "page_namespace" => $sourceid, |
| 644 | + "page_title" => $row->oldtitle, |
| 645 | + ), |
| 646 | + $fname ); |
| 647 | + } |
| 648 | + } |
| 649 | + return array(NS_RESULT=>NS_PSEUDO_CONVERTED); |
| 650 | + |
| 651 | + } |
| 652 | + |
| 653 | + function getNamespaceObjectByIndex( $index ) { |
| 654 | + return $this->nsarray[$index]; |
| 655 | + } |
| 656 | + |
| 657 | + function hasIndex( $index ) { |
| 658 | + return !empty($this->nsarray[$index]); |
| 659 | + } |
| 660 | + |
| 661 | + function getNamespaceObjectByName( $name ) { |
| 662 | + $index=$this->getIndexForName($name); |
| 663 | + return $this->nsarray[$index]; |
| 664 | + } |
| 665 | + |
| 666 | + function getAllNamespaceObjects( ) { |
| 667 | + return $this->nsarray; |
| 668 | + } |
| 669 | +} |
| 670 | + |
| 671 | + |
| 672 | +?> |
Property changes on: branches/wikidata/includes/NamespaceStore.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 673 | + native |
Index: branches/wikidata/includes/AutoLoader.php |
— | — | @@ -116,6 +116,7 @@ |
117 | 117 | 'MessageCache' => 'includes/MessageCache.php', |
118 | 118 | 'MimeMagic' => 'includes/MimeMagic.php', |
119 | 119 | 'Namespace' => 'includes/Namespace.php', |
| 120 | + 'NamespaceStore' => 'includes/NamespaceStore.php', |
120 | 121 | 'FakeMemCachedClient' => 'includes/ObjectCache.php', |
121 | 122 | 'OutputPage' => 'includes/OutputPage.php', |
122 | 123 | 'PageHistory' => 'includes/PageHistory.php', |
Index: branches/wikidata/includes/Title.php |
— | — | @@ -648,8 +648,13 @@ |
649 | 649 | * @return string |
650 | 650 | */ |
651 | 651 | function getTalkNsText() { |
652 | | - global $wgContLang; |
653 | | - return( $wgContLang->getNsText( Namespace::getTalk( $this->mNamespace ) ) ); |
| 652 | + $ns = Namespace::getTalk( $this->getNamespace() ); |
| 653 | + if(is_null($ns)) { |
| 654 | + return null; |
| 655 | + } |
| 656 | + else { |
| 657 | + return Title::makeTitle( $ns, $this->getDBkey() ); |
| 658 | + } |
654 | 659 | } |
655 | 660 | |
656 | 661 | /** |
Index: branches/wikidata/includes/Namespace.php |
— | — | @@ -1,120 +1,594 @@ |
2 | 2 | <?php |
3 | 3 | /** |
4 | | - * Provide things related to namespaces |
| 4 | + * Class for creating namespace objects |
| 5 | + * and static namespace-related functions. |
| 6 | + * |
| 7 | + * Definitions: |
| 8 | + * |
| 9 | + * Namespace: |
| 10 | + * A prefix of the form "Abc:" represented on the database level |
| 11 | + * with an integer number. Using namespaces, pages with the same title |
| 12 | + * can exist in different contexts; for example, a page |
| 13 | + * "User:Stephen King" could coexist with "Stephen King". (No prefix |
| 14 | + * refers to the "article namespace", or to the target namespace; see |
| 15 | + * below. |
| 16 | + * |
| 17 | + * Default name, synonyms: |
| 18 | + * Any namespace can have an arbitrary number of valid names. This is |
| 19 | + * to ensure that, for example, "Image:", "File:", "Video:" etc. can |
| 20 | + * all be used to access uploaded files. The default name is the name |
| 21 | + * which all these valid names ''redirect to'', that is, if you access |
| 22 | + * index.php?title=Image:Bear.jpg, it will redirect you to |
| 23 | + * index.php?Title=File:Bear.jpg, because "File:" is the default name |
| 24 | + * for this namespace. Any name which is not the default name will be |
| 25 | + * referred to herein as a synyonm. |
| 26 | + * |
| 27 | + * Canonical namespace name: |
| 28 | + * Due to the fact that namespace names are translated into many |
| 29 | + * languages, and customized to many wikis, it is desirable to have |
| 30 | + * a reliable way by which a namespace with a particular ''meaning'' |
| 31 | + * can be accessed on any wiki installation. For example, you may |
| 32 | + * want to access a user page on the Serbian Wikipedia, but you don't |
| 33 | + * know the equivalent of "User:" in Serbian. During installation, |
| 34 | + * some namespace names are flagged as canonical. These cannot |
| 35 | + * be changed using the namespace manager, and can be expected |
| 36 | + * to work on all wikis. A canonical namespace name does not have |
| 37 | + * to be the default name, e.g., "User:" would still redirect to |
| 38 | + * the Serbian equivalent. |
| 39 | + * |
| 40 | + * Namespace target: |
| 41 | + * Depending on the nature of your project, it may be desirable |
| 42 | + * that all [[unprefixed links]] within a namespace point to a |
| 43 | + * particular destination (another namespace or InterWiki). An |
| 44 | + * example is Wikibooks, where namespaces are used to separate |
| 45 | + * different types of books, and you would like all links within |
| 46 | + * a book to point to other chapters in the book unless otherwise |
| 47 | + * specified. Another example is Wikinews, where links from the |
| 48 | + * main namespace typically point to Wikipedia. |
| 49 | + * |
| 50 | + * Quite simply put, if this target is set for a namespace, all |
| 51 | + * links without a valid namespace or InterWiki prefix in pages |
| 52 | + * in that namespace are treated as if they were written: |
| 53 | + * [[LINK TARGET:Actual title|Actual title]]. |
| 54 | + * |
| 55 | + * How to modify a namespace and its properties: |
| 56 | + * Do NOT alter the $wgNamespaces object. Instead, create a clone() |
| 57 | + * of the object and change its properties. To delete a name, |
| 58 | + * set it to null (not ''). Call its save() method. It will be |
| 59 | + * compared to the matching object in $wgNamespaces and modified |
| 60 | + * if posible. |
| 61 | + * |
| 62 | + * How to create a new namespace: |
| 63 | + * Create a new namespace object with the desired properties. Set |
| 64 | + * the index to NULL. Call its save() method. |
| 65 | + * |
5 | 66 | */ |
6 | 67 | |
7 | | -/** |
8 | | - * Definitions of the NS_ constants are in Defines.php |
9 | | - * @private |
10 | | - */ |
11 | | -$wgCanonicalNamespaceNames = array( |
12 | | - NS_MEDIA => 'Media', |
13 | | - NS_SPECIAL => 'Special', |
14 | | - NS_TALK => 'Talk', |
15 | | - NS_USER => 'User', |
16 | | - NS_USER_TALK => 'User_talk', |
17 | | - NS_PROJECT => 'Project', |
18 | | - NS_PROJECT_TALK => 'Project_talk', |
19 | | - NS_IMAGE => 'Image', |
20 | | - NS_IMAGE_TALK => 'Image_talk', |
21 | | - NS_MEDIAWIKI => 'MediaWiki', |
22 | | - NS_MEDIAWIKI_TALK => 'MediaWiki_talk', |
23 | | - NS_TEMPLATE => 'Template', |
24 | | - NS_TEMPLATE_TALK => 'Template_talk', |
25 | | - NS_HELP => 'Help', |
26 | | - NS_HELP_TALK => 'Help_talk', |
27 | | - NS_CATEGORY => 'Category', |
28 | | - NS_CATEGORY_TALK => 'Category_talk', |
29 | | -); |
30 | 68 | |
31 | | -if( is_array( $wgExtraNamespaces ) ) { |
32 | | - $wgCanonicalNamespaceNames = $wgCanonicalNamespaceNames + $wgExtraNamespaces; |
33 | | -} |
34 | 69 | |
| 70 | +# Namespace name operations |
| 71 | +# used by save() function |
| 72 | +define('NS_NAME_MODIFY',1); |
| 73 | +define('NS_NAME_DELETE',2); |
| 74 | +define('NS_NAME_ADD',3); |
| 75 | + |
35 | 76 | /** |
36 | | - * This is a utility class with only static functions |
37 | | - * for dealing with namespaces that encodes all the |
38 | | - * "magic" behaviors of them based on index. The textual |
39 | | - * names of the namespaces are handled by Language.php. |
| 77 | + * This class defines the namespace objects which are stored in |
| 78 | + * $wgNamespaces. |
40 | 79 | * |
41 | | - * These are synonyms for the names given in the language file |
42 | | - * Users and translators should not change them |
43 | | - * |
44 | 80 | */ |
45 | 81 | class Namespace { |
46 | 82 | |
| 83 | + # Fundamentals |
| 84 | + var |
| 85 | + $index, # Database-level index of this namespace |
| 86 | + $systemType; # If non-empty, string constant that defines |
| 87 | + # a system namespace. |
| 88 | + |
| 89 | + # Options |
| 90 | + var |
| 91 | + $isMovable, # Can pages in this namespace be moved? |
| 92 | + $isCountable, # Should pages in this namespace count as content (stats)? |
| 93 | + $parentIndex, # If this is a talk page, what is its mother |
| 94 | + # namespace? Otherwise NULL. |
| 95 | + $allowsSubpages, # Are subpages of the form [[Namespace:Foo/Bar]] |
| 96 | + # valid? |
| 97 | + $isSearchedByDefault, # Are pages in this namespace searched by default? |
| 98 | + $isHidden, # Should this namespace be hidden from the UI? |
| 99 | + $handlerClass, # Name of an external class to use for handling content |
| 100 | + $target; # Treat unprefixed links as prefixed with "$target:"? |
| 101 | + |
| 102 | + # Associated names |
| 103 | + var |
| 104 | + $names = array(), # Contains all the namespace names |
| 105 | + $defaultNameIndex, # Index of the name all other names redirect to? |
| 106 | + $canonicalNameIndex; # Index of the name that's valid everywhere |
| 107 | + |
| 108 | + |
| 109 | + /** |
| 110 | + * Constructor with reasonable defaults. |
| 111 | + */ |
| 112 | + function Namespace() { |
| 113 | + |
| 114 | + $this->setIndex(NULL); |
| 115 | + $this->setMovable(); |
| 116 | + $this->setParentIndex(NULL); |
| 117 | + $this->setSubpages(false); |
| 118 | + $this->setSearchedByDefault(false); |
| 119 | + $this->setTarget(NULL); |
| 120 | + $this->setHidden(false); |
| 121 | + } |
| 122 | + |
47 | 123 | /** |
48 | | - * Check if the given namespace might be moved |
| 124 | + * @return index to the correct $wgNamespaces object |
| 125 | + * or database record for this namespace. |
| 126 | + */ |
| 127 | + function getIndex() { |
| 128 | + return $this->index; |
| 129 | + } |
| 130 | + |
| 131 | + /** |
| 132 | + * @param $index New index for this namespace. |
| 133 | + * Generally only used during creation. |
| 134 | + */ |
| 135 | + function setIndex($index) { |
| 136 | + $this->index=$index; |
| 137 | + } |
| 138 | + |
| 139 | + /** |
| 140 | + * @return String like NS_MAIN for identifying |
| 141 | + * system namespaces (see Defines.php). |
| 142 | + */ |
| 143 | + function getSystemType() { |
| 144 | + return $this->systemType; |
| 145 | + } |
| 146 | + |
| 147 | + /** |
| 148 | + * Set the system type for this namespace. |
| 149 | + * @param string Constant name - needs to exist |
| 150 | + * in Defines.php. |
| 151 | + * @return bool depending on success |
| 152 | + * |
| 153 | + */ |
| 154 | + function setSystemType($type) { |
| 155 | + $typeString=(string)$type; |
| 156 | + if(defined($typeString)) { |
| 157 | + $this->systemType=$typeString; |
| 158 | + return true; |
| 159 | + } else { |
| 160 | + return false; |
| 161 | + } |
| 162 | + } |
| 163 | + |
| 164 | + /** |
| 165 | + * Is this a system namsepace? |
49 | 166 | * @return bool |
50 | 167 | */ |
51 | | - static function isMovable( $index ) { |
52 | | - return !( $index < NS_MAIN || $index == NS_IMAGE || $index == NS_CATEGORY ); |
| 168 | + function isSystemNamespace() { |
| 169 | + $sys=$this->getSystemType(); |
| 170 | + return !empty($sys); |
53 | 171 | } |
| 172 | + |
| 173 | + /** |
| 174 | + * Check if pages in this namespace can be moved. |
| 175 | + * Special pages, images and categories cannot be moved |
| 176 | + * @return bool |
| 177 | + */ |
| 178 | + function isMovable() { |
| 179 | + return $this->isMovable; |
| 180 | + } |
54 | 181 | |
55 | 182 | /** |
| 183 | + * Can pages in this namespace be moved? |
| 184 | + * @param bool |
| 185 | + */ |
| 186 | + function setMovable($movable=true) { |
| 187 | + $this->isMovable=(bool)$movable; |
| 188 | + } |
| 189 | + |
| 190 | + /** |
| 191 | + * Check if pages in this namespaces should be counted in the site statistics |
| 192 | + * @return bool |
| 193 | + */ |
| 194 | + function isCountable() { |
| 195 | + return $this->isCountable; |
| 196 | + } |
| 197 | + |
| 198 | + /** |
| 199 | + * Set if pages in this namespace should be counted in the site statistics |
| 200 | + * @param bool |
| 201 | + */ |
| 202 | + function setCountable($countable=true) { |
| 203 | + $this->isCountable=(bool)$countable; |
| 204 | + } |
| 205 | + |
| 206 | + /** |
| 207 | + * Are pages from this namespace hidden in lists? |
| 208 | + * @return bool |
| 209 | + */ |
| 210 | + function isHidden() { |
| 211 | + return $this->isHidden; |
| 212 | + } |
| 213 | + |
| 214 | + /** |
| 215 | + * Should pages from this namespace be hidden in lists? |
| 216 | + * @param bool |
| 217 | + */ |
| 218 | + function setHidden($hidden=true) { |
| 219 | + $this->isHidden=(bool)$hidden; |
| 220 | + } |
| 221 | + |
| 222 | + /** |
| 223 | + * @return int Index of the parent namespace to a |
| 224 | + * child namespace (talk or otherwise), NULL if none |
| 225 | + */ |
| 226 | + function getParentIndex() { |
| 227 | + return $this->parentIndex; |
| 228 | + } |
| 229 | + |
| 230 | + /** |
| 231 | + * @return int Same as getParentIndex(), but returns this |
| 232 | + * namespace's index if no parent namespace exists. Doubles as |
| 233 | + * a static function which returns the same given the namespace |
| 234 | + * index. |
| 235 | + */ |
| 236 | + function getSubject($index = null) { |
| 237 | + if( is_null( $index ) ) { |
| 238 | + $ns = $this; |
| 239 | + } |
| 240 | + else { |
| 241 | + $nsstore = wfGetNamespaceStore(); |
| 242 | + $ns = $nsstore->getNamespaceObjectByIndex($index); |
| 243 | + } |
| 244 | + |
| 245 | + if($ns->isTalk()) { |
| 246 | + return $ns->getParentIndex(); |
| 247 | + } else { |
| 248 | + return $ns->getIndex(); |
| 249 | + } |
| 250 | + } |
| 251 | + |
| 252 | + |
| 253 | + /** |
| 254 | + * Set parent namespace |
| 255 | + * @param int |
| 256 | + */ |
| 257 | + function setParentIndex($index) { |
| 258 | + $this->parentIndex=$index; |
| 259 | + } |
| 260 | + |
| 261 | + /** |
| 262 | + * Does this namespace have a parent namespace? |
| 263 | + * @return bool |
| 264 | + */ |
| 265 | + function hasParent() { |
| 266 | + return ($this->getParentIndex()!=NULL); |
| 267 | + } |
| 268 | + |
| 269 | + /** |
| 270 | + * Synonym for hasParent(), but might be logically different in |
| 271 | + * the near future, if parent/child relationships go beyond talk |
| 272 | + * pages. Doubles as a static function which returns the same |
| 273 | + * given the namespace index. |
| 274 | + * @return bool |
| 275 | + */ |
| 276 | + function isTalk( $index = null ) { |
| 277 | + if( is_null( $index ) ) { |
| 278 | + return $this->hasParent(); |
| 279 | + } |
| 280 | + else { |
| 281 | + $nsstore = wfGetNamespaceStore(); |
| 282 | + $ns = $nsstore->getNamespaceObjectByIndex($index); |
| 283 | + return $ns->hasParent(); |
| 284 | + } |
| 285 | + } |
| 286 | + |
| 287 | + /** |
56 | 288 | * Check if the given namespace is not a talk page |
57 | 289 | * @return bool |
58 | 290 | */ |
59 | | - static function isMain( $index ) { |
60 | | - return ! Namespace::isTalk( $index ); |
| 291 | + function isMain( $index = null) { |
| 292 | + if( is_null( $index ) ) { |
| 293 | + return !$this->isTalk(); |
| 294 | + } |
| 295 | + else { |
| 296 | + $nsstore = wfGetNamespaceStore(); |
| 297 | + $ns = $nsstore->getNamespaceObjectByIndex($index); |
| 298 | + return !$ns->isTalk(); |
| 299 | + } |
61 | 300 | } |
62 | 301 | |
| 302 | + |
63 | 303 | /** |
64 | | - * Check if the give namespace is a talk page |
| 304 | + * Is this a "special" namespace (Media:, Special:)? |
| 305 | + * Special namespaces cannot contain any pages. |
65 | 306 | * @return bool |
| 307 | + */ |
| 308 | + function isSpecial() { |
| 309 | + return($this->getIndex()<NS_MAIN); |
| 310 | + } |
| 311 | + |
| 312 | + /** |
| 313 | + * Is content in this namespace searched by default? |
| 314 | + * @return bool |
66 | 315 | */ |
67 | | - static function isTalk( $index ) { |
68 | | - return ($index > NS_MAIN) // Special namespaces are negative |
69 | | - && ($index % 2); // Talk namespaces are odd-numbered |
| 316 | + function isSearchedByDefault() { |
| 317 | + return $this->isSearchedByDefault; |
70 | 318 | } |
71 | 319 | |
72 | 320 | /** |
73 | | - * Get the talk namespace corresponding to the given index |
| 321 | + * Should this namespace be searched by default? |
| 322 | + * @param bool |
74 | 323 | */ |
75 | | - static function getTalk( $index ) { |
76 | | - if ( Namespace::isTalk( $index ) ) { |
| 324 | + function setSearchedByDefault($search=true) { |
| 325 | + $this->isSearchedByDefault=(bool)$search; |
| 326 | + } |
| 327 | + |
| 328 | + /** |
| 329 | + Get the index of the discussion namespace associated |
| 330 | + with a namespace. If this _is_ a discussion namespace, |
| 331 | + return its index. |
| 332 | + Doubles as a static function which returns the same |
| 333 | + given the namespace index. |
| 334 | + |
| 335 | + @return int |
| 336 | + |
| 337 | + TODO: support multiple discussion namespaces, |
| 338 | + so that things like a Review: namespace |
| 339 | + become possible in parallel to normal talk |
| 340 | + pages. |
| 341 | + */ |
| 342 | + function getTalk($index = null) { |
| 343 | + if( is_null( $index ) ) { |
| 344 | + $ns = $this; |
| 345 | + } |
| 346 | + else { |
| 347 | + $nsstore = wfGetNamespaceStore(); |
| 348 | + $ns = $nsstore->getNamespaceObjectByIndex($index); |
| 349 | + } |
| 350 | + |
| 351 | + /* This behavior is expected by Title.php! */ |
| 352 | + if($ns->isTalk()) return $ns->getIndex(); |
| 353 | + |
| 354 | + $nsstore = wfGetNamespaceStore(); |
| 355 | + return $nsstore->getTalk($ns); |
| 356 | + } |
| 357 | + |
| 358 | + |
| 359 | + /** |
| 360 | + * Return the default prefix for unprefixed links |
| 361 | + * from this namespace. |
| 362 | + * @return string |
| 363 | + */ |
| 364 | + function getTarget() { |
| 365 | + return $this->target; |
| 366 | + } |
| 367 | + |
| 368 | + /** |
| 369 | + * Set the default prefix for unprefixed links, e.g. |
| 370 | + * "User:" (local namespace prefix) or "MeatBall:" |
| 371 | + * (InterWiki prefix). |
| 372 | + * |
| 373 | + * @param string |
| 374 | + */ |
| 375 | + function setTarget($target) { |
| 376 | + $this->target=(string)$target; |
| 377 | + } |
| 378 | + |
| 379 | + /** |
| 380 | + * Does this namespace allow [[/subpages]]? |
| 381 | + * @return bool |
| 382 | + */ |
| 383 | + function allowsSubpages() { |
| 384 | + return $this->allowsSubpages; |
| 385 | + |
| 386 | + } |
| 387 | + |
| 388 | + /** |
| 389 | + * Should this namespace allow [[/subpages]]? |
| 390 | + * @param bool |
| 391 | + */ |
| 392 | + function setSubpages($subpages=true) { |
| 393 | + $this->allowsSubpages=(bool)$subpages; |
| 394 | + } |
| 395 | + |
| 396 | + /** |
| 397 | + * Return the default name for this namespace, if any. |
| 398 | + * The default name is the one all others redirect to. |
| 399 | + * |
| 400 | + * @return string |
| 401 | + */ |
| 402 | + function getDefaultName() { |
| 403 | + if(isset($this->defaultNameIndex) && array_key_exists($this->defaultNameIndex,$this->names)) { |
| 404 | + return $this->names[$this->defaultNameIndex]; |
| 405 | + } else { |
| 406 | + return null; |
| 407 | + } |
| 408 | + } |
| 409 | + |
| 410 | + /** |
| 411 | + Used when a default name is deleted, to assign a new one |
| 412 | + @return int - index to the first non-empty name of this namespace |
| 413 | + null if there are no non-empty names. |
| 414 | + */ |
| 415 | + function getNewDefaultNameIndex() { |
| 416 | + foreach($this->names as $nsi=>$name) { |
| 417 | + if(!empty($name)) { |
| 418 | + return $nsi; |
| 419 | + } |
| 420 | + } |
| 421 | + return null; |
| 422 | + } |
| 423 | + |
| 424 | + /** |
| 425 | + * Among the names of this namespace, which one should |
| 426 | + * be set as the default name? |
| 427 | + * @param int Key to the names array |
| 428 | + */ |
| 429 | + function setDefaultNameIndex($index) { |
| 430 | + $this->defaultNameIndex=$index; |
| 431 | + } |
| 432 | + |
| 433 | + /** |
| 434 | + * Among the names of this namespace, which one |
| 435 | + * should be "canonical" (i.e. not editable, and |
| 436 | + * assumed to exist under this name in other |
| 437 | + * wikis)? |
| 438 | + * @param int Key to the names array |
| 439 | + */ |
| 440 | + function setCanonicalNameIndex($index) { |
| 441 | + $this->canonicalNameIndex=$index; |
| 442 | + } |
| 443 | + |
| 444 | + /** |
| 445 | + * Add a name to the list of names for this |
| 446 | + * namespace. |
| 447 | + * @return index of the newly added name, |
| 448 | + * or NULL if hte name is not valid. |
| 449 | + */ |
| 450 | + function addName($name) { |
| 451 | + $index=count($this->names); |
| 452 | + if($this->isValidName($name)) { |
| 453 | + $name=strtr($name, ' ','_'); |
| 454 | + $this->names[$index]=$name; |
77 | 455 | return $index; |
78 | 456 | } else { |
79 | | - # FIXME |
80 | | - return $index + 1; |
| 457 | + return NULL; |
81 | 458 | } |
82 | 459 | } |
83 | 460 | |
84 | | - static function getSubject( $index ) { |
85 | | - if ( Namespace::isTalk( $index ) ) { |
86 | | - return $index - 1; |
| 461 | + /** |
| 462 | + * Return the key in the name list for a given |
| 463 | + * name. |
| 464 | + * @return int Matching key or NULL |
| 465 | + */ |
| 466 | + function getNameIndexForName($findname) { |
| 467 | + foreach($this->names as $nsi=>$name) { |
| 468 | + if($name==$findname) { |
| 469 | + return $nsi; |
| 470 | + } |
| 471 | + } |
| 472 | + return null; |
| 473 | + } |
| 474 | + |
| 475 | + /** |
| 476 | + * Change a namespace name. |
| 477 | + * @param $oldname The old name |
| 478 | + * @param $newname The new name |
| 479 | + * @param $checkvalid Does the new name have to be |
| 480 | + * valid? (is checked by save() in any case) |
| 481 | + */ |
| 482 | + function setName($oldname,$newname,$checkvalid=true) { |
| 483 | + if($checkvalid && !$this->isValidName($newname)) { |
| 484 | + return NULL; |
| 485 | + } |
| 486 | + $newname=strtr($newname, ' ','_'); |
| 487 | + $nsi=$this->getNameIndexForName($oldname); |
| 488 | + if(!is_null($nsi)) { |
| 489 | + $this->names[$nsi]=$newname; |
| 490 | + return true; |
87 | 491 | } else { |
88 | | - return $index; |
| 492 | + return false; |
89 | 493 | } |
90 | 494 | } |
| 495 | + |
| 496 | + /** |
| 497 | + * Is this a valid namespace name? Valid characters |
| 498 | + * are defined in the NS_CHAR constant. |
| 499 | + * @return bool |
| 500 | + */ |
| 501 | + function isValidName($name) { |
| 502 | + # Consist only of (at least one) valid char(s) |
| 503 | + if(preg_match("/^".NS_CHAR."+$/",$name)) { |
| 504 | + return true; |
| 505 | + } else { |
| 506 | + return false; |
| 507 | + } |
| 508 | + } |
91 | 509 | |
92 | 510 | /** |
93 | | - * Returns the canonical (English Wikipedia) name for a given index |
| 511 | + * How many pages does this namespace contain? |
| 512 | + * @return The number of pages |
| 513 | + */ |
| 514 | + function countPages() { |
| 515 | + $dbs =& wfGetDB(DB_SLAVE); |
| 516 | + return $dbs->selectField( |
| 517 | + 'page', |
| 518 | + 'count(*)', |
| 519 | + array('page_namespace'=>$this->getIndex()) |
| 520 | + ); |
| 521 | + } |
| 522 | + |
| 523 | + /** |
| 524 | + * Get the default name with spaces instead of |
| 525 | + * underscores. |
| 526 | + * @return string |
94 | 527 | */ |
95 | | - static function getCanonicalName( $index ) { |
96 | | - global $wgCanonicalNamespaceNames; |
97 | | - return $wgCanonicalNamespaceNames[$index]; |
| 528 | + function getFormattedDefaultName() { |
| 529 | + $ns=$this->getDefaultName(); |
| 530 | + return strtr($ns, '_',' '); |
98 | 531 | } |
| 532 | + /** |
| 533 | + * @return the key in the names array for the default |
| 534 | + * name of this namespace |
| 535 | + */ |
| 536 | + function getDefaultNameIndex() { |
| 537 | + return $this->defaultNameIndex; |
| 538 | + } |
99 | 539 | |
100 | 540 | /** |
101 | | - * Returns the index for a given canonical name, or NULL |
102 | | - * The input *must* be converted to lower case first |
| 541 | + * @return the key in the names array for the |
| 542 | + * canonical name of this namespace |
103 | 543 | */ |
104 | | - static function getCanonicalIndex( $name ) { |
105 | | - global $wgCanonicalNamespaceNames; |
106 | | - static $xNamespaces = false; |
107 | | - if ( $xNamespaces === false ) { |
108 | | - $xNamespaces = array(); |
109 | | - foreach ( $wgCanonicalNamespaceNames as $i => $text ) { |
110 | | - $xNamespaces[strtolower($text)] = $i; |
111 | | - } |
| 544 | + function getCanonicalNameIndex() { |
| 545 | + return $this->canonicalNameIndex; |
| 546 | + } |
| 547 | + |
| 548 | + /** |
| 549 | + * @return The canonical name associated with this namespace, or |
| 550 | + * NULL. Doubles as a static function which returns the same |
| 551 | + * given the namespace index. |
| 552 | + */ |
| 553 | + function getCanonicalName( $index = null ) { |
| 554 | + if( is_null( $index ) ) { |
| 555 | + $ns = $this; |
112 | 556 | } |
113 | | - if ( array_key_exists( $name, $xNamespaces ) ) { |
114 | | - return $xNamespaces[$name]; |
| 557 | + else { |
| 558 | + $nsstore = wfGetNamespaceStore(); |
| 559 | + $ns = $nsstore->getNamespaceObjectByIndex($index); |
| 560 | + } |
| 561 | + if(!is_null($ns->getCanonicalNameIndex())) { |
| 562 | + return $ns->names[$ns->getCanonicalNameIndex()]; |
115 | 563 | } else { |
116 | | - return NULL; |
| 564 | + return null; |
117 | 565 | } |
118 | 566 | } |
| 567 | + |
| 568 | + /** |
| 569 | + * An external handler class can be configured for a namespace. |
| 570 | + * It is expected in extensions/ClassName/ClassName.php by default |
| 571 | + * and should have a view(), edit() and delete() function. |
| 572 | + * |
| 573 | + * @param $classname |
| 574 | + */ |
| 575 | + function setHandlerClass($classname) { |
| 576 | + $this->handlerClass=$classname; |
| 577 | + } |
| 578 | + |
| 579 | + /** |
| 580 | + * @return name of the handler class (string) |
| 581 | + */ |
| 582 | + function getHandlerClass() { |
| 583 | + return $this->handlerClass; |
| 584 | + } |
| 585 | + /** |
| 586 | + * Returns the index for a given canonical name, or NULL |
| 587 | + * The input *must* be converted to lower case first |
| 588 | + */ |
| 589 | + static function getCanonicalIndex( $name ) { |
| 590 | + $nsstore = wfGetNamespaceStore(); |
| 591 | + return $nsstore->getIndexForName( $name ); |
| 592 | + } |
119 | 593 | |
120 | 594 | /** |
121 | 595 | * Can this namespace ever have a talk namespace? |
— | — | @@ -123,19 +597,49 @@ |
124 | 598 | static function canTalk( $index ) { |
125 | 599 | return( $index >= NS_MAIN ); |
126 | 600 | } |
127 | | - |
| 601 | + |
| 602 | + |
128 | 603 | /** |
129 | | - * Does this namespace contain content, for the purposes |
130 | | - * of calculating statistics, etc? |
131 | | - * |
132 | | - * @param $index Index to check |
133 | | - * @return bool |
| 604 | + * @param int Key to the names array of the name |
| 605 | + * which should be removed. |
134 | 606 | */ |
135 | | - public static function isContent( $index ) { |
136 | | - global $wgContentNamespaces; |
137 | | - return $index == NS_MAIN || in_array( $index, $wgContentNamespaces ); |
138 | | - } |
139 | | - |
| 607 | + function removeNameByIndex($index) { |
| 608 | + if(array_key_exists($index,$this->names)) { |
| 609 | + unset($this->names[$index]); |
| 610 | + return true; |
| 611 | + } else { |
| 612 | + return false; |
| 613 | + } |
| 614 | + } |
| 615 | + |
| 616 | + /** |
| 617 | + * Kill them all! Well, all the ones in this namespace |
| 618 | + * object. And only if we save(). |
| 619 | + */ |
| 620 | + function removeAllNames() { |
| 621 | + $this->names=array(); |
| 622 | + return true; |
| 623 | + } |
| 624 | + |
| 625 | + function save($overrideInterwiki=false, |
| 626 | + $testSave=false) { |
| 627 | + $nsstore = wfGetNamespaceStore(); |
| 628 | + return $nsstore->saveNamespace($this,$overrideInterwiki,$testSave); |
| 629 | + } |
| 630 | + |
| 631 | + /** |
| 632 | + * A simple shortcut to save() with the right parameters |
| 633 | + * to run in test mode. See save() documentation. |
| 634 | + */ |
| 635 | + function testSave($overrideInterwiki=false) { |
| 636 | + return $this->save($overrideInterwiki,true); |
| 637 | + } |
| 638 | + |
| 639 | + function deleteNamespace($deleteSystem=false) { |
| 640 | + $nsstore = wfGetNamespaceStore(); |
| 641 | + return $nsstore->deleteNamespace($this,$deleteSystem); |
| 642 | + } |
| 643 | + |
140 | 644 | } |
141 | 645 | |
142 | 646 | ?> |
Index: branches/wikidata/includes/SkinTemplate.php |
— | — | @@ -653,12 +653,14 @@ |
654 | 654 | !$this->mTitle->isTalkPage() && !$prevent_active_tabs, |
655 | 655 | '', true); |
656 | 656 | |
657 | | - $content_actions['talk'] = $this->tabAction( |
658 | | - $talkpage, |
659 | | - 'talk', |
660 | | - $this->mTitle->isTalkPage() && !$prevent_active_tabs, |
661 | | - '', |
662 | | - true); |
| 657 | + if(!is_null($talkpage)) { |
| 658 | + $content_actions['talk'] = $this->tabAction( |
| 659 | + $talkpage, |
| 660 | + 'talk', |
| 661 | + $this->mTitle->isTalkPage() && !$prevent_active_tabs, |
| 662 | + '', |
| 663 | + true); |
| 664 | + } |
663 | 665 | |
664 | 666 | wfProfileIn( "$fname-edit" ); |
665 | 667 | if ( $this->mTitle->quickUserCan( 'edit' ) && ( $this->mTitle->exists() || $this->mTitle->quickUserCan( 'create' ) ) ) { |
Index: branches/wikidata/includes/DefaultSettings.php |
— | — | @@ -1004,7 +1004,10 @@ |
1005 | 1005 | |
1006 | 1006 | // Permission to change users' group assignments |
1007 | 1007 | $wgGroupPermissions['bureaucrat']['userrights'] = true; |
| 1008 | +// Permission to change namespace definitions |
| 1009 | +$wgGroupPermissions['bureaucrat']['namespaces'] = true; |
1008 | 1010 | |
| 1011 | + |
1009 | 1012 | // Experimental permissions, not ready for production use |
1010 | 1013 | //$wgGroupPermissions['sysop']['deleterevision'] = true; |
1011 | 1014 | //$wgGroupPermissions['bureaucrat']['hiderevision'] = true; |
Index: branches/wikidata/includes/SpecialPage.php |
— | — | @@ -142,6 +142,9 @@ |
143 | 143 | 'Mytalk' => array( 'SpecialMytalk' ), |
144 | 144 | 'Mycontributions' => array( 'SpecialMycontributions' ), |
145 | 145 | 'Listadmins' => array( 'SpecialRedirectToSpecial', 'Listadmins', 'Listusers', 'sysop' ), |
| 146 | + |
| 147 | + 'Namespaces' => array( 'SpecialPage', 'Namespaces', 'namespaces'), |
| 148 | + |
146 | 149 | ); |
147 | 150 | |
148 | 151 | static public $mAliases; |
Index: branches/wikidata/includes/SpecialNamespaces.php |
— | — | @@ -0,0 +1,883 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * |
| 5 | + * @package MediaWiki |
| 6 | + * @subpackage SpecialPage |
| 7 | + */ |
| 8 | + |
| 9 | +/** |
| 10 | + * Constructor |
| 11 | + * Can display the main form or perform addition, changes, |
| 12 | + * pseudonamespace conversion or deletions. |
| 13 | + */ |
| 14 | +function wfSpecialNamespaces() { |
| 15 | + global $wgUser, $wgRequest; |
| 16 | + |
| 17 | + $action = $wgRequest->getVal( 'action' ); |
| 18 | + $f = new NamespaceForm(); |
| 19 | + |
| 20 | + if ( $action == 'submit' && $wgRequest->wasPosted() && |
| 21 | + $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) { |
| 22 | + if($wgRequest->getText('nsAction')=='addnamespaces') { |
| 23 | + $f->addNamespaces(); |
| 24 | + } elseif($wgRequest->getText('nsAction')=='changenamespaces') { |
| 25 | + $f->changeNamespaces(); |
| 26 | + } elseif($wgRequest->getText('nsAction')=='fixpseudonamespaces') { |
| 27 | + $f->fixPseudonamespaces(); |
| 28 | + } |
| 29 | + } elseif($action == 'delete') { |
| 30 | + $f->deleteNamespace(); |
| 31 | + } else { |
| 32 | + $f->showForm(); |
| 33 | + } |
| 34 | +} |
| 35 | + |
| 36 | +/** |
| 37 | +* |
| 38 | +* @package MediaWiki |
| 39 | +* @subpackage SpecialPage |
| 40 | +*/ |
| 41 | +class NamespaceForm { |
| 42 | + |
| 43 | +/** |
| 44 | +* |
| 45 | +* This is the main namespace manager form which gives access to |
| 46 | +* all namespace operations. |
| 47 | +* |
| 48 | +* @param $errorHeader if this is an error page, we need at least a headline |
| 49 | +* @param $errorBody wikitext for extended error descriptions |
| 50 | +* |
| 51 | +*/ |
| 52 | +function showForm( $errorHeader='', $errorBody='' ) { |
| 53 | + global $wgOut, $wgUser, $wgTitle; |
| 54 | + |
| 55 | + $wgOut->setPagetitle( wfMsg( 'namespaces' ) ); |
| 56 | + |
| 57 | + /* In case of an error, we generally just show what went wrong |
| 58 | + and continue displaying the main form */ |
| 59 | + if ( '' != $errorHeader ) { |
| 60 | + $wgOut->setSubtitle( wfMsg( 'transactionerror' ) ); |
| 61 | + $wgOut->addHTML( '<p class="error">' . htmlspecialchars($errorHeader) . '</p>'); |
| 62 | + if($errorBody) { |
| 63 | + $wgOut->addWikiText($errorBody); |
| 64 | + } |
| 65 | + } |
| 66 | + |
| 67 | + # Standard token to avoid remote form submission exploits |
| 68 | + $token = $wgUser->editToken(); |
| 69 | + $action = $wgTitle->escapeLocalURL( 'action=submit' ); |
| 70 | + $talksuffix = wfEscapeJsString(wfMsgForContent('talkpagesuffix')); |
| 71 | + |
| 72 | + $nsstore = wfGetNamespaceStore(); |
| 73 | + |
| 74 | + # For the namespace selection box |
| 75 | + $name_array = $nsstore->getFormattedDefaultNamespaces(true); |
| 76 | + |
| 77 | + $noparent = wfMsg('no_parent_namespace'); |
| 78 | + $name_array[key($name_array)-1] = $noparent; |
| 79 | + |
| 80 | + # Sort for foreach loops |
| 81 | + ksort($name_array); |
| 82 | + |
| 83 | + $wgOut->addWikiText( wfMsg( 'add_namespaces_header' ) ); |
| 84 | + |
| 85 | + # Prefill talk namespace field, but only for languages |
| 86 | + # where it's not disabled |
| 87 | + if($talksuffix != '-') { |
| 88 | + $talkpagejs= |
| 89 | +' onchange="if(!window.document.addnamespaces.nsTalkName.value && window.document.addnamespaces.nsName.value && window.document.addnamespaces.nsCreateTalk.checked) { window.document.addnamespaces.nsTalkName.value=window.document.addnamespaces.nsName.value+\''.$talksuffix.'\'; }"'; |
| 90 | + } else { |
| 91 | + $talkpagejs=''; |
| 92 | + } |
| 93 | + |
| 94 | + $addnshtmlform=' |
| 95 | +<div id="addnsForm"> |
| 96 | +<form name="addnamespaces" method="post" action="'.$action.'"> |
| 97 | +<table> |
| 98 | + <tr valign="top"> |
| 99 | + <td>'.wfMsg('add_namespace_default_name').'</td> |
| 100 | + <td> |
| 101 | + <input type="hidden" name="nsAction" value="addnamespaces" /> |
| 102 | + <input type="text" name="nsName" size="20"'.$talkpagejs.' /> |
| 103 | + </td> |
| 104 | + </tr> |
| 105 | + <tr valign="top"> |
| 106 | + <td>'.wfMsg('add_namespace_default_talk').'<br /></td> |
| 107 | + <td><input type="text" name="nsTalkName" size="20" /></td> |
| 108 | + </tr> |
| 109 | + <tr> |
| 110 | + <td colspan="2"> |
| 111 | + <label> |
| 112 | + <input type="checkbox" name="nsCreateTalk" checked="checked" />'.wfMsg('add_namespace_talk_confirm').' |
| 113 | + </label> |
| 114 | + </td> |
| 115 | + </tr> |
| 116 | +</table> |
| 117 | +<input type="hidden" name="wpEditToken" value="'.$token.'" /> |
| 118 | +<input type="submit" value="'.wfMsg('add_namespace_button').'" /> |
| 119 | +</form> |
| 120 | +</div> |
| 121 | +'; |
| 122 | + $wgOut->addHTML($addnshtmlform); |
| 123 | + |
| 124 | + $wgOut->addWikiText( wfMsg( 'modify_namespaces_header' ) ); |
| 125 | + |
| 126 | + // Array of messages to be used |
| 127 | + $nsMessages = array ( |
| 128 | + 'child_of', 'default_link_prefix', 'default_name', 'delete_name', |
| 129 | + 'existing_names', 'hide_in_lists', 'names', 'new_names', |
| 130 | + 'properties', 'save_changes', 'search_by_default', 'slot', |
| 131 | + 'support_subpages', 'system', |
| 132 | + ); |
| 133 | + |
| 134 | + // Build variables using the array. 'child_of' will do: |
| 135 | + // $namespace_child_of = wfMsg('namespace_child_of'); |
| 136 | + foreach( $nsMessages as $nsMessage ) { |
| 137 | + $msgName = 'namespace_' . $nsMessage ; |
| 138 | + $$msgName = wfMsg( $msgName ); |
| 139 | + } |
| 140 | + |
| 141 | + // Initialise the form |
| 142 | + $htmlform = <<<END |
| 143 | +<style type="text/css"> |
| 144 | +.mwNsAnnotation { |
| 145 | + font-size: 80%; |
| 146 | + color: #a0a0a0; |
| 147 | +} |
| 148 | +table.mwNsNames { |
| 149 | + border-spacing: 0 2px; |
| 150 | +} |
| 151 | +table.mwNsNames tr.mwDefaultName { |
| 152 | + background-color: #d2d5ff; |
| 153 | +} |
| 154 | +</style> |
| 155 | + |
| 156 | +<form name="changenamespaces" method="post" action="{$action}"> |
| 157 | +<input type="hidden" name="nsAction" value="changenamespaces" /> |
| 158 | +<input type="hidden" name="wpEditToken" value="{$token}" /> |
| 159 | +END; |
| 160 | + |
| 161 | + $nsobjects = $nsstore->getAllNamespaceObjects(); |
| 162 | + // Now we proceed each namespace |
| 163 | + foreach ($nsobjects as $ns) { |
| 164 | + $index = $ns->getIndex(); |
| 165 | + |
| 166 | + // Make sure the checkboxes remain checked: |
| 167 | + $subpages = $ns->allowsSubpages() ? ' checked' : ''; |
| 168 | + $searchdefault = $ns->isSearchedByDefault() ? ' checked' :''; |
| 169 | + $hidden = $ns->isHidden ? ' checked' : ''; |
| 170 | + |
| 171 | + $linkprefix = $ns->getTarget(); |
| 172 | + $parentslot = $ns->getParentIndex(); |
| 173 | + # maybe make HTMLnamespaceselector more flexible and use |
| 174 | + # it instead here |
| 175 | + if( !$ns->isSpecial() ) { |
| 176 | + $namespaceselect=$this->getSelector($name_array,$parentslot); |
| 177 | + |
| 178 | + // TODO : fix code below, maybe use HTMLForm ? |
| 179 | + |
| 180 | + $namespaceselect_html = <<<END |
| 181 | +<tr valign="top"> |
| 182 | + <td colspan="2">{$namespace_child_of}<br /> |
| 183 | + <select name="ns{$index}Parent" size="1">{$namespaceselect}</select> |
| 184 | + </td> |
| 185 | +</tr> |
| 186 | +END; |
| 187 | + $subpages_html = $this->checkRow( 'namespace_support_subpages', |
| 188 | + "ns{$index}Subpages", $ns->allowsSubpages() ); |
| 189 | + $searchdefault_html = $this->checkRow( 'namespace_search_by_default', |
| 190 | + "ns{$index}Search", $ns->isSearchedByDefault() ); |
| 191 | + $hide_html = $this->checkRow( 'namespace_hide_in_lists', |
| 192 | + "ns{$index}Hidden", $ns->isHidden() ); |
| 193 | + |
| 194 | + $target_html = <<<END |
| 195 | +<tr valign="top"> |
| 196 | + <td>{$namespace_default_link_prefix}</td> |
| 197 | + <td align="right"><input type="text" size="10" name="ns{$index}Linkprefix" value="{$linkprefix}" /></td> |
| 198 | +</tr> |
| 199 | +END; |
| 200 | + $target_html = $this->selectorRow( 'namespace_default_link_prefix', |
| 201 | + "ns{$index}Linkprefix", |
| 202 | + $nsstore->getIndexForName( $ns->getTarget() ) ); |
| 203 | + $special_html=''; |
| 204 | + |
| 205 | + } else { |
| 206 | + // For special namespace |
| 207 | + $namespaceselect_html = ''; |
| 208 | + $subpages_html = ''; |
| 209 | + $searchdefault_html = ''; |
| 210 | + $hide_html = ''; |
| 211 | + $target_html = ''; |
| 212 | + $special_namespace = wfMsg('special_namespace'); |
| 213 | + $special_html = '<tr valign="top"><td colspan="2"><em>'.$special_namespace.'</em></td></tr>' . "\n"; |
| 214 | + } |
| 215 | + |
| 216 | + |
| 217 | + $systemtype = $ns->getSystemType(); |
| 218 | + |
| 219 | + if( $ns->getSystemType() ) { |
| 220 | + // No delete link ? |
| 221 | + $systemtype_html = <<<END |
| 222 | +<tr valign="top"> |
| 223 | + <td><b><font color="red">{$namespace_system}</font></b></td> |
| 224 | + <td align="right"><b>{$systemtype}</b></td> |
| 225 | +</tr> |
| 226 | +END; |
| 227 | + $deletenamespace_html = ''; |
| 228 | + } else { |
| 229 | + // Give out a link to delete the namespace |
| 230 | + $sk = $wgUser->getSkin(); |
| 231 | + $delete_link = $sk->makeKnownLinkObj( $wgTitle, wfMsg('delete_namespace'), 'action=delete&ns=' . $index ); |
| 232 | + $deletenamespace_html = '<tr valign="top"><td colspan="2"><b>'.$delete_link.'</b></td></tr>' . "\n"; |
| 233 | + $systemtype_html=''; |
| 234 | + } |
| 235 | + |
| 236 | + |
| 237 | + // Yet another table of tables :p |
| 238 | + $htmlform .= <<<END |
| 239 | +<table class="specialnamespaces"> |
| 240 | +<tr valign="top"><td> |
| 241 | + <table border="0" style="margin-right:1em;" width="300"> |
| 242 | + <tr><th colspan="2">{$namespace_properties}</th></tr> |
| 243 | + <tr><td>{$namespace_slot}</td><td align="right">{$index}</td></tr> |
| 244 | +END; |
| 245 | + // Also add html part generated before |
| 246 | + $htmlform .= |
| 247 | + $systemtype_html |
| 248 | + . $special_html |
| 249 | + . $subpages_html |
| 250 | + . $searchdefault_html |
| 251 | + . $hide_html |
| 252 | + . $target_html |
| 253 | + . $namespaceselect_html |
| 254 | + . $deletenamespace_html |
| 255 | + ; |
| 256 | +$htmlform .= <<<END |
| 257 | + </table> |
| 258 | +</td><td> |
| 259 | + <table class="mwNsNames"> |
| 260 | + <tr><th colspan="3">{$namespace_names}</th></tr> |
| 261 | + <tr> |
| 262 | + <th>{$namespace_default_name}</th> |
| 263 | + <th align="left">{$namespace_existing_names}</th> |
| 264 | + <th>{$namespace_delete_name}</th> |
| 265 | + </tr> |
| 266 | +END; |
| 267 | + |
| 268 | + foreach ( $ns->names as $nsi => $nsname ) { |
| 269 | + $isDefault = ( $nsi === $ns->getDefaultNameIndex() ); |
| 270 | + $isCanonical = ( $nsi === $ns->getCanonicalNameIndex() ); |
| 271 | + $prettyName = str_replace( '_', ' ', $nsname ); |
| 272 | + |
| 273 | + $default = wfRadio( "ns{$index}Default", $nsi, $isDefault ); |
| 274 | + if( $isCanonical ) { |
| 275 | + $nameinput = htmlspecialchars( $prettyName ) . |
| 276 | + $this->annotation( wfMsg( 'canonicalname' ) ); |
| 277 | + $delete = "N/A"; |
| 278 | + } else { |
| 279 | + $nameinput = wfInput( "ns{$index}Name{$nsi}", 20, $prettyName ); |
| 280 | + if( $isDefault ) { |
| 281 | + $nameinput .= $this->annotation( wfMsg( 'defaultname' ) ); |
| 282 | + } |
| 283 | + $delete = wfCheck( "ns{$index}Delete{$nsi}" ); |
| 284 | + } |
| 285 | + $class = $isDefault ? 'mwDefaultName' : ''; |
| 286 | + $htmlform .= |
| 287 | +<<<END |
| 288 | + <tr valign="top" class="$class"> |
| 289 | + <td align="center">{$default}</td> |
| 290 | + <td>{$nameinput}</td> |
| 291 | + <td align="center">{$delete}</td> |
| 292 | + </tr> |
| 293 | +END; |
| 294 | + } |
| 295 | + |
| 296 | + $htmlform .= '<tr><th align="left" colspan="3">' . $namespace_new_names . '</th></tr>' ; |
| 297 | + |
| 298 | + # 3 blank namespace fields |
| 299 | + // FIXME cant we just count elements ? |
| 300 | + if( !is_null( $ns->names ) ) { |
| 301 | + end( $ns->names ); |
| 302 | + $highestName = key( $ns->names ) + 1; |
| 303 | + } else { |
| 304 | + $highestName = 0; |
| 305 | + } |
| 306 | + |
| 307 | + for( $i=$highestName; $i<$highestName+3; $i++) { |
| 308 | + $htmlform .= |
| 309 | +<<<END |
| 310 | + <tr valign="top"> |
| 311 | + <td align="center"><input type="radio" name="ns{$index}Default" value="{$i}" /></td> |
| 312 | + <td><input name="ns{$index}NewName{$i}" size="20" value="" /></td> |
| 313 | + <td align="center"> </td> |
| 314 | + </tr> |
| 315 | + |
| 316 | +END; |
| 317 | + } |
| 318 | + $htmlform .= |
| 319 | +<<<END |
| 320 | +</table> |
| 321 | + </td></tr> |
| 322 | +</table> |
| 323 | + |
| 324 | +END; |
| 325 | + } |
| 326 | + $htmlform.= |
| 327 | +<<<END |
| 328 | +<input type="submit" value="{$namespace_save_changes}" /> |
| 329 | +</form> |
| 330 | +<br/> |
| 331 | +END; |
| 332 | + |
| 333 | + |
| 334 | + // Ouput the form |
| 335 | + $wgOut->addHTML( $htmlform ); |
| 336 | + |
| 337 | + // Pseudonamespace converter |
| 338 | + $all_name_array = $nsstore->getFormattedDefaultNamespaces(true); |
| 339 | + $pseudons_select=$this->getSelector($all_name_array); |
| 340 | + $wgOut->addWikiText( wfMsg( 'fix_pseudonamespaces_header' ) ); |
| 341 | + $phtmlform =' |
| 342 | +<div id="fixPseudoNsForm"> |
| 343 | +<form name="fixpseudonamepaces" method="post" action="'.$action.'"> |
| 344 | +<input type="hidden" name="nsAction" value="fixpseudonamespaces" /> |
| 345 | +<input type="hidden" name="wpEditToken" value="'.$token.'" /> |
| 346 | +<table> |
| 347 | + <tr valign="top"> |
| 348 | + <td>'.wfMsgHtml('pseudonamespace_prefix').'</td> |
| 349 | + <td> |
| 350 | + <input type="text" name="nsPrefix" size="20" /> |
| 351 | + </td> |
| 352 | + </tr> |
| 353 | + <tr valign="top"> |
| 354 | + <td>'.wfMsgHtml('pseudonamespace_target').'<br /></td> |
| 355 | + <td><select name="nsConvertTo" size="1">'.$pseudons_select.'</select></td> |
| 356 | + </tr> |
| 357 | + <tr> |
| 358 | + <td colspan="2"> |
| 359 | + <label> |
| 360 | + <input type="checkbox" name="nsConvertTalk" checked="checked" />'.wfMsgHtml('pseudonamespace_converttalk').' |
| 361 | + </label> |
| 362 | + </td> |
| 363 | + </tr>'; |
| 364 | + if( $wgUser->isAllowed( 'merge_pseudonamespaces' ) ) { |
| 365 | + $phtmlform.=' |
| 366 | + <tr> |
| 367 | + <td colspan="2"> |
| 368 | + <label> |
| 369 | + <input type="checkbox" name="nsMerge" />'.wfMsg('pseudonamespace_merge').' |
| 370 | + </label> |
| 371 | + </td> |
| 372 | + </tr>'; |
| 373 | + } |
| 374 | + $phtmlform.=' |
| 375 | +</table> |
| 376 | +<input type="submit" value="'.wfMsgHtml('pseudonamespace_convert').'" /> |
| 377 | +</form> |
| 378 | +</div>'; |
| 379 | + $wgOut->addHTML($phtmlform); |
| 380 | + |
| 381 | +} |
| 382 | + |
| 383 | + /** |
| 384 | + * Add a new namespace with a single default name, |
| 385 | + * and optionally a talk namespace, also with a |
| 386 | + * defaultname. Uses the request data from the form. |
| 387 | + */ |
| 388 | + function addNamespaces() { |
| 389 | + |
| 390 | + global $wgOut, $wgUser, $wgRequest; |
| 391 | + |
| 392 | + $nsname = $wgRequest->getText('nsName'); |
| 393 | + $nstalkname = $wgRequest->getText('nsTalkName'); |
| 394 | + $nscreatetalk = $wgRequest->getBool('nsCreateTalk'); |
| 395 | + |
| 396 | + if(empty($nsname)) { |
| 397 | + $this->showForm( wfMsg('namespace_name_missing') ); |
| 398 | + } |
| 399 | + |
| 400 | + $ns = new Namespace(); |
| 401 | + $newnameindex = $ns->addName($nsname); |
| 402 | + |
| 403 | + if( is_null($newnameindex) ) { |
| 404 | + $this->showForm( |
| 405 | + wfMsg('namespace_error',$nsname), |
| 406 | + wfMsg('namespace_name_illegal_characters', NS_CHAR) |
| 407 | + ); |
| 408 | + return false; |
| 409 | + } |
| 410 | + |
| 411 | + $ns->setDefaultNameIndex($newnameindex); |
| 412 | + $nrv=$ns->testSave(); |
| 413 | + /* |
| 414 | + The only errors which can occur here should be |
| 415 | + name-related. |
| 416 | + */ |
| 417 | + if( $nrv[NS_RESULT] == NS_NAME_ISSUES ) { |
| 418 | + $this->showForm( |
| 419 | + wfMsg('namespace_error',$nsname), |
| 420 | + $this->nameIssues($nrv) |
| 421 | + ); |
| 422 | + return false; |
| 423 | + } |
| 424 | + |
| 425 | + $newnamespaceindex = $nrv[NS_SAVE_ID]; |
| 426 | + |
| 427 | + if( $nscreatetalk && !empty($nstalkname) ) { |
| 428 | + |
| 429 | + // Initialize a talk namespace |
| 430 | + $talkns = new Namespace(); |
| 431 | + $talkns->setParentIndex( $newnamespaceindex ); |
| 432 | + $talkns->setSubpages(); |
| 433 | + $newtalknameindex=$talkns->addName( $nstalkname ); |
| 434 | + $talkns->setDefaultNameIndex( $newtalknameindex ); |
| 435 | + |
| 436 | + // attempt to create it |
| 437 | + $trv = $talkns->testSave(); |
| 438 | + // Did it success ? |
| 439 | + if( $trv[NS_RESULT] != NS_CREATED ) { |
| 440 | + $this->showForm( |
| 441 | + wfMsg('talk_namespace_error',$nstalkname), |
| 442 | + $this->nameIssues($trv) |
| 443 | + ); |
| 444 | + return false; |
| 445 | + } |
| 446 | + } |
| 447 | + |
| 448 | + // We now have validated stuff, lets save for real. |
| 449 | + // No logic errors should occur beyond this point. |
| 450 | + $ns->save(); |
| 451 | + $complete = wfMsg( 'namespace_created', $nsname ); |
| 452 | + |
| 453 | + if( $nscreatetalk && !empty($nstalkname) ) { |
| 454 | + |
| 455 | + $talkns->save(); |
| 456 | + $complete .= ' '.wfMsg('talk_namespace_created'); |
| 457 | + } |
| 458 | + |
| 459 | + // Report success to user |
| 460 | + $wgOut->addWikiText($complete); |
| 461 | + $this->showForm(); |
| 462 | + |
| 463 | + $this->logNs('add',$nsname); |
| 464 | + if($nscreatetalk) { |
| 465 | + $this->logNs('add',$nstalkname); |
| 466 | + } |
| 467 | + } |
| 468 | + |
| 469 | + /** |
| 470 | + * Convenient access to the logging functions |
| 471 | + * @param $action - 'add','delete','modify' or 'pseudo' |
| 472 | + * @param $ns - name of the namespace |
| 473 | + * @param $tns - for pseudonamespaces, name of the target namespace |
| 474 | + */ |
| 475 | + function logNs($action,$ns='',$tns='') { |
| 476 | + $log = new LogPage( 'namespace' ); |
| 477 | + $dummyTitle = Title::makeTitle( 0, '' ); |
| 478 | + if($action=='pseudo') { |
| 479 | + $log->addEntry( $action,$dummyTitle,'',array($ns,$tns)); |
| 480 | + } elseif($action=='modify') { |
| 481 | + $log->addEntry( $action,$dummyTitle,''); |
| 482 | + } else { |
| 483 | + $log->addEntry( $action,$dummyTitle,'',array($ns)); |
| 484 | + } |
| 485 | + } |
| 486 | + |
| 487 | + /** |
| 488 | + * Modify, delete or add namespace names, set default names, |
| 489 | + * or change namespace properties. Uses the request data from |
| 490 | + * the form. Note that we have to create a new namespace object, |
| 491 | + * since we do not want to modify the "live" namespace until |
| 492 | + * we know that all the requested operations can be performed. |
| 493 | + * Nothing will be done unless every transaction can be completed |
| 494 | + * successfully. |
| 495 | + */ |
| 496 | + function changeNamespaces() { |
| 497 | + |
| 498 | + global $wgOut, $wgRequest; |
| 499 | + $nsstore = wfGetNamespaceStore(); |
| 500 | + $nsobjects = $nsstore->getAllNamespaceObjects(); |
| 501 | + |
| 502 | + $newns = array(); |
| 503 | + foreach( $nsobjects as $ns ) { |
| 504 | + $nsindex = $ns->getIndex(); |
| 505 | + $newns[$nsindex] = new Namespace(); |
| 506 | + $newns[$nsindex]->setIndex($nsindex); |
| 507 | + $newns[$nsindex]->setSystemType($ns->getSystemType()); |
| 508 | + |
| 509 | + if(!$ns->isSpecial()) { |
| 510 | + // Some variables names |
| 511 | + $subvar = "ns{$nsindex}Subpages"; |
| 512 | + $searchvar = "ns{$nsindex}Search"; |
| 513 | + $hiddenvar = "ns{$nsindex}Hidden"; |
| 514 | + $prefixvar = "ns{$nsindex}Linkprefix"; |
| 515 | + $parentvar = "ns{$nsindex}Parent"; |
| 516 | + |
| 517 | + // Get data submitted by user |
| 518 | + $subpages = $wgRequest->getBool($subvar); |
| 519 | + $searchdefault = $wgRequest->getBool($searchvar); |
| 520 | + $hidden = $wgRequest->getBool($hiddenvar); |
| 521 | + $prefix = $wgRequest->getText($prefixvar); |
| 522 | + $parent = $wgRequest->getIntOrNull($parentvar); |
| 523 | + |
| 524 | + // Initialise our new namespace |
| 525 | + $newns[$nsindex]->setSubpages($subpages); |
| 526 | + $newns[$nsindex]->setSearchedByDefault($searchdefault); |
| 527 | + $newns[$nsindex]->setHidden($hidden); |
| 528 | + $newns[$nsindex]->setTarget($prefix); |
| 529 | + |
| 530 | + if($nsstore->hasIndex($parent)) { |
| 531 | + $newns[$nsindex]->setParentIndex($parent); |
| 532 | + } |
| 533 | + } |
| 534 | + |
| 535 | + // Inherit namespace names |
| 536 | + $newns[$nsindex]->names = $ns->names; |
| 537 | + |
| 538 | + // This can never be changed by the user. |
| 539 | + $newns[$nsindex]->setCanonicalNameIndex($ns->getCanonicalNameIndex()); |
| 540 | + |
| 541 | + // New names, appended to end |
| 542 | + for($i=1;$i<=3;$i++) { |
| 543 | + $nvar = "ns{$nsindex}NewName{$i}"; |
| 544 | + if( $nname = $wgRequest->getText($nvar) ) { |
| 545 | + $newns[$nsindex]->addName($nname); |
| 546 | + } |
| 547 | + } |
| 548 | + |
| 549 | + # Changes and deletions. Do them last since they can |
| 550 | + # affect index slots of existing names. |
| 551 | + foreach( $ns->names as $nameindex=>$name ) { |
| 552 | + $var="ns{$nsindex}Name{$nameindex}"; |
| 553 | + if($req=$wgRequest->getText($var)) { |
| 554 | + #wfDebug("Name var $var contains $req\n"); |
| 555 | + |
| 556 | + # Alter name if necessary. |
| 557 | + if($req != $name) { |
| 558 | + # The last parameter means that we do not check if the |
| 559 | + # name is valid - this is done later for all names. |
| 560 | + $newns[$nsindex]->setName($name, $req, false); |
| 561 | + |
| 562 | + #wfDebug("Setting name $nameindex of namespace $nsindex to $req. Old name is $name.\n"); |
| 563 | + } |
| 564 | + } |
| 565 | + $delvar = "ns{$nsindex}Delete{$nameindex}"; |
| 566 | + if( $wgRequest->getInt($delvar) ) { |
| 567 | + #wfDebug("$delvar should be deleted.\n"); |
| 568 | + $newns[$nsindex]->removeNameByIndex($nameindex); |
| 569 | + } |
| 570 | + } |
| 571 | + |
| 572 | + $dvar = "ns{$nsindex}Default"; |
| 573 | + |
| 574 | + # Did the user select a default name? |
| 575 | + $dindex = $wgRequest->getIntOrNull($dvar); |
| 576 | + |
| 577 | + # If not, get the old one. |
| 578 | + if(is_null($dindex)) { |
| 579 | + $dindex=$ns->getDefaultNameIndex(); |
| 580 | + } |
| 581 | + |
| 582 | + # Does the name exist and is it non-empty? |
| 583 | + if( |
| 584 | + !is_null($dindex) |
| 585 | + && array_key_exists($dindex, |
| 586 | + $newns[$nsindex]->names) |
| 587 | + && !empty($newns[$nsindex]->names[$dindex]) |
| 588 | + ) { |
| 589 | + # Use this default name. |
| 590 | + $newns[$nsindex]->setDefaultNameIndex($dindex); |
| 591 | + } else { |
| 592 | + # We have lost our default name, perhaps it |
| 593 | + # was deleted. Get a new one if possible. |
| 594 | + $newns[$nsindex]->setDefaultNameIndex( |
| 595 | + $newns[$nsindex]->getNewDefaultNameIndex() |
| 596 | + ); |
| 597 | + } |
| 598 | + } |
| 599 | + |
| 600 | + foreach($newns as $nns) { |
| 601 | + $nrv = $nns->testSave(); |
| 602 | + if( $nrv[NS_RESULT] == NS_NAME_ISSUES ) { |
| 603 | + $this->showForm( |
| 604 | + wfMsg( |
| 605 | + 'namespace_error', |
| 606 | + $nns->getDefaultName()), |
| 607 | + $this->nameIssues($nrv) |
| 608 | + ); |
| 609 | + return false; |
| 610 | + } elseif($nrv[NS_RESULT] == NS_MISSING) { |
| 611 | + $this->showForm( |
| 612 | + wfmsg('namespace_has_gone_missing', |
| 613 | + $nns->getIndex()) |
| 614 | + ); |
| 615 | + return false; |
| 616 | + } |
| 617 | + } |
| 618 | + # Only do anything if everything can be done successfully. |
| 619 | + foreach($newns as $nns) { |
| 620 | + $nns->save(); |
| 621 | + } |
| 622 | + |
| 623 | + # Unfortunately, NS_IDENTICAL does not work consistently |
| 624 | + # atm, so we can only add a generic log entry. |
| 625 | + $this->logNs('modify'); |
| 626 | + |
| 627 | + # IMPORTANT: The namespace name indexes are unpredictable when |
| 628 | + # serialized, so we have to reload the definitions from the |
| 629 | + # database at this point; otherwise, there could be index |
| 630 | + # mismatches. |
| 631 | + $nsstore->load(true); |
| 632 | + |
| 633 | + # Return to the namespace manager with the changes made. |
| 634 | + $wgOut->addWikiText( wfMsg('namespace_changes_saved') ); |
| 635 | + $this->showForm(); |
| 636 | + |
| 637 | + return true; |
| 638 | + } |
| 639 | + |
| 640 | + /** |
| 641 | + * Creates a table showing problems with namespace name changes |
| 642 | + * or additions, based on result data from the save operation. |
| 643 | + * |
| 644 | + * @param array Namespace::save result array |
| 645 | + * @return string A HTML table with namespaces issues |
| 646 | + */ |
| 647 | + function nameIssues( $result ) { |
| 648 | + |
| 649 | + # Initialize table with heading |
| 650 | + $htmltable= |
| 651 | + '<table border="0" width="100%" cellspacing="5" cellpadding="5" rules="all">' |
| 652 | + . '<tr><th colspan="2">' . wfMsg('namespace_name_issues') . '</th></tr>' |
| 653 | + . '<tr><th>' . wfMsg('namespace_name_header') . '</th>' |
| 654 | + . '<th>'.wfMsg('namespace_issue_header').'</th></tr>' |
| 655 | + . "\n"; |
| 656 | + |
| 657 | + foreach($result[NS_ILLEGAL_NAMES] as $illegalName) { |
| 658 | + $htmltable.= |
| 659 | + '<tr><td>' |
| 660 | + .$illegalName. |
| 661 | + '</td><td>' |
| 662 | + .wfMsg('namespace_name_illegal_characters'). |
| 663 | + '</td></tr>'; |
| 664 | + } |
| 665 | + foreach($result[NS_DUPLICATE_NAMES] as $duplicateName) { |
| 666 | + $htmltable.= |
| 667 | + '<tr><td>' |
| 668 | + .$duplicateName. |
| 669 | + '</td><td>' |
| 670 | + .wfMsg('namespace_name_dupe'). |
| 671 | + '</td></tr>'; |
| 672 | + } |
| 673 | + foreach($result[NS_INTERWIKI_NAMES] as $interwikiName) { |
| 674 | + $htmltable.= |
| 675 | + '<tr><td>' |
| 676 | + .$interwikiName. |
| 677 | + '</td><td>' |
| 678 | + .wfMsg('namespace_name_interwiki'). |
| 679 | + '</td></tr>'; |
| 680 | + } |
| 681 | + foreach($result[NS_PREFIX_NAMES] as $prefixName) { |
| 682 | + $htmltable.= |
| 683 | + '<tr><td>' |
| 684 | + .$prefixName. |
| 685 | + '</td><td>' |
| 686 | + .wfMsg('namespace_name_prefix'). |
| 687 | + '</td></tr>'; |
| 688 | + } |
| 689 | + |
| 690 | + # Close table |
| 691 | + $htmltable .= '</table>'."\n"; |
| 692 | + return $htmltable; |
| 693 | + } |
| 694 | + |
| 695 | + /** |
| 696 | + * List of titles which exist in a real namespace |
| 697 | + * which duplicate page titles in a pseudonamespace. |
| 698 | + * |
| 699 | + * @return a wiki-formatted list of links |
| 700 | + */ |
| 701 | + function pseudoDupes( $prefix, $dupelist ) { |
| 702 | + $wikilist=wfMsg('pseudonamespace_title_dupes')."\n"; |
| 703 | + foreach($dupelist as $dupe) { |
| 704 | + $wikilist.="# [[{$prefix}:{$dupe}|{$dupe}]]\n"; |
| 705 | + } |
| 706 | + return $wikilist; |
| 707 | + } |
| 708 | + |
| 709 | + /** |
| 710 | + * Delete the namespace, using form data. |
| 711 | + * Checks for many error conditions: |
| 712 | + * - Namespace must exist |
| 713 | + * - System namespaces cannot be deleted |
| 714 | + * - Namespaces which are non-empty cannot be deleted |
| 715 | + */ |
| 716 | + function deleteNamespace() { |
| 717 | + global $wgOut,$wgRequest; |
| 718 | + |
| 719 | + $nsstore = wfGetNamespaceStore(); |
| 720 | + |
| 721 | + $nsid = $wgRequest->getInt('ns'); |
| 722 | + |
| 723 | + $ns = $nsstore->getNamespaceObjectByIndex($nsid); |
| 724 | + |
| 725 | + /* There should be no delete links for namespaces which cannot |
| 726 | + be deleted, but let's catch two possible problems just in case. */ |
| 727 | + if(is_null($ns) ) { |
| 728 | + $this->showForm( wfMsg('namespace_not_deletable') , wfMsg('namespace_has_gone_missing', $nsid) ); |
| 729 | + return false; |
| 730 | + } elseif( $ns->isSystemNamespace() ) { |
| 731 | + $this->showForm( wfMsg('namespace_not_deletable') , wfMsg('namespace_not_deletable_system', $nsid) ); |
| 732 | + return false; |
| 733 | + } |
| 734 | + |
| 735 | + $nsdelete = clone ($ns); |
| 736 | + $nsdeletename = $ns->getDefaultName(); |
| 737 | + $drv = $nsdelete->deleteNamespace(); |
| 738 | + |
| 739 | + if( empty($nsdeletename) ) { |
| 740 | + # At least show the index |
| 741 | + $nsdeletename = $nsid; |
| 742 | + } |
| 743 | + |
| 744 | + if( $drv[NS_RESULT] == NS_DELETED ) { |
| 745 | + $wgOut->addWikiText( wfMsg('namespace_deleted',$nsdeletename) ); |
| 746 | + $this->showForm(); |
| 747 | + $this->logNs('delete',$nsdeletename); |
| 748 | + return true; |
| 749 | + } elseif( $drv[NS_RESULT] == NS_NAME_ISSUES ) { |
| 750 | + $this->showForm( wfMsg('namespace_delete_error',$nsdeletename),$this->nameIssues($drv) ); |
| 751 | + return false; |
| 752 | + } elseif ($drv[NS_RESULT] == NS_HAS_PAGES) { |
| 753 | + /** TODO: link to Special:Allpages/namespace */ |
| 754 | + $this->showForm(wfMsg('namespace_delete_error',$nsdeletename), wfMsg('namespace_delete_not_empty')); |
| 755 | + return false; |
| 756 | + } else { |
| 757 | + $this->showForm( wfMsg('namespace_delete_error') ); |
| 758 | + return false; |
| 759 | + } |
| 760 | + } |
| 761 | + |
| 762 | + /** |
| 763 | + * Call Namespace::convertPseudonamespace with the correct |
| 764 | + * parameters and display the results. |
| 765 | + */ |
| 766 | + function fixPseudonamespaces() { |
| 767 | + |
| 768 | + global $wgOut, $wgRequest, $wgUser; |
| 769 | + # Merging into non-empty namespaces is generally prohibited |
| 770 | + $merge=$wgRequest->getBool('nsMerge'); |
| 771 | + if($merge && !in_array( 'merge_pseudonamespaces', $wgUser->getRights())) { |
| 772 | + $this->showForm( wfMsg('badaccess'), wfMsg('badaccesstext','[['.wfMsg('administrators').']]','fix_pseudonamespaces')); |
| 773 | + return false; |
| 774 | + } |
| 775 | + $prefix=$wgRequest->getText('nsPrefix'); |
| 776 | + $targetid=$wgRequest->getIntOrNull('nsConvertTo'); |
| 777 | + $converttalk = $wgRequest->getBool('nsConvertTalk'); |
| 778 | + if(empty($prefix) || is_null($targetid)) { |
| 779 | + $this->showForm (wfMsg('pseudonamespace_info_missing')); |
| 780 | + return false; |
| 781 | + |
| 782 | + } |
| 783 | + |
| 784 | + $nsstore = wfGetNamespaceStore(); |
| 785 | + $nsobj = $nsstore->getNamespaceObjectByIndex($targetid); |
| 786 | + |
| 787 | + $talktargetid=$nsobj->getTalk(); |
| 788 | + $talk4nsobj=$nsstore->getNamespaceObjectByIndex($targettalkid); |
| 789 | + |
| 790 | + if($converttalk && is_null($talktargetid)) { |
| 791 | + $this->showForm (wfMsg('pseudonamespace_target_talk_not_found')); |
| 792 | + return false; |
| 793 | + } |
| 794 | + |
| 795 | + $mainnsobj = $nsstore->getNamespaceObjectByIndex(NS_MAIN); |
| 796 | + $maintalknsobj = $nsstore->getNamespaceObjectByIndex(NS_TALK); |
| 797 | + $rv=$nsstore->convertPseudonamespace($prefix,$nsobj,$mainnsobj,$merge); |
| 798 | + if($rv[NS_RESULT]!=NS_PSEUDO_CONVERTED) { |
| 799 | + if(!($rv[NS_RESULT]==NS_PSEUDO_NOT_FOUND && $converttalk)) { |
| 800 | + $this->showPseudoError($rv,$targetid,$prefix); |
| 801 | + return false; |
| 802 | + } else { |
| 803 | + # Even if the prefix doesn't exist, we still |
| 804 | + # want to check for possible talk page content. |
| 805 | + $wgOut->addWikiText(wfMsg('pseudonamespace_not_found',$prefix)); |
| 806 | + $wgOut->addWikiText(wfMsg('pseudonamespace_trying_talk')); |
| 807 | + } |
| 808 | + } else { |
| 809 | + $wgOut->addWikiText(wfMsg('pseudonamespace_converted', $prefix, $nsobj->getDefaultName())); |
| 810 | + $this->logNs('pseudo',$prefix,$nsobj->getDefaultName()); |
| 811 | + } |
| 812 | + if($converttalk) { |
| 813 | + # A pseudonamespace, by definition, exists in the |
| 814 | + # main (unprefixed) namespace - therefore its talk |
| 815 | + # pages are NS_TALK. |
| 816 | + $trv=$nsstore->convertPseudonamespace($prefix,$talk4nsobj,$maintalknsobj,$merge); |
| 817 | + if($trv[NS_RESULT]!=NS_PSEUDO_CONVERTED) { |
| 818 | + $this->showPseudoError($trv,$talktargetid,$prefix); |
| 819 | + return false; |
| 820 | + } else { |
| 821 | + $wgOut->addWikiText(wfMsg('pseudonamespace_talk_converted')); |
| 822 | + } |
| 823 | + } |
| 824 | + $this->showForm(); |
| 825 | + } |
| 826 | + function showPseudoError($rv,$targetid,$prefix) { |
| 827 | + global $wgNamespaces; |
| 828 | + $istalk=$wgNamespaces[$targetid]->isTalk(); |
| 829 | + # For messages |
| 830 | + $talk=$istalk ? 'talk_' : ''; |
| 831 | + if($rv[NS_RESULT]==NS_PSEUDO_NOT_FOUND) { |
| 832 | + $this->showForm( wfMsg("pseudonamespace_{$talk}not_found",$prefix)); |
| 833 | + } elseif($rv[NS_RESULT]==NS_NON_EMPTY) { |
| 834 | + $this->showForm( |
| 835 | + wfMsg("pseudonamespace_{$talk}conversion_error",$prefix), |
| 836 | + wfMsg("pseudonamespace_cannot_merge")); |
| 837 | + } elseif($rv[NS_RESULT]==NS_DUPLICATE_TITLES) { |
| 838 | + $this->showForm( |
| 839 | + wfMsg("pseudonamespace_{$talk}conversion_error",$prefix), |
| 840 | + $this->pseudoDupes($wgNamespaces[$targetid]->getDefaultName(),$rv[NS_DUPLICATE_TITLE_LIST])); |
| 841 | + } |
| 842 | + } |
| 843 | + |
| 844 | + function getSelector($name_array,$parentslot=null) { |
| 845 | + $noparent = wfMsg('no_parent_namespace'); |
| 846 | + $namespaceselect=''; |
| 847 | + foreach ( $name_array as $arr_index => $arr_name ) { |
| 848 | + if( $arr_index < NS_MAIN && $arr_name!=$noparent ) |
| 849 | + continue; |
| 850 | + $list_option = ($arr_index == NS_MAIN ? wfMsg ( 'blanknamespace' ) : $arr_name); |
| 851 | + if(is_null($parentslot)) { |
| 852 | + $arr_name == $noparent ? $selected = ' selected="selected" ' : $selected=''; |
| 853 | + } else { |
| 854 | + $arr_index == $parentslot ? $selected = ' selected="selected"' : $selected=''; |
| 855 | + } |
| 856 | + $namespaceselect .= "\n<option value='$arr_index'$selected>$list_option</option>"; |
| 857 | + } |
| 858 | + return $namespaceselect; |
| 859 | + |
| 860 | + } |
| 861 | + |
| 862 | + function annotation( $text ) { |
| 863 | + return wfElement( 'div', array( 'class' => 'mwNsAnnotation' ), $text ); |
| 864 | + } |
| 865 | + |
| 866 | + function checkRow( $labelMessage, $name, $checked ) { |
| 867 | + return '<tr><td colspan="2">' . |
| 868 | + wfCheckLabel( wfMsg( $labelMessage ), $name, $name, $checked ) . |
| 869 | + "</td></tr>\n"; |
| 870 | + } |
| 871 | + |
| 872 | + function selectorRow( $labelMessage, $name, $selected ) { |
| 873 | + $nsstore = wfGetNamespaceStore(); |
| 874 | + return '<tr><td colspan="2">' . |
| 875 | + "<div>" . wfMsgHtml( $labelMessage ) . "</div>\n" . |
| 876 | + "<div>" . |
| 877 | + wfOpenElement( 'select', array( 'name' => $name ) ) . |
| 878 | + $this->getSelector( $nsstore->getFormattedDefaultNamespaces(true), $selected ) . |
| 879 | + wfCloseElement( 'select' ) . |
| 880 | + "</div>" . |
| 881 | + "</td></tr>\n"; |
| 882 | + } |
| 883 | +} |
| 884 | +?> |
Property changes on: branches/wikidata/includes/SpecialNamespaces.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 885 | + native |
Index: branches/wikidata/config/index.php |
— | — | @@ -909,6 +909,14 @@ |
910 | 910 | $article->updateRevisionOn( $wgDatabase, $revision ); |
911 | 911 | } |
912 | 912 | |
| 913 | + # Namespace defs will be needed for new and existing installations |
| 914 | + if($wgDatabase->selectField('namespace', 'COUNT(*)')==0) { |
| 915 | + print "<li>Bootstrapping namespace definitions..."; |
| 916 | + require_once '../maintenance/nsBootstrap.php'; |
| 917 | + print "Done."; |
| 918 | + } |
| 919 | + |
| 920 | + |
913 | 921 | /* Write out the config file now that all is well */ |
914 | 922 | print "<li style=\"list-style: none\">\n"; |
915 | 923 | print "<p>Creating LocalSettings.php...</p>\n\n"; |
Index: branches/wikidata/languages/messages/MessagesEn.php |
— | — | @@ -2783,6 +2783,89 @@ |
2784 | 2784 | 'size-megabytes' => '$1 MB', |
2785 | 2785 | 'size-gigabytes' => '$1 GB', |
2786 | 2786 | |
| 2787 | +# Namespace manager and namespace related messages |
| 2788 | +'transactionerror' => 'Error: could not complete transaction', |
| 2789 | +'nstarget' => "''In this namespace, unprefixed links will be treated as if they were prefixed with \"'''<tt>$1:</tt>'''\" (for example, <tt><nowiki>[[Scratchpad]]</nowiki></tt> will point to <tt><nowiki>[[$1:Scratchpad]]</nowiki></tt>). To suppress this behavior, add a colon in front of the title, e.g. <tt><nowiki>[[:Scratchpad]]</nowiki></tt>.''", |
| 2790 | +'namespace_missing_prefix'=>'Invalid namespace', |
| 2791 | + |
| 2792 | +'namespaces' => 'Namespace management', |
| 2793 | +'no_parent_namespace'=>'(Not a discussion namespace)', |
| 2794 | +'add_namespaces_header' => 'Create new namespaces here, then set all namespace-specific preferences below. In most cases, you will also want to add a matching talk (discussion) namespace.', |
| 2795 | +'add_namespace_default_name' => 'Namespace default name', |
| 2796 | +'add_namespace_default_talk' => 'Talk namespace default name', |
| 2797 | +'add_namespace_talk_confirm' => 'Yes, I want to create a talk namespace', |
| 2798 | +'add_namespace_button' => 'Add namespace', |
| 2799 | +'namespace_created'=>'Namespace "$1" was successfully created.', |
| 2800 | +'talk_namespace_created'=>'A corresponding talk (discussion) namespace has also been created.', |
| 2801 | +'namespace_name_issues'=>'There are issues with the following name(s):', |
| 2802 | +'namespace_name_illegal_characters'=>'The namespace name contains illegal characters. Only the following characters are allowed in namespace names: \'\'\'$1\'\'\'', |
| 2803 | +'namespace_name_missing'=>'You have not entered a namespace name.', |
| 2804 | +'namespace_name_dupe'=>'The namespace name is already in use. Please choose a different name.', |
| 2805 | +'namespace_name_not_empty'=>'Some pages still refer to this namespace using this name. It canot be deleted or renamed until all links are changed.', |
| 2806 | +'namespace_name_prefix'=>'There are pages which contain this name as a "pseudonamespace" (a text prefix which is not recognized as a real namespace). Use the pseudonamespace conversion form at the bottom to convert the pseudonamespace into a real one. You can create a temporary name first and change it later.', |
| 2807 | +'namespace_name_interwiki'=>'The name is already used as an Interwiki prefix to link to another wiki site. This Interwiki prefix would become unusable if this name was used.', |
| 2808 | +'namespace_name_linked'=>'The namespace name is still linked to from some pages. If it was deleted, these links would be broken.', |
| 2809 | +'namespace_error'=>'The namespace "$1" cannot be created or modified.', |
| 2810 | +'namespace_delete_error'=>'The namespace "$1" cannot be deleted.', |
| 2811 | +'namespace_delete_not_empty'=>'The namespace still contains pages. Only empty namespaces can be removed.', |
| 2812 | +'namespace_deleted'=>'The namespace "$1" has been deleted successfully.', |
| 2813 | +'namespace_changes_saved'=>'The namespace changes have been saved.', |
| 2814 | +'talk_namespace_error'=>'The corresponding discussion (talk) namespace "$1" cannot be created. No namespaces have been added.', |
| 2815 | +'modify_namespaces_header' => 'Configure namespaces here. System namespaces cannot be deleted.', |
| 2816 | +'canonicalname' => 'canonical name', |
| 2817 | +'defaultname' => 'primary name', |
| 2818 | +'namespace_name_header'=>'Namespace name', |
| 2819 | +'namespace_issue_header'=>'Problem', |
| 2820 | +'fix_pseudonamespaces_header'=>'You can convert "pseudonamespaces" (titles with a consistent prefix like "Cookbook:" which is not a real namespace) into real namespaces here, by providing the prefix and selecting a target namespace. If the namespace does not yet exist, create a temporary name for it above first; you can change it after the conversion.', |
| 2821 | +'pseudonamespace_prefix'=>'Pseudonamespace prefix', |
| 2822 | +'pseudonamespace_target'=>'Convert into', |
| 2823 | +'pseudonamespace_converttalk'=>'Try to convert discussion pages into the associated talk namespace', |
| 2824 | +'pseudonamespace_convert'=>'Convert', |
| 2825 | +'pseudonamespace_converted'=>'The pseudonamespace "$1" was successfully converted into the real namespace with the default name "$2".', |
| 2826 | +'pseudonamespace_trying_talk'=>'Looking for talk pages anyway.', |
| 2827 | +'pseudonamespace_talk_converted'=>'The associated discussion pages were converted.', |
| 2828 | +'pseudonamespace_conversion_error'=>'There was a problem converting the pseudonamespace "$1".', |
| 2829 | +'pseudonamespace_talk_conversion_error'=>'There was a problem converting the talk pages of the pseudonamespace.', |
| 2830 | +'pseudonamespace_info_missing'=>'You did not provide a namespace name or target namespace.', |
| 2831 | +'pseudonamespace_not_found'=>'No pages with the prefix "$1" were found!', |
| 2832 | +'pseudonamespace_talk_not_found'=>'No talk pages associated with this pseudonamespace were found.', |
| 2833 | +'pseudonamespace_target_talk_not_found'=>'The target namespace does not appear to have an associated talk namespace!', |
| 2834 | +'pseudonamespace_merge'=>'Merge into a namespace that contains pages (MAY BE IRREVERSIBLE)', |
| 2835 | +'pseudonamespace_cannot_merge'=>'The target namespace contains pages. You have not specified that you want to merge into a non-empty namespace, or do not have permission to do so. This action is restricted because it is potentially irreversible.', |
| 2836 | +'pseudonamespace_title_dupes'=>'The target namespace already contains pages with the following titles:', |
| 2837 | + |
| 2838 | +# Namespace logging |
| 2839 | +'namespacelogpage'=>'Namespace_log', |
| 2840 | +'namespacelogtext'=>'This is a log of all changes made through the namespace manager.', |
| 2841 | +'namespaceaddlog'=>'Added namespace "$2"', |
| 2842 | +'namespacedeletelog'=>'Deleted namespace "$2"', |
| 2843 | +'namespacemodifylog'=>'Modified namespace definitions', |
| 2844 | +'namespacepseudolog'=>'Converted pseudonamespace "$2" into real namespace "$3"', |
| 2845 | + |
| 2846 | +# This is appended via JavaScript to the entered namsepace name |
| 2847 | +# as a suggested talkpage name in Special:Namespaces. If set to '-', |
| 2848 | +# it will not be used. |
| 2849 | +'talkpagesuffix' => ' talk', |
| 2850 | +'special_namespace'=>'This is a special namespace that cannot contain pages.', |
| 2851 | +'namespace_child_of'=>'Discussion namespace of:', |
| 2852 | +'namespace_support_subpages'=>'Support subpages', |
| 2853 | +'namespace_search_by_default'=>'Include in search results', |
| 2854 | +'namespace_hide_in_lists'=>'Hide in lists', |
| 2855 | +'namespace_default_link_prefix'=>'Unmarked links go to:', |
| 2856 | +'namespace_system'=>'System namespace', |
| 2857 | +'delete_namespace'=>'Delete namespace', |
| 2858 | +'namespace_properties'=>'Namespace properties', |
| 2859 | +'namespace_slot'=>'Internal slot', |
| 2860 | +'namespace_names'=>'Names', |
| 2861 | +'namespace_existing_names'=>'Existing names', |
| 2862 | +'namespace_new_names'=>'New names', |
| 2863 | +'namespace_default_name'=>'Default', |
| 2864 | +'namespace_delete_name'=>'Delete', |
| 2865 | +'namespace_save_changes'=>'Save changes', |
| 2866 | +'namespace_not_deletable'=>'The namespace cannot be deleted.', |
| 2867 | +'namespace_has_gone_missing'=>'A namespace with the number $1 was not found.', |
| 2868 | +'namespace_not_deletable_system'=>'The namespace with the number $1 is a system namespace which is required for the operation of MediaWiki.', |
| 2869 | + |
2787 | 2870 | ); |
2788 | 2871 | |
2789 | 2872 | ?> |
Index: branches/wikidata/languages/Language.php |
— | — | @@ -176,14 +176,52 @@ |
177 | 177 | return $this->bookstoreList; |
178 | 178 | } |
179 | 179 | |
180 | | - /** |
| 180 | + /**#@+ |
| 181 | + * |
| 182 | + * Only used for migrating older version of MediaWiki to the |
| 183 | + * current version, and for populating the database in a fresh |
| 184 | + * install. |
181 | 185 | * @return array |
182 | | - */ |
183 | | - function getNamespaces() { |
| 186 | + * |
| 187 | + */ |
| 188 | + function getNamespaceSynonymsBootstrap() { |
| 189 | + return array( |
| 190 | + NS_IMAGE => array( 'Image', 'Sound', 'Video' ), |
| 191 | + NS_IMAGE_TALK => array( 'Image_talk', 'Sound_talk', 'Video_talk' ) |
| 192 | + ); |
| 193 | + } |
| 194 | + |
| 195 | + function getNamespacesBootstrap() { |
184 | 196 | $this->load(); |
185 | 197 | return $this->namespaceNames; |
186 | 198 | } |
187 | 199 | |
| 200 | + /**#@+ |
| 201 | + * @deprecated |
| 202 | + * |
| 203 | + */ |
| 204 | + function getNamespaceSynonyms() { |
| 205 | + $nsstore = wfGetNamespaceStore(); |
| 206 | + $defns = $nsstore->getDefaultNamespaces(); |
| 207 | + $allns = $nsstore->getAllNamespacesArray(); |
| 208 | + foreach ($defns as $nskey => $nsstring) { |
| 209 | + $aliases = array_diff($allns[$nskey],array($nsstring)); |
| 210 | + if(empty($aliases)) { |
| 211 | + unset($allns[$nskey]); |
| 212 | + } |
| 213 | + else { |
| 214 | + $allns[$ns]=$aliases; |
| 215 | + } |
| 216 | + } |
| 217 | + return $allns; |
| 218 | + } |
| 219 | + |
| 220 | + function getNamespaces() { |
| 221 | + $nsstore = wfGetNamespaceStore(); |
| 222 | + return $nsstore->getDefaultNamespaces(); |
| 223 | + } |
| 224 | + |
| 225 | + |
188 | 226 | /** |
189 | 227 | * A convenience function that returns the same thing as |
190 | 228 | * getNamespaces() except with the array values changed to ' ' |
Index: branches/wikidata/RELEASE-NOTES |
— | — | @@ -30,6 +30,8 @@ |
31 | 31 | |
32 | 32 | == Changes since 1.9 == |
33 | 33 | |
| 34 | +* Namespace definitions moved into the database, new namespace manager (Special:Namespaces) |
| 35 | + available to bureaucrats to change them. Various new namespace-related properties. |
34 | 36 | * (bug 7292) Fix site statistics when moving pages in/out of content namespaces |
35 | 37 | * (bug 6937) Introduce "statistics-footer" message, appended to Special:Statistics |
36 | 38 | * (bug 8531) Correct local name of Lingála (patch by Raymond) |