Index: branches/hashar/includes/LinksUpdate.php |
— | — | @@ -173,29 +173,60 @@ |
174 | 174 | |
175 | 175 | wfProfileOut( $fname ); |
176 | 176 | } |
| 177 | + |
| 178 | + /** |
| 179 | + * Invalidate the cache of a list of pages from a single namespace |
| 180 | + * |
| 181 | + * @param integer $namespace |
| 182 | + * @param array $dbkeys |
| 183 | + */ |
| 184 | + function invalidatePages( $namespace, $dbkeys ) { |
| 185 | + $fname = 'LinksUpdate::invalidatePages'; |
| 186 | + |
| 187 | + if ( !count( $dbkeys ) ) { |
| 188 | + return; |
| 189 | + } |
| 190 | + |
| 191 | + /** |
| 192 | + * Determine which pages need to be updated |
| 193 | + * This is necessary to prevent the job queue from smashing the DB with |
| 194 | + * large numbers of concurrent invalidations of the same page |
| 195 | + */ |
| 196 | + $now = $this->mDb->timestamp(); |
| 197 | + $ids = array(); |
| 198 | + $res = $this->mDb->select( 'page', array( 'page_id' ), |
| 199 | + array( |
| 200 | + 'page_namespace' => $namespace, |
| 201 | + 'page_title IN (' . $this->mDb->makeList( $dbkeys ) . ')', |
| 202 | + 'page_touched < ' . $this->mDb->addQuotes( $now ) |
| 203 | + ), $fname |
| 204 | + ); |
| 205 | + while ( $row = $this->mDb->fetchObject( $res ) ) { |
| 206 | + $ids[] = $row->page_id; |
| 207 | + } |
| 208 | + if ( !count( $ids ) ) { |
| 209 | + return; |
| 210 | + } |
| 211 | + |
| 212 | + /** |
| 213 | + * Do the update |
| 214 | + * We still need the page_touched condition, in case the row has changed since |
| 215 | + * the non-locking select above. |
| 216 | + */ |
| 217 | + $this->mDb->update( 'page', array( 'page_touched' => $now ), |
| 218 | + array( |
| 219 | + 'page_id IN (' . $this->mDb->makeList( $ids ) . ')', |
| 220 | + 'page_touched < ' . $this->mDb->addQuotes( $now ) |
| 221 | + ), $fname |
| 222 | + ); |
| 223 | + } |
177 | 224 | |
178 | 225 | function invalidateCategories( $cats ) { |
179 | | - $fname = 'LinksUpdate::invalidateCategories'; |
180 | | - if ( count( $cats ) ) { |
181 | | - $this->mDb->update( 'page', array( 'page_touched' => $this->mDb->timestamp() ), |
182 | | - array( |
183 | | - 'page_namespace' => NS_CATEGORY, |
184 | | - 'page_title IN (' . $this->mDb->makeList( array_keys( $cats ) ) . ')' |
185 | | - ), $fname |
186 | | - ); |
187 | | - } |
| 226 | + $this->invalidatePages( NS_CATEGORY, array_keys( $cats ) ); |
188 | 227 | } |
189 | 228 | |
190 | 229 | function invalidateImageDescriptions( $images ) { |
191 | | - $fname = 'LinksUpdate::invalidateImageDescriptions'; |
192 | | - if ( count( $images ) ) { |
193 | | - $this->mDb->update( 'page', array( 'page_touched' => $this->mDb->timestamp() ), |
194 | | - array( |
195 | | - 'page_namespace' => NS_IMAGE, |
196 | | - 'page_title IN (' . $this->mDb->makeList( array_keys( $images ) ) . ')' |
197 | | - ), $fname |
198 | | - ); |
199 | | - } |
| 230 | + $this->invalidatePages( NS_IMAGE, array_keys( $images ) ); |
200 | 231 | } |
201 | 232 | |
202 | 233 | function dumbTableUpdate( $table, $insertions, $fromField ) { |
Index: branches/hashar/includes/SpecialNewpages.php |
— | — | @@ -106,6 +106,47 @@ |
107 | 107 | } |
108 | 108 | return parent::feedItemDesc( $row ); |
109 | 109 | } |
| 110 | + |
| 111 | + /** |
| 112 | + * Show a namespace selection form for filtering |
| 113 | + * |
| 114 | + * @return string |
| 115 | + */ |
| 116 | + function getPageHeader() { |
| 117 | + $thisTitle = Title::makeTitle( NS_SPECIAL, $this->getName() ); |
| 118 | + $form = wfOpenElement( 'form', array( |
| 119 | + 'method' => 'post', |
| 120 | + 'action' => $thisTitle->getLocalUrl() ) ); |
| 121 | + $form .= wfElement( 'label', array( 'for' => 'namespace' ), |
| 122 | + wfMsg( 'namespace' ) ) . ' '; |
| 123 | + $form .= HtmlNamespaceSelector( $this->namespace ); |
| 124 | + # Preserve the offset and limit |
| 125 | + $form .= wfElement( 'input', array( |
| 126 | + 'type' => 'hidden', |
| 127 | + 'name' => 'offset', |
| 128 | + 'value' => $this->offset ) ); |
| 129 | + $form .= wfElement( 'input', array( |
| 130 | + 'type' => 'hidden', |
| 131 | + 'name' => 'limit', |
| 132 | + 'value' => $this->limit ) ); |
| 133 | + $form .= wfElement( 'input', array( |
| 134 | + 'type' => 'submit', |
| 135 | + 'name' => 'submit', |
| 136 | + 'id' => 'submit', |
| 137 | + 'value' => wfMsg( 'allpagessubmit' ) ) ); |
| 138 | + $form .= wfCloseElement( 'form' ); |
| 139 | + return( $form ); |
| 140 | + } |
| 141 | + |
| 142 | + /** |
| 143 | + * Link parameters |
| 144 | + * |
| 145 | + * @return array |
| 146 | + */ |
| 147 | + function linkParameters() { |
| 148 | + return( array( 'namespace' => $this->namespace ) ); |
| 149 | + } |
| 150 | + |
110 | 151 | } |
111 | 152 | |
112 | 153 | /** |
— | — | @@ -136,7 +177,11 @@ |
137 | 178 | } |
138 | 179 | } |
139 | 180 | } |
| 181 | + } else { |
| 182 | + if( $ns = $wgRequest->getInt( 'namespace', 0 ) ) |
| 183 | + $namespace = $ns; |
140 | 184 | } |
| 185 | + |
141 | 186 | if ( ! isset( $shownavigation ) ) |
142 | 187 | $shownavigation = ! $specialPage->including(); |
143 | 188 | |
Index: branches/hashar/includes/SpecialImport.php |
— | — | @@ -122,6 +122,7 @@ |
123 | 123 | */ |
124 | 124 | class WikiRevision { |
125 | 125 | var $title = NULL; |
| 126 | + var $id = 0; |
126 | 127 | var $timestamp = "20010115000000"; |
127 | 128 | var $user = 0; |
128 | 129 | var $user_text = ""; |
— | — | @@ -133,6 +134,10 @@ |
134 | 135 | $this->title = Title::newFromText( $text ); |
135 | 136 | } |
136 | 137 | |
| 138 | + function setID( $id ) { |
| 139 | + $this->id = $id; |
| 140 | + } |
| 141 | + |
137 | 142 | function setTimestamp( $ts ) { |
138 | 143 | # 2003-08-05T18:30:02Z |
139 | 144 | $this->timestamp = wfTimestamp( TS_MW, $ts ); |
— | — | @@ -162,6 +167,10 @@ |
163 | 168 | return $this->title; |
164 | 169 | } |
165 | 170 | |
| 171 | + function getID() { |
| 172 | + return $this->id; |
| 173 | + } |
| 174 | + |
166 | 175 | function getTimestamp() { |
167 | 176 | return $this->timestamp; |
168 | 177 | } |
— | — | @@ -468,6 +477,11 @@ |
469 | 478 | $this->workTitle = $this->appenddata; |
470 | 479 | $this->pageCallback( $this->workTitle ); |
471 | 480 | break; |
| 481 | + case "id": |
| 482 | + if ( $this->parenttag == 'revision' ) { |
| 483 | + $this->workRevision->setID( $this->appenddata ); |
| 484 | + } |
| 485 | + break; |
472 | 486 | case "text": |
473 | 487 | $this->workRevision->setText( $this->appenddata ); |
474 | 488 | break; |
Index: branches/hashar/includes/SpecialUnusedtemplates.php |
— | — | @@ -47,7 +47,8 @@ |
48 | 48 | } |
49 | 49 | |
50 | 50 | function getPageHeader() { |
51 | | - return wfMsgHtml( 'unusedtemplatestext' ); |
| 51 | + global $wgOut; |
| 52 | + return $wgOut->parse( wfMsg( 'unusedtemplatestext' ) ); |
52 | 53 | } |
53 | 54 | |
54 | 55 | } |
Index: branches/hashar/includes/SpecialVersion.php |
— | — | @@ -38,7 +38,7 @@ |
39 | 39 | } |
40 | 40 | |
41 | 41 | /**#@+ |
42 | | - * @access private |
| 42 | + * @private |
43 | 43 | */ |
44 | 44 | |
45 | 45 | /** |
Index: branches/hashar/includes/SpecialMostcategories.php |
— | — | @@ -34,7 +34,7 @@ |
35 | 35 | FROM $categorylinks |
36 | 36 | LEFT JOIN $page ON cl_from = page_id |
37 | 37 | WHERE page_namespace = " . NS_MAIN . " |
38 | | - GROUP BY cl_from, page_namespace, page_title |
| 38 | + GROUP BY cl_from |
39 | 39 | HAVING COUNT(*) > 1 |
40 | 40 | "; |
41 | 41 | } |
Index: branches/hashar/includes/LinkBatch.php |
— | — | @@ -47,6 +47,20 @@ |
48 | 48 | } |
49 | 49 | |
50 | 50 | /** |
| 51 | + * Returns true if no pages have been added, false otherwise. |
| 52 | + */ |
| 53 | + function isEmpty() { |
| 54 | + return ($this->getSize() == 0); |
| 55 | + } |
| 56 | + |
| 57 | + /** |
| 58 | + * Returns the size of the batch. |
| 59 | + */ |
| 60 | + function getSize() { |
| 61 | + return count( $this->data ); |
| 62 | + } |
| 63 | + |
| 64 | + /** |
51 | 65 | * Do the query and add the results to the LinkCache object |
52 | 66 | * Return an array mapping PDBK to ID |
53 | 67 | */ |
— | — | @@ -100,7 +114,7 @@ |
101 | 115 | $fname = 'LinkBatch::doQuery'; |
102 | 116 | $namespaces = array(); |
103 | 117 | |
104 | | - if ( !count( $this->data ) ) { |
| 118 | + if ( $this->isEmpty() ) { |
105 | 119 | return false; |
106 | 120 | } |
107 | 121 | wfProfileIn( $fname ); |
Index: branches/hashar/includes/SpecialWantedpages.php |
— | — | @@ -52,7 +52,7 @@ |
53 | 53 | } |
54 | 54 | |
55 | 55 | /** |
56 | | - * Fetch user page links and cache their existence |
| 56 | + * Cache page existence for performance |
57 | 57 | */ |
58 | 58 | function preprocessResults( &$db, &$res ) { |
59 | 59 | $batch = new LinkBatch; |
— | — | @@ -70,17 +70,42 @@ |
71 | 71 | function formatResult( $skin, $result ) { |
72 | 72 | global $wgContLang; |
73 | 73 | |
74 | | - $nt = Title::makeTitle( $result->namespace, $result->title ); |
75 | | - $text = $wgContLang->convert( $nt->getPrefixedText() ); |
76 | | - $plink = $this->isCached() ? |
77 | | - $skin->makeLinkObj( $nt, $text ) : |
78 | | - $skin->makeBrokenLink( $nt->getPrefixedText(), $text ); |
| 74 | + $title = Title::makeTitleSafe( $result->namespace, $result->title ); |
79 | 75 | |
80 | | - $nl = wfMsg( 'nlinks', $result->value ); |
81 | | - $nlink = $skin->makeKnownLink( $wgContLang->specialPage( 'Whatlinkshere' ), $nl, 'target=' . $nt->getPrefixedURL() ); |
82 | | - |
83 | | - return $this->nlinks ? "$plink ($nlink)" : $plink; |
| 76 | + if( $this->isCached() ) { |
| 77 | + # Check existence; which is stored in the link cache |
| 78 | + if( !$title->exists() ) { |
| 79 | + # Make a redlink |
| 80 | + $pageLink = $skin->makeBrokenLinkObj( $title ); |
| 81 | + } else { |
| 82 | + # Make a struck-out blue link |
| 83 | + $pageLink = "<s>" . $skin->makeKnownLinkObj( $title ) . "</s>"; |
| 84 | + } |
| 85 | + } else { |
| 86 | + # Not cached? Don't bother checking existence; it can't |
| 87 | + $pageLink = $skin->makeBrokenLinkObj( $title ); |
| 88 | + } |
| 89 | + |
| 90 | + # Make a link to "what links here" if it's required |
| 91 | + $wlhLink = $this->nlinks |
| 92 | + ? " (" . $this->makeWlhLink( $title, $skin, wfMsgHtml( 'nlinks', $result->value ) ) . ")" |
| 93 | + : ""; |
| 94 | + |
| 95 | + return "{$pageLink}{$wlhLink}"; |
84 | 96 | } |
| 97 | + |
| 98 | + /** |
| 99 | + * Make a "what links here" link for a specified title |
| 100 | + * @param $title Title to make the link for |
| 101 | + * @param $skin Skin to use |
| 102 | + * @param $text Link text |
| 103 | + * @return string |
| 104 | + */ |
| 105 | + function makeWlhLink( &$title, &$skin, $text ) { |
| 106 | + $wlhTitle = Title::makeTitle( NS_SPECIAL, 'Whatlinkshere' ); |
| 107 | + return $skin->makeKnownLinkObj( $wlhTitle, $text, 'target=' . $title->getPrefixedUrl() ); |
| 108 | + } |
| 109 | + |
85 | 110 | } |
86 | 111 | |
87 | 112 | /** |
Index: branches/hashar/includes/SpecialWatchlist.php |
— | — | @@ -90,6 +90,7 @@ |
91 | 91 | if( $wl->removeWatch() === false ) { |
92 | 92 | $wgOut->addHTML( "<br />\n" . wfMsg( 'couldntremove', htmlspecialchars($one) ) ); |
93 | 93 | } else { |
| 94 | + wfRunHooks('UnwatchArticle', array(&$wgUser, new Article($t))); |
94 | 95 | $wgOut->addHTML( ' (' . htmlspecialchars($one) . ')' ); |
95 | 96 | } |
96 | 97 | } else { |
Index: branches/hashar/includes/DefaultSettings.php |
— | — | @@ -1623,7 +1623,7 @@ |
1624 | 1624 | '/^Mozilla\/4\.[^ ]+ .*?\((?!compatible).*; [UIN]/', |
1625 | 1625 | |
1626 | 1626 | /** |
1627 | | - * MSIE on Mac OS 9 is teh sux0r, converts þ to <thorn>, ð to <eth>, Þ to <THORN> and �? to <ETH> |
| 1627 | + * MSIE on Mac OS 9 is teh sux0r, converts þ to <thorn>, ð to <eth>, Þ to <THORN> and Ð to <ETH> |
1628 | 1628 | * |
1629 | 1629 | * Known useragents: |
1630 | 1630 | * - Mozilla/4.0 (compatible; MSIE 5.0; Mac_PowerPC) |
Index: branches/hashar/includes/Revision.php |
— | — | @@ -530,6 +530,11 @@ |
531 | 531 | if( in_array( 'object', $flags ) ) { |
532 | 532 | # Generic compressed storage |
533 | 533 | $obj = unserialize( $text ); |
| 534 | + if ( !is_object( $obj ) ) { |
| 535 | + // Invalid object |
| 536 | + wfProfileOut( $fname ); |
| 537 | + return false; |
| 538 | + } |
534 | 539 | $text = $obj->getText(); |
535 | 540 | } |
536 | 541 | |
Index: branches/hashar/includes/SkinTemplate.php |
— | — | @@ -37,7 +37,7 @@ |
38 | 38 | * Wrapper object for MediaWiki's localization functions, |
39 | 39 | * to be passed to the template engine. |
40 | 40 | * |
41 | | - * @access private |
| 41 | + * @private |
42 | 42 | * @package MediaWiki |
43 | 43 | */ |
44 | 44 | class MediaWiki_I18N { |
— | — | @@ -74,7 +74,7 @@ |
75 | 75 | */ |
76 | 76 | class SkinTemplate extends Skin { |
77 | 77 | /**#@+ |
78 | | - * @access private |
| 78 | + * @private |
79 | 79 | */ |
80 | 80 | |
81 | 81 | /** |
— | — | @@ -120,7 +120,7 @@ |
121 | 121 | * @param string $repository subdirectory where we keep template files |
122 | 122 | * @param string $cache_dir |
123 | 123 | * @return object |
124 | | - * @access private |
| 124 | + * @private |
125 | 125 | */ |
126 | 126 | function setupTemplate( $classname, $repository=false, $cache_dir=false ) { |
127 | 127 | return new $classname(); |
— | — | @@ -130,7 +130,7 @@ |
131 | 131 | * initialize various variables and generate the template |
132 | 132 | * |
133 | 133 | * @param OutputPage $out |
134 | | - * @access public |
| 134 | + * @public |
135 | 135 | */ |
136 | 136 | function outputPage( &$out ) { |
137 | 137 | global $wgTitle, $wgArticle, $wgUser, $wgLang, $wgContLang, $wgOut; |
— | — | @@ -438,7 +438,7 @@ |
439 | 439 | * For the base class, assume strings all around. |
440 | 440 | * |
441 | 441 | * @param mixed $str |
442 | | - * @access private |
| 442 | + * @private |
443 | 443 | */ |
444 | 444 | function printOrError( &$str ) { |
445 | 445 | echo $str; |
— | — | @@ -447,7 +447,7 @@ |
448 | 448 | /** |
449 | 449 | * build array of urls for personal toolbar |
450 | 450 | * @return array |
451 | | - * @access private |
| 451 | + * @private |
452 | 452 | */ |
453 | 453 | function buildPersonalUrls() { |
454 | 454 | global $wgTitle, $wgShowIPinHeader; |
— | — | @@ -584,7 +584,7 @@ |
585 | 585 | /** |
586 | 586 | * an array of edit links by default used for the tabs |
587 | 587 | * @return array |
588 | | - * @access private |
| 588 | + * @private |
589 | 589 | */ |
590 | 590 | function buildContentActionUrls () { |
591 | 591 | global $wgContLang; |
— | — | @@ -762,7 +762,7 @@ |
763 | 763 | /** |
764 | 764 | * build array of common navigation links |
765 | 765 | * @return array |
766 | | - * @access private |
| 766 | + * @private |
767 | 767 | */ |
768 | 768 | function buildNavUrls () { |
769 | 769 | global $wgUseTrackbacks, $wgTitle, $wgArticle; |
— | — | @@ -870,14 +870,14 @@ |
871 | 871 | /** |
872 | 872 | * Generate strings used for xml 'id' names |
873 | 873 | * @return string |
874 | | - * @access private |
| 874 | + * @private |
875 | 875 | */ |
876 | 876 | function getNameSpaceKey () { |
877 | 877 | return $this->mTitle->getNamespaceKey(); |
878 | 878 | } |
879 | 879 | |
880 | 880 | /** |
881 | | - * @access private |
| 881 | + * @private |
882 | 882 | */ |
883 | 883 | function setupUserCss() { |
884 | 884 | $fname = 'SkinTemplate::setupUserCss'; |
— | — | @@ -926,7 +926,7 @@ |
927 | 927 | } |
928 | 928 | |
929 | 929 | /** |
930 | | - * @access private |
| 930 | + * @private |
931 | 931 | */ |
932 | 932 | function setupUserJs() { |
933 | 933 | $fname = 'SkinTemplate::setupUserJs'; |
— | — | @@ -950,7 +950,7 @@ |
951 | 951 | * Code for extensions to hook into to provide per-page CSS, see |
952 | 952 | * extensions/PageCSS/PageCSS.php for an implementation of this. |
953 | 953 | * |
954 | | - * @access private |
| 954 | + * @private |
955 | 955 | */ |
956 | 956 | function setupPageCss() { |
957 | 957 | $fname = 'SkinTemplate::setupPageCss'; |
— | — | @@ -964,7 +964,7 @@ |
965 | 965 | |
966 | 966 | /** |
967 | 967 | * returns css with user-specific options |
968 | | - * @access public |
| 968 | + * @public |
969 | 969 | */ |
970 | 970 | |
971 | 971 | function getUserStylesheet() { |
— | — | @@ -978,7 +978,7 @@ |
979 | 979 | } |
980 | 980 | |
981 | 981 | /** |
982 | | - * @access public |
| 982 | + * @public |
983 | 983 | */ |
984 | 984 | function getUserJs() { |
985 | 985 | $fname = 'SkinTemplate::getUserJs'; |
— | — | @@ -1010,7 +1010,7 @@ |
1011 | 1011 | */ |
1012 | 1012 | class QuickTemplate { |
1013 | 1013 | /** |
1014 | | - * @access public |
| 1014 | + * @public |
1015 | 1015 | */ |
1016 | 1016 | function QuickTemplate() { |
1017 | 1017 | $this->data = array(); |
— | — | @@ -1018,28 +1018,28 @@ |
1019 | 1019 | } |
1020 | 1020 | |
1021 | 1021 | /** |
1022 | | - * @access public |
| 1022 | + * @public |
1023 | 1023 | */ |
1024 | 1024 | function set( $name, $value ) { |
1025 | 1025 | $this->data[$name] = $value; |
1026 | 1026 | } |
1027 | 1027 | |
1028 | 1028 | /** |
1029 | | - * @access public |
| 1029 | + * @public |
1030 | 1030 | */ |
1031 | 1031 | function setRef($name, &$value) { |
1032 | 1032 | $this->data[$name] =& $value; |
1033 | 1033 | } |
1034 | 1034 | |
1035 | 1035 | /** |
1036 | | - * @access public |
| 1036 | + * @public |
1037 | 1037 | */ |
1038 | 1038 | function setTranslator( &$t ) { |
1039 | 1039 | $this->translator = &$t; |
1040 | 1040 | } |
1041 | 1041 | |
1042 | 1042 | /** |
1043 | | - * @access public |
| 1043 | + * @public |
1044 | 1044 | */ |
1045 | 1045 | function execute() { |
1046 | 1046 | echo "Override this function."; |
— | — | @@ -1047,28 +1047,28 @@ |
1048 | 1048 | |
1049 | 1049 | |
1050 | 1050 | /** |
1051 | | - * @access private |
| 1051 | + * @private |
1052 | 1052 | */ |
1053 | 1053 | function text( $str ) { |
1054 | 1054 | echo htmlspecialchars( $this->data[$str] ); |
1055 | 1055 | } |
1056 | 1056 | |
1057 | 1057 | /** |
1058 | | - * @access private |
| 1058 | + * @private |
1059 | 1059 | */ |
1060 | 1060 | function html( $str ) { |
1061 | 1061 | echo $this->data[$str]; |
1062 | 1062 | } |
1063 | 1063 | |
1064 | 1064 | /** |
1065 | | - * @access private |
| 1065 | + * @private |
1066 | 1066 | */ |
1067 | 1067 | function msg( $str ) { |
1068 | 1068 | echo htmlspecialchars( $this->translator->translate( $str ) ); |
1069 | 1069 | } |
1070 | 1070 | |
1071 | 1071 | /** |
1072 | | - * @access private |
| 1072 | + * @private |
1073 | 1073 | */ |
1074 | 1074 | function msgHtml( $str ) { |
1075 | 1075 | echo $this->translator->translate( $str ); |
— | — | @@ -1076,7 +1076,7 @@ |
1077 | 1077 | |
1078 | 1078 | /** |
1079 | 1079 | * An ugly, ugly hack. |
1080 | | - * @access private |
| 1080 | + * @private |
1081 | 1081 | */ |
1082 | 1082 | function msgWiki( $str ) { |
1083 | 1083 | global $wgParser, $wgTitle, $wgOut; |
— | — | @@ -1088,14 +1088,14 @@ |
1089 | 1089 | } |
1090 | 1090 | |
1091 | 1091 | /** |
1092 | | - * @access private |
| 1092 | + * @private |
1093 | 1093 | */ |
1094 | 1094 | function haveData( $str ) { |
1095 | 1095 | return $this->data[$str]; |
1096 | 1096 | } |
1097 | 1097 | |
1098 | 1098 | /** |
1099 | | - * @access private |
| 1099 | + * @private |
1100 | 1100 | */ |
1101 | 1101 | function haveMsg( $str ) { |
1102 | 1102 | $msg = $this->translator->translate( $str ); |
Index: branches/hashar/includes/SpecialMostlinked.php |
— | — | @@ -37,7 +37,7 @@ |
38 | 38 | page_namespace |
39 | 39 | FROM $pagelinks |
40 | 40 | LEFT JOIN $page ON pl_namespace=page_namespace AND pl_title=page_title |
41 | | - GROUP BY pl_namespace,pl_title,page_namespace |
| 41 | + GROUP BY pl_namespace,pl_title |
42 | 42 | HAVING COUNT(*) > 1"; |
43 | 43 | } |
44 | 44 | |
Index: branches/hashar/includes/SpecialListusers.php |
— | — | @@ -135,7 +135,7 @@ |
136 | 136 | "FROM $user ". |
137 | 137 | "LEFT JOIN $user_groups ON user_id=ug_user " . |
138 | 138 | $this->userQueryWhere( $dbr ) . |
139 | | - " GROUP BY user_name, user_id"; |
| 139 | + " GROUP BY user_name"; |
140 | 140 | |
141 | 141 | return $sql; |
142 | 142 | } |
Index: branches/hashar/includes/Database.php |
— | — | @@ -1078,8 +1078,10 @@ |
1079 | 1079 | |
1080 | 1080 | /** |
1081 | 1081 | * Makes a wfStrencoded list from an array |
1082 | | - * $mode: LIST_COMMA - comma separated, no field names |
| 1082 | + * $mode: |
| 1083 | + * LIST_COMMA - comma separated, no field names |
1083 | 1084 | * LIST_AND - ANDed WHERE clause (without the WHERE) |
| 1085 | + * LIST_OR - ORed WHERE clause (without the WHERE) |
1084 | 1086 | * LIST_SET - comma separated with field names, like a SET clause |
1085 | 1087 | * LIST_NAMES - comma separated field names |
1086 | 1088 | */ |
— | — | @@ -1104,7 +1106,7 @@ |
1105 | 1107 | } |
1106 | 1108 | if ( ($mode == LIST_AND || $mode == LIST_OR) && is_numeric( $field ) ) { |
1107 | 1109 | $list .= "($value)"; |
1108 | | - } elseif ( $mode == LIST_AND && is_array ($value) ) { |
| 1110 | + } elseif ( ($mode == LIST_AND || $mode == LIST_OR) && is_array ($value) ) { |
1109 | 1111 | $list .= $field." IN (".$this->makeList($value).") "; |
1110 | 1112 | } else { |
1111 | 1113 | if ( $mode == LIST_AND || $mode == LIST_OR || $mode == LIST_SET ) { |
Index: branches/hashar/includes/SpecialDoubleRedirects.php |
— | — | @@ -29,24 +29,38 @@ |
30 | 30 | return '<p>'.wfMsg("doubleredirectstext")."</p><br />\n"; |
31 | 31 | } |
32 | 32 | |
33 | | - function getSQL() { |
34 | | - $dbr =& wfGetDB( DB_SLAVE ); |
| 33 | + function getSQLText( &$dbr, $namespace = null, $title = null ) { |
| 34 | + |
35 | 35 | extract( $dbr->tableNames( 'page', 'pagelinks' ) ); |
36 | 36 | |
37 | | - $sql = "SELECT 'DoubleRedirects' as type," . |
38 | | - " pa.page_namespace as namespace, pa.page_title as title," . |
39 | | - " pb.page_namespace as nsb, pb.page_title as tb," . |
40 | | - " pc.page_namespace as nsc, pc.page_title as tc" . |
41 | | - " FROM $pagelinks AS la, $pagelinks AS lb, $page AS pa, $page AS pb, $page AS pc" . |
42 | | - " WHERE pa.page_is_redirect=1 AND pb.page_is_redirect=1" . |
43 | | - " AND la.pl_from=pa.page_id" . |
44 | | - " AND la.pl_namespace=pb.page_namespace" . |
45 | | - " AND la.pl_title=pb.page_title" . |
46 | | - " AND lb.pl_from=pb.page_id" . |
47 | | - " AND lb.pl_namespace=pc.page_namespace" . |
48 | | - " AND lb.pl_title=pc.page_title"; |
| 37 | + $limitToTitle = !( $namespace === null && $title === null ); |
| 38 | + $sql = $limitToTitle ? "SELECT" : "SELECT 'DoubleRedirects' as type," ; |
| 39 | + $sql .= |
| 40 | + " pa.page_namespace as namespace, pa.page_title as title," . |
| 41 | + " pb.page_namespace as nsb, pb.page_title as tb," . |
| 42 | + " pc.page_namespace as nsc, pc.page_title as tc" . |
| 43 | + " FROM $pagelinks AS la, $pagelinks AS lb, $page AS pa, $page AS pb, $page AS pc" . |
| 44 | + " WHERE pa.page_is_redirect=1 AND pb.page_is_redirect=1" . |
| 45 | + " AND la.pl_from=pa.page_id" . |
| 46 | + " AND la.pl_namespace=pb.page_namespace" . |
| 47 | + " AND la.pl_title=pb.page_title" . |
| 48 | + " AND lb.pl_from=pb.page_id" . |
| 49 | + " AND lb.pl_namespace=pc.page_namespace" . |
| 50 | + " AND lb.pl_title=pc.page_title"; |
| 51 | + |
| 52 | + if( $limitToTitle ) { |
| 53 | + $encTitle = $dbr->addQuotes( $title ); |
| 54 | + $sql .= " AND pa.page_namespace=$namespace" . |
| 55 | + " AND pa.page_title=$encTitle"; |
| 56 | + } |
| 57 | + |
49 | 58 | return $sql; |
50 | 59 | } |
| 60 | + |
| 61 | + function getSQL() { |
| 62 | + $dbr =& wfGetDB( DB_SLAVE ); |
| 63 | + return $this->getSQLText( $dbr ); |
| 64 | + } |
51 | 65 | |
52 | 66 | function getOrder() { |
53 | 67 | return ''; |
— | — | @@ -60,25 +74,11 @@ |
61 | 75 | |
62 | 76 | if ( $result && !isset( $result->nsb ) ) { |
63 | 77 | $dbr =& wfGetDB( DB_SLAVE ); |
64 | | - extract( $dbr->tableNames( 'page', 'pagelinks' ) ); |
65 | | - $encTitle = $dbr->addQuotes( $result->title ); |
66 | | - |
67 | | - $sql = "SELECT pa.page_namespace as namespace, pa.page_title as title," . |
68 | | - " pb.page_namespace as nsb, pb.page_title as tb," . |
69 | | - " pc.page_namespace as nsc, pc.page_title as tc" . |
70 | | - " FROM $pagelinks AS la, $pagelinks AS lb, $page AS pa, $page AS pb, $page AS pc" . |
71 | | - " WHERE pa.page_is_redirect=1 AND pb.page_is_redirect=1" . |
72 | | - " AND la.pl_from=pa.page_id" . |
73 | | - " AND la.pl_namespace=pb.page_namespace" . |
74 | | - " AND la.pl_title=pb.page_title" . |
75 | | - " AND lb.pl_from=pb.page_id" . |
76 | | - " AND lb.pl_namespace=pc.page_namespace" . |
77 | | - " AND lb.pl_title=pc.page_title" . |
78 | | - " AND pa.page_namespace={$result->namespace}" . |
79 | | - " AND pa.page_title=$encTitle"; |
| 78 | + $sql = $this->getSQLText( $dbr, $result->namespace, $result->title ); |
80 | 79 | $res = $dbr->query( $sql, $fname ); |
81 | 80 | if ( $res ) { |
82 | 81 | $result = $dbr->fetchObject( $res ); |
| 82 | + $dbr->freeResult( $res ); |
83 | 83 | } |
84 | 84 | } |
85 | 85 | if ( !$result ) { |
Index: branches/hashar/includes/QueryPage.php |
— | — | @@ -63,6 +63,14 @@ |
64 | 64 | * @var bool |
65 | 65 | */ |
66 | 66 | var $listoutput = false; |
| 67 | + |
| 68 | + /** |
| 69 | + * The offset and limit in use, as passed to the query() function |
| 70 | + * |
| 71 | + * @var integer |
| 72 | + */ |
| 73 | + var $offset = 0; |
| 74 | + var $limit = 0; |
67 | 75 | |
68 | 76 | /** |
69 | 77 | * A mutator for $this->listoutput; |
— | — | @@ -264,6 +272,9 @@ |
265 | 273 | function doQuery( $offset, $limit, $shownavigation=true ) { |
266 | 274 | global $wgUser, $wgOut, $wgLang, $wgContLang; |
267 | 275 | |
| 276 | + $this->offset = $offset; |
| 277 | + $this->limit = $limit; |
| 278 | + |
268 | 279 | $sname = $this->getName(); |
269 | 280 | $fname = get_class($this) . '::doQuery'; |
270 | 281 | $sql = $this->getSQL(); |
Index: branches/hashar/includes/CategoryPage.php |
— | — | @@ -61,7 +61,7 @@ |
62 | 62 | * @param string $from -- return only sort keys from this item on |
63 | 63 | * @param string $until -- don't return keys after this point. |
64 | 64 | * @return string HTML output |
65 | | - * @access private |
| 65 | + * @private |
66 | 66 | */ |
67 | 67 | function doCategoryMagic( $from = '', $until = '' ) { |
68 | 68 | global $wgContLang,$wgUser, $wgCategoryMagicGallery, $wgCategoryPagingLimit, $wgInterlanguageTitles, $wgLanguageCode; |
— | — | @@ -210,7 +210,7 @@ |
211 | 211 | * @param array $articles |
212 | 212 | * @param string $message |
213 | 213 | * @return string |
214 | | - * @access private |
| 214 | + * @private |
215 | 215 | */ |
216 | 216 | function formatCount( $articles, $message ) { |
217 | 217 | global $wgContLang; |
— | — | @@ -229,7 +229,7 @@ |
230 | 230 | * @param array $articles_start_char |
231 | 231 | * @param int $cutoff |
232 | 232 | * @return string |
233 | | - * @access private |
| 233 | + * @private |
234 | 234 | */ |
235 | 235 | function formatList( $articles, $articles_start_char, $cutoff = 6 ) { |
236 | 236 | if ( count ( $articles ) > $cutoff ) { |
— | — | @@ -248,7 +248,7 @@ |
249 | 249 | * @param array $articles |
250 | 250 | * @param array $articles_start_char |
251 | 251 | * @return string |
252 | | - * @access private |
| 252 | + * @private |
253 | 253 | */ |
254 | 254 | function columnList( $articles, $articles_start_char ) { |
255 | 255 | // divide list into three equal chunks |
— | — | @@ -307,7 +307,7 @@ |
308 | 308 | * @param array $articles |
309 | 309 | * @param array $articles_start_char |
310 | 310 | * @return string |
311 | | - * @access private |
| 311 | + * @private |
312 | 312 | */ |
313 | 313 | function shortList( $articles, $articles_start_char ) { |
314 | 314 | $r = '<h3>' . htmlspecialchars( $articles_start_char[0] ) . "</h3>\n"; |
— | — | @@ -332,7 +332,7 @@ |
333 | 333 | * @param int $limit |
334 | 334 | * @param array $query - additional query options to pass |
335 | 335 | * @return string |
336 | | - * @access private |
| 336 | + * @private |
337 | 337 | */ |
338 | 338 | function pagingLinks( $title, $first, $last, $limit, $query = array() ) { |
339 | 339 | global $wgUser, $wgLang; |
Index: branches/hashar/includes/SpecialMostrevisions.php |
— | — | @@ -36,7 +36,7 @@ |
37 | 37 | FROM $revision |
38 | 38 | LEFT JOIN $page ON page_id = rev_page |
39 | 39 | WHERE page_namespace = " . NS_MAIN . " |
40 | | - GROUP BY rev_page, page_namespace, page_title |
| 40 | + GROUP BY rev_page |
41 | 41 | HAVING COUNT(*) > 1 |
42 | 42 | "; |
43 | 43 | } |
Index: branches/hashar/includes/SpecialBrokenRedirects.php |
— | — | @@ -26,7 +26,8 @@ |
27 | 27 | function isSyndicated() { return false; } |
28 | 28 | |
29 | 29 | function getPageHeader( ) { |
30 | | - return wfMsgWikiHtml('brokenredirectstext')."<br />\n"; |
| 30 | + global $wgOut; |
| 31 | + return $wgOut->parse( wfMsg( 'brokenredirectstext' ) ); |
31 | 32 | } |
32 | 33 | |
33 | 34 | function getSQL() { |
Index: branches/hashar/RELEASE-NOTES |
— | — | @@ -108,8 +108,13 @@ |
109 | 109 | * (bug 5422) Stub for Romani (rmy) language which extends ro |
110 | 110 | * Fix linktrail for LanguageSr |
111 | 111 | * (bug 5664) Fix Bosnian linktrail |
| 112 | +* (bug 3825) Namespace filtering on Special:Newpages |
| 113 | +* (bug 1922) When Special:Wantedpages is cached, mark links to pages |
| 114 | + which have since been created |
| 115 | +* (bug 5659) Change grammar hacks for Bosnian Wikimedia namespaces. |
| 116 | + This sort of special casing should be removed and fixed properly. |
| 117 | +* Remove useless whitespace from Special:Brokenredirects header |
112 | 118 | |
113 | | - |
114 | 119 | == Compatibility == |
115 | 120 | |
116 | 121 | Older PHP 4.2 and 4.1 releases are no longer supported; PHP 4 users must |
Index: branches/hashar/languages/MessagesFr.php |
— | — | @@ -612,13 +612,15 @@ |
613 | 613 | 'statistics' => 'Statistiques', |
614 | 614 | 'sitestats' => 'Statistiques du site', |
615 | 615 | 'userstats' => 'Statistiques utilisateur', |
616 | | -'sitestatstext' => 'La base de données contient actuellement <b>$1</b> pages. |
| 616 | +'sitestatstext' => "La base de données contient actuellement <b>$1</b> pages. |
617 | 617 | |
618 | | -Ce chiffre inclut les pages « discussion », les pages relatives à {{SITENAME}}, les pages minimales ("bouchons"), les pages de redirection, ainsi que d\'autres pages qui ne peuvent sans doute pas être considérées comme des articles. |
619 | | -Si l\'on exclut ces pages, il reste <b>$2</b> pages qui sont probablement de véritables articles.<p> |
| 618 | +Ce chiffre inclut les pages « discussion », les pages relatives à {{SITENAME}}, les pages minimales (\"bouchons\"), les pages de redirection, ainsi que d'autres pages qui ne peuvent sans doute pas être considérées comme des articles. |
| 619 | +Si l'on exclut ces pages, il reste <b>$2</b> pages qui sont probablement de véritables articles.<p> |
620 | 620 | <b>$3</b> pages ont été consultées et <b>$4</b> pages modifiées. |
621 | 621 | |
622 | | -Cela représente une moyenne de <b>$5</b> modifications par page et de <b>$6</b> consultations pour une modification.', |
| 622 | +Cela représente une moyenne de <b>$5</b> modifications par page et de <b>$6</b> consultations pour une modification.</p> |
| 623 | + |
| 624 | +<p>Il y a '''$7''' articles dans [http://meta.wikimedia.org/wiki/Help:Job_queue le file de tâche].</p>", |
623 | 625 | 'userstatstext' => 'Il y a <b>$1</b> utilisateurs enregistrés. Parmi ceux-ci, <b>$2</b> ont le statut d\'administrateur (voir $3).', |
624 | 626 | |
625 | 627 | |
Index: branches/hashar/languages/Messages.php |
— | — | @@ -36,7 +36,7 @@ |
37 | 37 | 'tog-highlightbroken' => 'Format broken links <a href="" class="new">like this</a> (alternative: like this<a href="" class="internal">?</a>).', |
38 | 38 | 'tog-justify' => 'Justify paragraphs', |
39 | 39 | 'tog-hideminor' => 'Hide minor edits in recent changes', |
40 | | -'tog-extendwatchlist' => 'Enhanced watchlist', |
| 40 | +'tog-extendwatchlist' => 'Expand watchlist to show all applicable changes', |
41 | 41 | 'tog-usenewrc' => 'Enhanced recent changes (JavaScript)', |
42 | 42 | 'tog-numberheadings' => 'Auto-number headings', |
43 | 43 | 'tog-showtoolbar' => 'Show edit toolbar (JavaScript)', |
Index: branches/hashar/languages/LanguageBs.php |
— | — | @@ -56,6 +56,7 @@ |
57 | 57 | '6:12, 5 jan 2001', |
58 | 58 | ); |
59 | 59 | |
| 60 | +/* NOT USED IN STABLE VERSION */ |
60 | 61 | /* private */ $wgMagicWordsBs = array( |
61 | 62 | # ID CASE SYNONYMS |
62 | 63 | MAG_REDIRECT => array( 0, '#Preusmjeri', '#redirect', '#preusmjeri', '#PREUSMJERI' ), |
— | — | @@ -168,95 +169,95 @@ |
169 | 170 | function convertGrammar( $word, $case ) { |
170 | 171 | switch ( $case ) { |
171 | 172 | case 'genitiv': # genitive |
172 | | - if ( $word == 'Vikipedija' ) { |
173 | | - $word = 'Vikipedije'; |
174 | | - } elseif ( $word == 'Vikiknjige' ) { |
175 | | - $word = 'Vikiknjiga'; |
176 | | - } elseif ( $word == 'Vikivijesti' ) { |
177 | | - $word = 'Vikivijesti'; |
178 | | - } elseif ( $word == 'Vikicitati' ) { |
179 | | - $word = 'Vikicitata'; |
180 | | - } elseif ( $word == 'Vikiizvor' ) { |
181 | | - $word = 'Vikiizvora'; |
182 | | - } elseif ( $word == 'Vikiriječnik' ) { |
183 | | - $word = 'Vikiriječnika'; |
| 173 | + if ( $word == 'Wikipedia' ) { |
| 174 | + $word = 'Wikipedije'; |
| 175 | + } elseif ( $word == 'Wikiknjige' ) { |
| 176 | + $word = 'Wikiknjiga'; |
| 177 | + } elseif ( $word == 'Wikivijesti' ) { |
| 178 | + $word = 'Wikivijesti'; |
| 179 | + } elseif ( $word == 'Wikicitati' ) { |
| 180 | + $word = 'Wikicitata'; |
| 181 | + } elseif ( $word == 'Wikiizvor' ) { |
| 182 | + $word = 'Wikiizvora'; |
| 183 | + } elseif ( $word == 'Vikirječnik' ) { |
| 184 | + $word = 'Vikirječnika'; |
184 | 185 | } |
185 | 186 | break; |
186 | 187 | case 'dativ': # dative |
187 | | - if ( $word == 'Vikipedija' ) { |
188 | | - $word = 'Vikipediji'; |
189 | | - } elseif ( $word == 'Vikiknjige' ) { |
190 | | - $word = 'Vikiknjigama'; |
191 | | - } elseif ( $word == 'Vikicitati' ) { |
192 | | - $word = 'Vikicitatima'; |
193 | | - } elseif ( $word == 'Vikivijesti' ) { |
194 | | - $word = 'Vikivijestima'; |
195 | | - } elseif ( $word == 'Vikiizvor' ) { |
196 | | - $word = 'Vikiizvoru'; |
197 | | - } elseif ( $word == 'Vikiriječnik' ) { |
198 | | - $word = 'Vikiriječniku'; |
| 188 | + if ( $word == 'Wikipedia' ) { |
| 189 | + $word = 'Wikipediji'; |
| 190 | + } elseif ( $word == 'Wikiknjige' ) { |
| 191 | + $word = 'Wikiknjigama'; |
| 192 | + } elseif ( $word == 'Wikicitati' ) { |
| 193 | + $word = 'Wikicitatima'; |
| 194 | + } elseif ( $word == 'Wikivijesti' ) { |
| 195 | + $word = 'Wikivijestima'; |
| 196 | + } elseif ( $word == 'Wikiizvor' ) { |
| 197 | + $word = 'Wikiizvoru'; |
| 198 | + } elseif ( $word == 'Vikirječnik' ) { |
| 199 | + $word = 'Vikirječniku'; |
199 | 200 | } |
200 | 201 | break; |
201 | 202 | case 'akuzativ': # akusative |
202 | | - if ( $word == 'Vikipedija' ) { |
203 | | - $word = 'Vikipediju'; |
204 | | - } elseif ( $word == 'Vikiknjige' ) { |
205 | | - $word = 'Vikiknjige'; |
206 | | - } elseif ( $word == 'Vikicitati' ) { |
207 | | - $word = 'Vikicitate'; |
208 | | - } elseif ( $word == 'Vikivijesti' ) { |
209 | | - $word = 'Vikivijesti'; |
210 | | - } elseif ( $word == 'Vikiizvor' ) { |
211 | | - $word = 'Vikiizvora'; |
212 | | - } elseif ( $word == 'Vikiriječnik' ) { |
213 | | - $word = 'Vikiriječnika'; |
| 203 | + if ( $word == 'Wikipedia' ) { |
| 204 | + $word = 'Wikipediju'; |
| 205 | + } elseif ( $word == 'Wikiknjige' ) { |
| 206 | + $word = 'Wikiknjige'; |
| 207 | + } elseif ( $word == 'Wikicitati' ) { |
| 208 | + $word = 'Wikicitate'; |
| 209 | + } elseif ( $word == 'Wikivijesti' ) { |
| 210 | + $word = 'Wikivijesti'; |
| 211 | + } elseif ( $word == 'Wikiizvor' ) { |
| 212 | + $word = 'Wikiizvora'; |
| 213 | + } elseif ( $word == 'Vikirječnik' ) { |
| 214 | + $word = 'Vikirječnika'; |
214 | 215 | } |
215 | 216 | break; |
216 | 217 | case 'vokativ': # vocative |
217 | | - if ( $word == 'Vikipedija' ) { |
218 | | - $word = 'Vikipedijo'; |
219 | | - } elseif ( $word == 'Vikiknjige' ) { |
220 | | - $word = 'Vikiknjige'; |
221 | | - } elseif ( $word == 'Vikicitati' ) { |
222 | | - $word = 'Vikicitati'; |
223 | | - } elseif ( $word == 'Vikivijesti' ) { |
224 | | - $word = 'Vikivijesti'; |
225 | | - } elseif ( $word == 'Vikiizvor' ) { |
226 | | - $word = 'Vikizivoru'; |
227 | | - } elseif ( $word == 'Vikiriječnik' ) { |
228 | | - $word = 'Vikiriječniče'; |
| 218 | + if ( $word == 'Wikipedia' ) { |
| 219 | + $word = 'Wikipedijo'; |
| 220 | + } elseif ( $word == 'Wikiknjige' ) { |
| 221 | + $word = 'Wikiknjige'; |
| 222 | + } elseif ( $word == 'Wikicitati' ) { |
| 223 | + $word = 'Wikicitati'; |
| 224 | + } elseif ( $word == 'Wikivijesti' ) { |
| 225 | + $word = 'Wikivijesti'; |
| 226 | + } elseif ( $word == 'Wikiizvor' ) { |
| 227 | + $word = 'Wikizivoru'; |
| 228 | + } elseif ( $word == 'Vikirječnik' ) { |
| 229 | + $word = 'Vikirječniče'; |
229 | 230 | } |
230 | 231 | break; |
231 | 232 | case 'instrumental': # instrumental |
232 | | - if ( $word == 'Vikipedija' ) { |
233 | | - $word = 's Vikipediom'; |
234 | | - } elseif ( $word == 'Vikiknjige' ) { |
235 | | - $word = 's Vikiknjigama'; |
236 | | - } elseif ( $word == 'Vikicitati' ) { |
237 | | - $word = 's Vikicitatima'; |
238 | | - } elseif ( $word == 'Vikivijesti' ) { |
239 | | - $word = 's Vikivijestima'; |
240 | | - } elseif ( $word == 'Vikiizvor' ) { |
241 | | - $word = 's Vikiizvorom'; |
242 | | - } elseif ( $word == 'Vikiriječnik' ) { |
243 | | - $word = 's Vikiriječnikom'; |
| 233 | + if ( $word == 'Wikipedia' ) { |
| 234 | + $word = 's Wikipediom'; |
| 235 | + } elseif ( $word == 'Wikiknjige' ) { |
| 236 | + $word = 's Wikiknjigama'; |
| 237 | + } elseif ( $word == 'Wikicitati' ) { |
| 238 | + $word = 's Wikicitatima'; |
| 239 | + } elseif ( $word == 'Wikivijesti' ) { |
| 240 | + $word = 's Wikivijestima'; |
| 241 | + } elseif ( $word == 'Wikiizvor' ) { |
| 242 | + $word = 's Wikiizvorom'; |
| 243 | + } elseif ( $word == 'Vikirječnik' ) { |
| 244 | + $word = 's Vikirječnikom'; |
244 | 245 | } else { |
245 | 246 | $word = 's ' . $word; |
246 | 247 | } |
247 | 248 | break; |
248 | 249 | case 'lokativ': # locative |
249 | | - if ( $word == 'Vikipedija' ) { |
| 250 | + if ( $word == 'Wikipedia' ) { |
250 | 251 | $word = 'o Wikipediji'; |
251 | | - } elseif ( $word == 'Vikiknjige' ) { |
252 | | - $word = 'o Vikiknjigama'; |
253 | | - } elseif ( $word == 'Vikicitati' ) { |
254 | | - $word = 'o Vikicitatima'; |
255 | | - } elseif ( $word == 'Vikivijesti' ) { |
256 | | - $word = 'o Vikivijestima'; |
257 | | - } elseif ( $word == 'Vikiizvor' ) { |
258 | | - $word = 'o Vikiizvoru'; |
259 | | - } elseif ( $word == 'Vikiriječnik' ) { |
260 | | - $word = 'o Vikiriječniku'; |
| 252 | + } elseif ( $word == 'Wikiknjige' ) { |
| 253 | + $word = 'o Wikiknjigama'; |
| 254 | + } elseif ( $word == 'Wikicitati' ) { |
| 255 | + $word = 'o Wikicitatima'; |
| 256 | + } elseif ( $word == 'Wikivijesti' ) { |
| 257 | + $word = 'o Wikivijestima'; |
| 258 | + } elseif ( $word == 'Wikiizvor' ) { |
| 259 | + $word = 'o Wikiizvoru'; |
| 260 | + } elseif ( $word == 'Vikirječnik' ) { |
| 261 | + $word = 'o Vikirječniku'; |
261 | 262 | } else { |
262 | 263 | $word = 'o ' . $word; |
263 | 264 | } |
— | — | @@ -268,4 +269,4 @@ |
269 | 270 | |
270 | 271 | } |
271 | 272 | |
272 | | -?> |
| 273 | +?> |
\ No newline at end of file |
Index: branches/hashar/languages/MessagesId.php |
— | — | @@ -14,7 +14,7 @@ |
15 | 15 | 'tog-showtoolbar' => 'Tampilkan batang alat penyuntingan', |
16 | 16 | 'tog-editondblclick' => 'Sunting halaman dengan klik ganda (JavaScript)', |
17 | 17 | 'tog-editsection'=> 'Fungsikan penyuntingan subbab melalui pranala [sunting]', |
18 | | -'tog-editsectiononrightclick' => 'Fungsikan penyuntingan subbab dengan klik-kanan<br/>pada judul bagian (JavaScript)', |
| 18 | +'tog-editsectiononrightclick' => 'Fungsikan penyuntingan subbab dengan klik-kanan pada judul bagian (JavaScript)', |
19 | 19 | 'tog-showtoc' => 'Tampilkan daftar isi<br />(untuk artikel yang mempunyai lebih dari 3 judul)', |
20 | 20 | 'tog-rememberpassword' => 'Ingat kata sandi pada setiap sesi', |
21 | 21 | 'tog-editwidth' => 'Kotak sunting memiliki lebar penuh', |
— | — | @@ -550,11 +550,11 @@ |
551 | 551 | |
552 | 552 | # Preferences page |
553 | 553 | # |
554 | | -"preferences" => "Konfigurasi", |
| 554 | +"preferences" => "Preferensi", |
555 | 555 | "prefsnologin" => "Belum masuk log", |
556 | 556 | "prefsnologintext" => "Anda harus [[Special:Userlogin|masuk log]] untuk menetapkan preferensi Anda.", |
557 | 557 | |
558 | | -"prefsreset" => "Konfigurasi telah dikembalikan ke asal dari storage.", |
| 558 | +"prefsreset" => "Preferensi telah dikembalikan ke konfigurasi baku.", |
559 | 559 | "qbsettings" => "Pengaturan quickbar", |
560 | 560 | "changepassword" => "Ganti kata sandi", |
561 | 561 | "skin" => "Kulit", |
— | — | @@ -591,7 +591,7 @@ |
592 | 592 | "contextchars" => "Karakter untuk konteks per baris", |
593 | 593 | "stubthreshold" => "Threshold tampilan stub", |
594 | 594 | "recentchangescount" => "Jumlah judul dalam perubahan terbaru", |
595 | | -"savedprefs" => "Konfigurasi Anda telah disimpan", |
| 595 | +"savedprefs" => "Preferensi Anda telah disimpan", |
596 | 596 | 'timezonelegend' => 'Daerah waktu', |
597 | 597 | "timezonetext" => "Masukkan perbedaan waktu (dalam jam) antara waktu setempat dengan waktu server (UTC).", |
598 | 598 | "localtime" => "Waktu setempat", |
— | — | @@ -796,9 +796,15 @@ |
797 | 797 | "statistics" => "Statistik", |
798 | 798 | "sitestats" => "Statistik situs", |
799 | 799 | "userstats" => "Statistik pengguna", |
800 | | -"sitestatstext" => "Ada sejumlah <b>$1</b> halaman dalam basis data. Ini termasuk halaman \"pembicaraan\", halaman tentang {{SITENAME}}, halaman minimum \"stub\", peralihan halaman, dan halaman-halaman lain yang mungkin bukan artikel. Selain itu, ada <b>$2</b> halaman yang mungkin adalah artikel yang sah.<p> Ada sejumlah <b>$3</b> penampilan halaman, dan sejumlah <b>$4</b> penyuntingan sejak wiki ini dimulai. Ini berarti rata-rata <b>$5</b> suntingan per halaman, dan <b>$6</b> penampilan per penyuntingan.", |
801 | | -"userstatstext" => "Ada <b>$1</b> pengguna terdaftar. <b>$2</b> diantaranya adalah administrator (lihat $3).", |
| 800 | +"sitestatstext" => "Terdapat total '''$1''' halaman dalam basis data. Ini termasuk halaman \"pembicaraan\", halaman tentang {{SITENAME}}, halaman \"rintisan\" minimum, halaman peralihan, dan halaman-halaman lain yang mungkin tidak masuk kriteria artikel. Selain itu, ada '''$2''' halaman yang mungkin termasuk artikel yang sah. |
802 | 801 | |
| 802 | +'''$8''' berkas telah dimuat. |
| 803 | + |
| 804 | +Ada sejumlah '''$3''' penampilan halaman, dan sejumlah '''$4''' penyuntingan sejak wiki ini dimulai. Ini berarti rata-rata '''$5''' suntingan per halaman, dan '''$6''' penampilan per penyuntingan. |
| 805 | + |
| 806 | +[http://meta.wikimedia.org/wiki/Help:Job_queue Antrian job] adalah sebanyak '''$7'''.", |
| 807 | +"userstatstext" => "Ada '''$1''' pengguna terdaftar, dimana '''$2''' (atau '''$4%''') diantaranya adalah administrator (lihat $3).", |
| 808 | + |
803 | 809 | "disambiguations" => "Halaman disambiguasi", |
804 | 810 | "disambiguationspage" => "Project:Pranala_ke_halaman_disambiguation", |
805 | 811 | "disambiguationstext" => "Halaman-halaman berikut ini berpaut ke sebuah <i>halaman disambiguation</i>. Halaman-halaman tersebut seharusnya berpaut ke topik-topik yang tepat.<br />Satu halaman dianggap sebagai disambiguation apabila halaman tersebut disambung dari $1.<br />Pranala dari namespace lain <i>tidak</i> terdaftar di sini.", |
— | — | @@ -907,20 +913,20 @@ |
908 | 914 | # |
909 | 915 | "watchlist" => "Daftar pantauan", |
910 | 916 | "watchlistsub" => "(untuk pengguna \"$1\")", |
911 | | -"nowatchlist" => "Daftar pemantauan Anda kosong.", |
| 917 | +"nowatchlist" => "Daftar pantauan Anda kosong.", |
912 | 918 | 'watchlistcount' => "'''Anda memiliki $1 entri di daftar pantauan Anda, termasuk halaman diskusi/bicara.'''", |
913 | 919 | 'clearwatchlist' => 'Kosongkan daftar pantauan', |
914 | 920 | 'watchlistcleartext' => 'Apakah Anda yakin untuk menghapusnya?', |
915 | 921 | 'watchlistclearbutton' => 'Kosongkan daftar pantauan', |
916 | 922 | 'watchlistcleardone' => 'Daftar pantauan Anda telah dikosongkan. $1 entri telah dihapus.', |
917 | 923 | "watchnologin" => "Belum masuk log", |
918 | | -"watchnologintext" => "Anda harus [[Special:Userlogin|masuk log]] untuk mengubah daftar pemantauan.", |
919 | | -"addedwatch" => "Telah ditambahkan ke daftar pemantauan", |
920 | | -"addedwatchtext" => "Halaman \"$1\" telah ditambahkan ke [[Special:Watchlist|daftar pemantauan]]. Pada masa yang akan datang, semua perubahan pada halaman tersebut berikut halaman pembicaraannya akan didaftar di sini, dan halaman tersebut akan <b>dicetak tebal</b> dalam [[Special:Recentchanges|daftar perubahan terbaru]] supaya lebih mudah dilihat.\n\n<p>Apabila nanti Anda ingin menghapus halaman dari daftar pemantauan, klik \"Berhenti memantau\" pada batang sebelah.", |
921 | | -"removedwatch" => "Telah dihapus dari daftar pemantauan", |
922 | | -"removedwatchtext" => "Halaman \"$1\" telah dihapus dari daftar pemantauan.", |
| 924 | +"watchnologintext" => "Anda harus [[Special:Userlogin|masuk log]] untuk mengubah daftar pantauan.", |
| 925 | +"addedwatch" => "Telah ditambahkan ke daftar pantauan", |
| 926 | +"addedwatchtext" => "Halaman \"$1\" telah ditambahkan ke [[Special:Watchlist|daftar pantauan]]. Pada masa yang akan datang, semua perubahan pada halaman tersebut berikut halaman pembicaraannya akan didaftar di sini, dan halaman tersebut akan <b>dicetak tebal</b> dalam [[Special:Recentchanges|daftar perubahan terbaru]] supaya lebih mudah dilihat.\n\n<p>Apabila nanti Anda ingin menghapus halaman dari daftar pantauan, klik \"Berhenti memantau\" pada batang sebelah.", |
| 927 | +"removedwatch" => "Telah dihapus dari daftar pantauan", |
| 928 | +"removedwatchtext" => "Halaman \"$1\" telah dihapus dari daftar pantauan.", |
923 | 929 | 'watch' => 'Pantau', |
924 | | -"watchthispage" => "Tambahkan ke daftar pemantauan", |
| 930 | +"watchthispage" => "Tambahkan ke daftar pantauan", |
925 | 931 | 'unwatch' => 'Berhenti memantau', |
926 | 932 | "unwatchthispage" => "Berhenti memantau", |
927 | 933 | "notanarticle" => "Bukan sebuah artikel", |
— | — | @@ -930,15 +936,15 @@ |
931 | 937 | 'wlheader-showupdated' => "* Halaman-halaman yang telah berubah sejak kunjungan terakhir Anda ditampilkan dengan '''huruf tebal'''", |
932 | 938 | "watchmethod-recent"=> "periksa daftar perubahan terbaru terhadap halaman yang dipantau", |
933 | 939 | "watchmethod-list" => "periksa halaman yang dipantau terhadap perubahan terbaru", |
934 | | -"removechecked" => "Hapus item yang diberi tanda cek dari daftar pemantauan", |
935 | | -"watchlistcontains" => "Daftar pemantauan Anda berisi $1 halaman.", |
936 | | -"watcheditlist" => "Berikut ini adalah daftar halaman-halaman yang Anda pantau. Untuk menghapus halaman dari daftar pemantauan Anda, berikan tanda cek pada kotak cek di sebelah judul halaman yang ingin Anda hapus, lalu klik tombol 'hapus yang dicek' yang terletak di bagian bawah layar.", |
937 | | -"removingchecked" => "Menghapus item-item yang diminta dari daftar pemantauan Anda...", |
| 940 | +"removechecked" => "Hapus item yang diberi tanda cek dari daftar pantauan", |
| 941 | +"watchlistcontains" => "Daftar pantauan Anda berisi $1 halaman.", |
| 942 | +"watcheditlist" => "Berikut ini adalah daftar halaman-halaman yang Anda pantau. Untuk menghapus halaman dari daftar pantauan Anda, berikan tanda cek pada kotak cek di sebelah judul halaman yang ingin Anda hapus, lalu klik tombol 'hapus yang dicek' yang terletak di bagian bawah layar.", |
| 943 | +"removingchecked" => "Menghapus item-item yang diminta dari daftar pantauan Anda...", |
938 | 944 | "couldntremove" => "Tidak dapat menghapus item '$1'...", |
939 | 945 | "iteminvalidname" => "Ada masalah dengan item '$1' (namanya tidak sah)...", |
940 | 946 | "wlnote" => "Di bawah ini adalah daftar $1 perubahan terakhir dalam <b>$2</b> jam terakhir.", |
941 | 947 | "wlshowlast" => "Tampilkan $1 jam $2 hari $3 terakhir", |
942 | | -"wlsaved" => "Ini adalah versi tersimpan dari daftar pemantauan Anda.", |
| 948 | +"wlsaved" => "Ini adalah versi tersimpan dari daftar pantauan Anda.", |
943 | 949 | 'wlhideshowown' => '$1 suntingan saya.', |
944 | 950 | 'wlhideshowbots' => '$1 suntingan bot.', |
945 | 951 | |
— | — | @@ -966,7 +972,7 @@ |
967 | 973 | Sistem notifikasi {{SITENAME}} |
968 | 974 | |
969 | 975 | -- |
970 | | -Untuk mengubah konfigurasi daftar pantauan Anda, kunjungi |
| 976 | +Untuk mengubah preferensi daftar pantauan Anda, kunjungi |
971 | 977 | {{SERVER}}{{localurl:Special:Watchlist/edit}} |
972 | 978 | |
973 | 979 | Masukan dan bantuan lanjutan: |
— | — | @@ -1141,8 +1147,8 @@ |
1142 | 1148 | # |
1143 | 1149 | "lockdb" => "Kunci basis data", |
1144 | 1150 | "unlockdb" => "Buka kunci basis data", |
1145 | | -"lockdbtext" => "Mengunci basis data akan menghentikan kemampuan semua pengguna dalam menyunting halaman, mengubah preferensi pengguna, menyunting daftar pemantauan mereka, dan hal-hal lain yang memerlukan perubahan terhadap basis data. Pastikan bahwa ini adalah yang ingin Anda lakukan, dan bahwa Anda akan membuka kunci basis data setelah pemeliharaan selesai.", |
1146 | | -"unlockdbtext" => "Membuka kunci basis data akan mengembalikan kemampuan semua pengguna dalam menyunting halaman, mengubah preferensi pengguna, menyunting daftar pemantauan mereka, dan hal-hal lain yang memerlukan perubahan terhadap basis data. Pastikan bahwa ini adalah yang ingin Anda lakukan.", |
| 1151 | +"lockdbtext" => "Mengunci basis data akan menghentikan kemampuan semua pengguna dalam menyunting halaman, mengubah preferensi pengguna, menyunting daftar pantauan mereka, dan hal-hal lain yang memerlukan perubahan terhadap basis data. Pastikan bahwa ini adalah yang ingin Anda lakukan, dan bahwa Anda akan membuka kunci basis data setelah pemeliharaan selesai.", |
| 1152 | +"unlockdbtext" => "Membuka kunci basis data akan mengembalikan kemampuan semua pengguna dalam menyunting halaman, mengubah preferensi pengguna, menyunting daftar pantauan mereka, dan hal-hal lain yang memerlukan perubahan terhadap basis data. Pastikan bahwa ini adalah yang ingin Anda lakukan.", |
1147 | 1153 | "lockconfirm" => "Ya, saya memang ingin mengunci basis data.", |
1148 | 1154 | "unlockconfirm" => "Ya, saya memang ingin membuka kunci basis data.", |
1149 | 1155 | "lockbtn" => "Kunci basis data", |
— | — | @@ -1242,7 +1248,7 @@ |
1243 | 1249 | 'tooltip-preview' => 'Pratilik perubahan Anda -- mohon gunakan ini sebelum menyimpan! [alt-p]', |
1244 | 1250 | 'tooltip-diff' => 'Lihat perubahan yang telah Anda lakukan. [alt-v]', |
1245 | 1251 | 'tooltip-compareselectedversions' => 'Lihat perbedaan antara dua versi halaman yang dipilih. [alt-v]', |
1246 | | -'tooltip-watch' => 'Tambahkan halaman ini ke daftar pemantauan Anda [alt-w]', |
| 1252 | +'tooltip-watch' => 'Tambahkan halaman ini ke daftar pantauan Anda [alt-w]', |
1247 | 1253 | |
1248 | 1254 | # stylesheets |
1249 | 1255 | |
— | — | @@ -1315,7 +1321,7 @@ |
1316 | 1322 | ta[\'pt-anonuserpage\'] = new Array(\'.\',\'Halaman pengguna IP Anda\'); |
1317 | 1323 | ta[\'pt-mytalk\'] = new Array(\'n\',\'Halaman pembicaraan saya\'); |
1318 | 1324 | ta[\'pt-anontalk\'] = new Array(\'n\',\'Diskusi tentang suntingan dari alamat IP ini\'); |
1319 | | -ta[\'pt-preferences\'] = new Array(\'\',\'Konfigurasi saya\'); |
| 1325 | +ta[\'pt-preferences\'] = new Array(\'\',\'Preferensi saya\'); |
1320 | 1326 | ta[\'pt-watchlist\'] = new Array(\'l\',\'Daftar halaman yang Anda pantau.\'); |
1321 | 1327 | ta[\'pt-mycontris\'] = new Array(\'y\',\'Daftar sumbangan saya\'); |
1322 | 1328 | ta[\'pt-login\'] = new Array(\'o\',\'Anda disarankan untuk masuk log, meskipun hal itu tidak diwajibkan.\'); |
— | — | @@ -1330,8 +1336,8 @@ |
1331 | 1337 | ta[\'ca-delete\'] = new Array(\'d\',\'Hapus halaman ini\'); |
1332 | 1338 | ta[\'ca-undelete\'] = new Array(\'d\',\'Kembalikan suntingan ke halaman ini sebelum halaman ini dihapus\'); |
1333 | 1339 | ta[\'ca-move\'] = new Array(\'m\',\'Pindahkan halaman ini\'); |
1334 | | -ta[\'ca-watch\'] = new Array(\'w\',\'Tambahkan halaman ini ke daftar pemantauan Anda\'); |
1335 | | -ta[\'ca-unwatch\'] = new Array(\'w\',\'Hapus halaman ini dari daftar pemantauan Anda\'); |
| 1340 | +ta[\'ca-watch\'] = new Array(\'w\',\'Tambahkan halaman ini ke daftar pantauan Anda\'); |
| 1341 | +ta[\'ca-unwatch\'] = new Array(\'w\',\'Hapus halaman ini dari daftar pantauan Anda\'); |
1336 | 1342 | ta[\'search\'] = new Array(\'f\',\'Cari dalam wiki ini\'); |
1337 | 1343 | ta[\'p-logo\'] = new Array(\'\',\'Halaman Utama\'); |
1338 | 1344 | ta[\'n-mainpage\'] = new Array(\'z\',\'Kunjungi Halaman Utama\'); |
— | — | @@ -1414,7 +1420,286 @@ |
1415 | 1421 | * fnumber |
1416 | 1422 | * focallength', |
1417 | 1423 | |
| 1424 | +# Exif tags |
| 1425 | +'exif-imagewidth' =>'Lebar', |
| 1426 | +'exif-imagelength' =>'Tinggi', |
| 1427 | +'exif-bitspersample' =>'Bit per komponen', |
| 1428 | +'exif-compression' =>'Skema kompresi', |
| 1429 | +'exif-photometricinterpretation' =>'Komposisi piksel', |
| 1430 | +'exif-orientation' =>'Orientasi', |
| 1431 | +'exif-samplesperpixel' =>'Jumlah komponen', |
| 1432 | +'exif-planarconfiguration' =>'Pengaturan data', |
| 1433 | +'exif-ycbcrsubsampling' =>'Rasio subsampling Y ke C', |
| 1434 | +'exif-ycbcrpositioning' =>'Penempatan Y dan C', |
| 1435 | +'exif-xresolution' =>'Resolusi horizontal', |
| 1436 | +'exif-yresolution' =>'Resolusi vertical', |
| 1437 | +'exif-resolutionunit' =>'Satuan resolusi X dan Y', |
| 1438 | +'exif-stripoffsets' =>'Lokasi data gambar', |
| 1439 | +'exif-rowsperstrip' =>'Jumlah baris per strip', |
| 1440 | +'exif-stripbytecounts' =>'Byte per strip kompresi', |
| 1441 | +'exif-jpeginterchangeformat' =>'Offset ke JPEG SOI', |
| 1442 | +'exif-jpeginterchangeformatlength' =>'Byte data JPEG', |
| 1443 | +'exif-transferfunction' =>'Fungsi transfer', |
| 1444 | +'exif-whitepoint' =>'Kromatisitas titik putih', |
| 1445 | +'exif-primarychromaticities' =>'Kromatisitas warna primer', |
| 1446 | +'exif-ycbcrcoefficients' =>'Koefisien matriks transformasi ruang warna', |
| 1447 | +'exif-referenceblackwhite' =>'Nilai referensi pasangan hitam putih', |
| 1448 | +'exif-datetime' =>'Tanggal dan waktu perubahan berkas', |
| 1449 | +'exif-imagedescription' =>'Judul gambar', |
| 1450 | +'exif-make' =>'Produsen kamera', |
| 1451 | +'exif-model' =>'Model kamera', |
| 1452 | +'exif-software' =>'Perangkat lunak', |
| 1453 | +'exif-artist' =>'Pembuat', |
| 1454 | +'exif-copyright' =>'Pemilik hak cipta', |
| 1455 | +'exif-exifversion' =>'Versi Exif', |
| 1456 | +'exif-flashpixversion' =>'Dukungan versi Flashpix', |
| 1457 | +'exif-colorspace' =>'Ruang warna', |
| 1458 | +'exif-componentsconfiguration' =>'Arti tiap komponen', |
| 1459 | +'exif-compressedbitsperpixel' =>'Mode kompresi gambar', |
| 1460 | +'exif-pixelydimension' =>'Lebar gambar yang sah', |
| 1461 | +'exif-pixelxdimension' =>'Tinggi gambar yang sah', |
| 1462 | +'exif-makernote' =>'Catatan produsen', |
| 1463 | +'exif-usercomment' =>'Komentar pengguna', |
| 1464 | +'exif-relatedsoundfile' =>'Berkas audio yang berhubungan', |
| 1465 | +'exif-datetimeoriginal' =>'Tanggal dan waktu pembuatan data', |
| 1466 | +'exif-datetimedigitized' =>'Tanggal dan waktu digitalisasi', |
| 1467 | +'exif-subsectime' =>'Subdetik DateTime', |
| 1468 | +'exif-subsectimeoriginal' =>'Subdetik DateTimeOriginal', |
| 1469 | +'exif-subsectimedigitized' =>'Subdetik DateTimeDigitized', |
| 1470 | +'exif-exposuretime' =>'Waktu exposure', |
| 1471 | +'exif-exposuretime-format' => '$1 detik ($2)', |
| 1472 | +'exif-fnumber' =>'F Number', |
| 1473 | +'exif-fnumber-format' =>'f/$1', |
| 1474 | +'exif-exposureprogram' =>'Exposure Program', |
| 1475 | +'exif-spectralsensitivity' =>'Sensitivitas spektral', |
| 1476 | +'exif-isospeedratings' =>'ISO speed rating', |
| 1477 | +'exif-oecf' =>'Faktor konversi optoelektronik', |
| 1478 | +'exif-shutterspeedvalue' =>'Kecepatan shutter', |
| 1479 | +'exif-aperturevalue' =>'Aperture', |
| 1480 | +'exif-brightnessvalue' =>'Brightness', |
| 1481 | +'exif-exposurebiasvalue' =>'Exposure bias', |
| 1482 | +'exif-maxaperturevalue' =>'Maximum land aperture', |
| 1483 | +'exif-subjectdistance' =>'Jarak subyek', |
| 1484 | +'exif-meteringmode' =>'Metering mode', |
| 1485 | +'exif-lightsource' =>'Sumber cahaya', |
| 1486 | +'exif-flash' =>'Flash', |
| 1487 | +'exif-focallength' =>'Lens focal length', |
| 1488 | +'exif-focallength-format' =>'$1 mm', |
| 1489 | +'exif-subjectarea' =>'Wilayah subyek', |
| 1490 | +'exif-flashenergy' =>'Flash energy', |
| 1491 | +'exif-spatialfrequencyresponse' =>'Respons frekuensi spasial', |
| 1492 | +'exif-focalplanexresolution' =>'Resolusi focal plane X', |
| 1493 | +'exif-focalplaneyresolution' =>'Resolusi focal plane Y', |
| 1494 | +'exif-focalplaneresolutionunit' =>'Unit resolusi focal plane', |
| 1495 | +'exif-subjectlocation' =>'Lokasi subyek', |
| 1496 | +'exif-exposureindex' =>'Indeks exposure', |
| 1497 | +'exif-sensingmethod' =>'Metode sensing', |
| 1498 | +'exif-filesource' =>'Sumber berkas', |
| 1499 | +'exif-scenetype' =>'Tipe scene', |
| 1500 | +'exif-cfapattern' =>'Pola CFA', |
| 1501 | +'exif-customrendered' =>'Proses buatan gambar', |
| 1502 | +'exif-exposuremode' =>'Mode exposure', |
| 1503 | +'exif-whitebalance' =>'White Balance', |
| 1504 | +'exif-digitalzoomratio' =>'Rasio pembesaran digital', |
| 1505 | +'exif-focallengthin35mmfilm' =>'Focal length in 35 mm film', |
| 1506 | +'exif-scenecapturetype' =>'Tipe scene capture', |
| 1507 | +'exif-gaincontrol' =>'Kontrol scene', |
| 1508 | +'exif-contrast' =>'Kontras', |
| 1509 | +'exif-saturation' =>'Saturasi', |
| 1510 | +'exif-sharpness' =>'Ketajaman', |
| 1511 | +'exif-devicesettingdescription' =>'Deskripsi pengaturan alat', |
| 1512 | +'exif-subjectdistancerange' =>'Jarak subyek', |
| 1513 | +'exif-imageuniqueid' =>'ID unik gambar', |
| 1514 | +'exif-gpsversionid' =>'Versi tag GPS', |
| 1515 | +'exif-gpslatituderef' =>'Lintang Utara atau Selatan', |
| 1516 | +'exif-gpslatitude' =>'Lintang', |
| 1517 | +'exif-gpslongituderef' =>'Bujur Timur atau Barat', |
| 1518 | +'exif-gpslongitude' =>'Bujur', |
| 1519 | +'exif-gpsaltituderef' =>'Referensi ketinggian', |
| 1520 | +'exif-gpsaltitude' =>'Ketinggian', |
| 1521 | +'exif-gpstimestamp' =>'Waktu GPS (jam atom)', |
| 1522 | +'exif-gpssatellites' =>'Satelit untuk pengukuran', |
| 1523 | +'exif-gpsstatus' =>'Status penerima', |
| 1524 | +'exif-gpsmeasuremode' =>'Mode pengukuran', |
| 1525 | +'exif-gpsdop' =>'Ketepatan pengukuran', |
| 1526 | +'exif-gpsspeedref' =>'Unit kecepatan', |
| 1527 | +'exif-gpsspeed' =>'Kecepatan penerima GPS', |
| 1528 | +'exif-gpstrackref' =>'Referensi arah gerakan', |
| 1529 | +'exif-gpstrack' =>'Arah gerakan', |
| 1530 | +'exif-gpsimgdirectionref' =>'Referensi arah gambar', |
| 1531 | +'exif-gpsimgdirection' =>'Arah gambar', |
| 1532 | +'exif-gpsmapdatum' =>'Data survei geodesi', |
| 1533 | +'exif-gpsdestlatituderef' =>'Referensi lintang dari tujuan', |
| 1534 | +'exif-gpsdestlatitude' =>'Lintang tujuan', |
| 1535 | +'exif-gpsdestlongituderef' =>'Referensi bujur dari tujuan', |
| 1536 | +'exif-gpsdestlongitude' =>'Bujur tujuan', |
| 1537 | +'exif-gpsdestbearingref' =>'Referensi bearing of destination', |
| 1538 | +'exif-gpsdestbearing' =>'Bearing of destination', |
| 1539 | +'exif-gpsdestdistanceref' =>'Referensi jarak dari tujuan', |
| 1540 | +'exif-gpsdestdistance' =>'Jarak dari tujuan', |
| 1541 | +'exif-gpsprocessingmethod' =>'Nama metode proses GPS', |
| 1542 | +'exif-gpsareainformation' =>'Nama wilayah GPS', |
| 1543 | +'exif-gpsdatestamp' =>'Tanggal GPS', |
| 1544 | +'exif-gpsdifferential' =>'Koreksi diferensial GPS', |
1418 | 1545 | |
| 1546 | +# Make & model, can be wikified in order to link to the camera and model name |
| 1547 | + |
| 1548 | +'exif-make-value' => '$1', |
| 1549 | +'exif-model-value' =>'$1', |
| 1550 | +'exif-software-value' => '$1', |
| 1551 | + |
| 1552 | +# Exif attributes |
| 1553 | + |
| 1554 | +'exif-compression-1' => 'Tak terkompresi', |
| 1555 | +'exif-compression-6' => 'JPEG', |
| 1556 | + |
| 1557 | +'exif-photometricinterpretation-2' => 'RGB', |
| 1558 | +'exif-photometricinterpretation-6' => 'YCbCr', |
| 1559 | + |
| 1560 | +'exif-orientation-1' => 'Normal', // 0th row: top; 0th column: left |
| 1561 | +'exif-orientation-2' => 'Dibalik horizontal', // 0th row: top; 0th column: right |
| 1562 | +'exif-orientation-3' => 'Diputar 180°', // 0th row: bottom; 0th column: right |
| 1563 | +'exif-orientation-4' => 'Dibalik vertikal', // 0th row: bottom; 0th column: left |
| 1564 | +'exif-orientation-5' => 'Diputar 90° CCW dan dibalik vertical', // 0th row: left; 0th column: top |
| 1565 | +'exif-orientation-6' => 'Diputar 90° CW', // 0th row: right; 0th column: top |
| 1566 | +'exif-orientation-7' => 'Diputar 90° CW dan dibalik vertical', // 0th row: right; 0th column: bottom |
| 1567 | +'exif-orientation-8' => 'Diputar 90° CCW', // 0th row: left; 0th column: bottom |
| 1568 | + |
| 1569 | +'exif-planarconfiguration-1' => 'format chunky', |
| 1570 | +'exif-planarconfiguration-2' => 'format planar', |
| 1571 | + |
| 1572 | +'exif-xyresolution-i' => '$1 dpi', |
| 1573 | +'exif-xyresolution-c' => '$1 dpc', |
| 1574 | + |
| 1575 | +'exif-colorspace-1' => 'sRGB', |
| 1576 | +'exif-colorspace-ffff.h' => 'FFFF.H', |
| 1577 | + |
| 1578 | +'exif-componentsconfiguration-0' => 'tak tersedia', |
| 1579 | +'exif-componentsconfiguration-1' => 'Y', |
| 1580 | +'exif-componentsconfiguration-2' => 'Cb', |
| 1581 | +'exif-componentsconfiguration-3' => 'Cr', |
| 1582 | +'exif-componentsconfiguration-4' => 'R', |
| 1583 | +'exif-componentsconfiguration-5' => 'G', |
| 1584 | +'exif-componentsconfiguration-6' => 'B', |
| 1585 | + |
| 1586 | +'exif-exposureprogram-0' => 'Tak terdefinisi', |
| 1587 | +'exif-exposureprogram-1' => 'Manual', |
| 1588 | +'exif-exposureprogram-2' => 'Program normal', |
| 1589 | +'exif-exposureprogram-3' => 'Prioritas aperture', |
| 1590 | +'exif-exposureprogram-4' => 'Prioritas shutter', |
| 1591 | +'exif-exposureprogram-5' => 'Program kreatif (condong ke depth of field)', |
| 1592 | +'exif-exposureprogram-6' => 'Program aksi (condong ke fast shutter speed)', |
| 1593 | +'exif-exposureprogram-7' => 'Mode potret (untuk foto closeup dengan latar belakang tak fokus)', |
| 1594 | +'exif-exposureprogram-8' => 'Mode pemandangan (untuk foto pemandangan dengan latar belakang fokus)', |
| 1595 | + |
| 1596 | +'exif-subjectdistance-value' => '$1 meter', |
| 1597 | + |
| 1598 | +'exif-meteringmode-0' => 'Tak diketahui', |
| 1599 | +'exif-meteringmode-1' => 'Average', |
| 1600 | +'exif-meteringmode-2' => 'CenterWeightedAverage', |
| 1601 | +'exif-meteringmode-3' => 'Spot', |
| 1602 | +'exif-meteringmode-4' => 'MultiSpot', |
| 1603 | +'exif-meteringmode-5' => 'Pattern', |
| 1604 | +'exif-meteringmode-6' => 'Partial', |
| 1605 | +'exif-meteringmode-255' => 'Lain-lain', |
| 1606 | + |
| 1607 | +'exif-lightsource-0' => 'Tak diketahui', |
| 1608 | +'exif-lightsource-1' => 'Daylight', |
| 1609 | +'exif-lightsource-2' => 'Fluorescent', |
| 1610 | +'exif-lightsource-3' => 'Tungsten (incandescent light)', |
| 1611 | +'exif-lightsource-4' => 'Flash', |
| 1612 | +'exif-lightsource-9' => 'Fine weather', |
| 1613 | +'exif-lightsource-10' => 'Cloudy weather', |
| 1614 | +'exif-lightsource-11' => 'Shade', |
| 1615 | +'exif-lightsource-12' => 'Daylight fluorescent (D 5700 – 7100K)', |
| 1616 | +'exif-lightsource-13' => 'Day white fluorescent (N 4600 – 5400K)', |
| 1617 | +'exif-lightsource-14' => 'Cool white fluorescent (W 3900 – 4500K)', |
| 1618 | +'exif-lightsource-15' => 'White fluorescent (WW 3200 – 3700K)', |
| 1619 | +'exif-lightsource-17' => 'Standard light A', |
| 1620 | +'exif-lightsource-18' => 'Standard light B', |
| 1621 | +'exif-lightsource-19' => 'Standard light C', |
| 1622 | +'exif-lightsource-20' => 'D55', |
| 1623 | +'exif-lightsource-21' => 'D65', |
| 1624 | +'exif-lightsource-22' => 'D75', |
| 1625 | +'exif-lightsource-23' => 'D50', |
| 1626 | +'exif-lightsource-24' => 'ISO studio tungsten', |
| 1627 | +'exif-lightsource-255' => 'Sumber cahaya lain', |
| 1628 | + |
| 1629 | +'exif-focalplaneresolutionunit-2' => 'inci', |
| 1630 | + |
| 1631 | +'exif-sensingmethod-1' => 'Tak terdefinisi', |
| 1632 | +'exif-sensingmethod-2' => 'One-chip color area sensor', |
| 1633 | +'exif-sensingmethod-3' => 'Two-chip color area sensor', |
| 1634 | +'exif-sensingmethod-4' => 'Three-chip color area sensor', |
| 1635 | +'exif-sensingmethod-5' => 'Color sequential area sensor', |
| 1636 | +'exif-sensingmethod-7' => 'Trilinear sensor', |
| 1637 | +'exif-sensingmethod-8' => 'Color sequential linear sensor', |
| 1638 | + |
| 1639 | +'exif-filesource-3' => 'DSC', |
| 1640 | + |
| 1641 | +'exif-scenetype-1' => 'Gambar foto langsung', |
| 1642 | + |
| 1643 | +'exif-customrendered-0' => 'Proses normal', |
| 1644 | +'exif-customrendered-1' => 'Proses kustom', |
| 1645 | + |
| 1646 | +'exif-exposuremode-0' => 'Exposure otomatis', |
| 1647 | +'exif-exposuremode-1' => 'Exposure manual', |
| 1648 | +'exif-exposuremode-2' => 'Bracket otomatis', |
| 1649 | + |
| 1650 | +'exif-whitebalance-0' => 'Auto white balance', |
| 1651 | +'exif-whitebalance-1' => 'Manual white balance', |
| 1652 | + |
| 1653 | +'exif-scenecapturetype-0' => 'Standar', |
| 1654 | +'exif-scenecapturetype-1' => 'Melebar', |
| 1655 | +'exif-scenecapturetype-2' => 'Potret', |
| 1656 | +'exif-scenecapturetype-3' => 'Scene malam', |
| 1657 | + |
| 1658 | +'exif-gaincontrol-0' => 'Tak ada', |
| 1659 | +'exif-gaincontrol-1' => 'Low gain up', |
| 1660 | +'exif-gaincontrol-2' => 'High gain up', |
| 1661 | +'exif-gaincontrol-3' => 'Low gain down', |
| 1662 | +'exif-gaincontrol-4' => 'High gain down', |
| 1663 | + |
| 1664 | +'exif-contrast-0' => 'Normal', |
| 1665 | +'exif-contrast-1' => 'Lembut', |
| 1666 | +'exif-contrast-2' => 'Keras', |
| 1667 | + |
| 1668 | +'exif-saturation-0' => 'Normal', |
| 1669 | +'exif-saturation-1' => 'Saturasi rendah', |
| 1670 | +'exif-saturation-2' => 'Saturasi tinggi', |
| 1671 | + |
| 1672 | +'exif-sharpness-0' => 'Normal', |
| 1673 | +'exif-sharpness-1' => 'Lembut', |
| 1674 | +'exif-sharpness-2' => 'Keras', |
| 1675 | + |
| 1676 | +'exif-subjectdistancerange-0' => 'Tak diketahui', |
| 1677 | +'exif-subjectdistancerange-1' => 'Makro', |
| 1678 | +'exif-subjectdistancerange-2' => 'Close view', |
| 1679 | +'exif-subjectdistancerange-3' => 'Distant view', |
| 1680 | + |
| 1681 | +// Pseudotags used for GPSLatitudeRef and GPSDestLatitudeRef |
| 1682 | +'exif-gpslatitude-n' => 'Lintang utara', |
| 1683 | +'exif-gpslatitude-s' => 'Lintang selatan', |
| 1684 | + |
| 1685 | +// Pseudotags used for GPSLongitudeRef and GPSDestLongitudeRef |
| 1686 | +'exif-gpslongitude-e' => 'Bujur timur', |
| 1687 | +'exif-gpslongitude-w' => 'Bujur barat', |
| 1688 | + |
| 1689 | +'exif-gpsstatus-a' => 'Pengukuran sedang berlangsung', |
| 1690 | +'exif-gpsstatus-v' => 'Interoperabilitas pengukuran', |
| 1691 | + |
| 1692 | +'exif-gpsmeasuremode-2' => 'Pengukuran 2-dimensi', |
| 1693 | +'exif-gpsmeasuremode-3' => 'Pengukuran 3-dimensi', |
| 1694 | + |
| 1695 | +// Pseudotags used for GPSSpeedRef and GPSDestDistanceRef |
| 1696 | +'exif-gpsspeed-k' => 'Kilometer per jam', |
| 1697 | +'exif-gpsspeed-m' => 'Mil per jam', |
| 1698 | +'exif-gpsspeed-n' => 'Knot', |
| 1699 | + |
| 1700 | +// Pseudotags used for GPSTrackRef, GPSImgDirectionRef and GPSDestBearingRef |
| 1701 | +'exif-gpsdirection-t' => 'Arah sejati', |
| 1702 | +'exif-gpsdirection-m' => 'Arah magnetis', |
| 1703 | + |
1419 | 1704 | # external editor support |
1420 | 1705 | 'edit-externally' => 'Sunting berkas ini dengan aplikasi luar', |
1421 | 1706 | 'edit-externally-help' => 'Lihat [http://meta.wikimedia.org/wiki/Help:External_editors instruksi pengaturan] untuk informasi lebih lanjut.', |
Index: branches/hashar/maintenance/storage/checkStorage.php |
— | — | @@ -0,0 +1,468 @@ |
| 2 | +<?php
|
| 3 | +
|
| 4 | +/**
|
| 5 | + * Fsck for MediaWiki
|
| 6 | + */
|
| 7 | +
|
| 8 | +define( 'CONCAT_HEADER', 'O:27:"concatenatedgziphistoryblob"' );
|
| 9 | +
|
| 10 | +if ( !defined( 'MEDIAWIKI' ) ) {
|
| 11 | + require_once( dirname(__FILE__) . '/../commandLine.inc' );
|
| 12 | + require_once( 'ExternalStore.php' );
|
| 13 | + require_once( 'ExternalStoreDB.php' );
|
| 14 | + require_once( 'SpecialImport.php' );
|
| 15 | +
|
| 16 | + $cs = new CheckStorage;
|
| 17 | + $fix = isset( $options['fix'] );
|
| 18 | + if ( isset( $args[0] ) ) {
|
| 19 | + $xml = $args[0];
|
| 20 | + } else {
|
| 21 | + $xml = false;
|
| 22 | + }
|
| 23 | + $cs->check( $fix, $xml );
|
| 24 | +}
|
| 25 | +
|
| 26 | +
|
| 27 | +//----------------------------------------------------------------------------------
|
| 28 | +
|
| 29 | +class CheckStorage
|
| 30 | +{
|
| 31 | + var $oldIdMap, $errors;
|
| 32 | + var $dbStore = null;
|
| 33 | +
|
| 34 | + var $errorDescriptions = array(
|
| 35 | + 'restore text' => 'Damaged text, need to be restored from a backup',
|
| 36 | + 'restore revision' => 'Damaged revision row, need to be restored from a backup',
|
| 37 | + 'unfixable' => 'Unexpected errors with no automated fixing method',
|
| 38 | + 'fixed' => 'Errors already fixed',
|
| 39 | + 'fixable' => 'Errors which would already be fixed if --fix was specified',
|
| 40 | + );
|
| 41 | +
|
| 42 | + function check( $fix = false, $xml = '' ) {
|
| 43 | + $fname = 'checkStorage';
|
| 44 | + $dbr =& wfGetDB( DB_SLAVE );
|
| 45 | + if ( $fix ) {
|
| 46 | + $dbw =& wfGetDB( DB_MASTER );
|
| 47 | + print "Checking, will fix errors if possible...\n";
|
| 48 | + } else {
|
| 49 | + print "Checking...\n";
|
| 50 | + }
|
| 51 | + $maxRevId = $dbr->selectField( 'revision', 'MAX(rev_id)', false, $fname );
|
| 52 | + $chunkSize = 1000;
|
| 53 | + $flagStats = array();
|
| 54 | + $objectStats = array();
|
| 55 | + $knownFlags = array( 'external', 'gzip', 'object', 'utf-8' );
|
| 56 | + $this->errors = array(
|
| 57 | + 'restore text' => array(),
|
| 58 | + 'restore revision' => array(),
|
| 59 | + 'unfixable' => array(),
|
| 60 | + 'fixed' => array(),
|
| 61 | + 'fixable' => array(),
|
| 62 | + );
|
| 63 | +
|
| 64 | + for ( $chunkStart = 1 ; $chunkStart < $maxRevId; $chunkStart += $chunkSize ) {
|
| 65 | + $chunkEnd = $chunkStart + $chunkSize - 1;
|
| 66 | + //print "$chunkStart of $maxRevId\n";
|
| 67 | +
|
| 68 | + // Fetch revision rows
|
| 69 | + $this->oldIdMap = array();
|
| 70 | + $dbr->ping();
|
| 71 | + $res = $dbr->select( 'revision', array( 'rev_id', 'rev_text_id' ),
|
| 72 | + array( "rev_id BETWEEN $chunkStart AND $chunkEnd" ), $fname );
|
| 73 | + while ( $row = $dbr->fetchObject( $res ) ) {
|
| 74 | + $this->oldIdMap[$row->rev_id] = $row->rev_text_id;
|
| 75 | + }
|
| 76 | + $dbr->freeResult( $res );
|
| 77 | +
|
| 78 | + if ( !count( $this->oldIdMap ) ) {
|
| 79 | + continue;
|
| 80 | + }
|
| 81 | +
|
| 82 | + // Fetch old_flags
|
| 83 | + $missingTextRows = array_flip( $this->oldIdMap );
|
| 84 | + $externalRevs = array();
|
| 85 | + $objectRevs = array();
|
| 86 | + $res = $dbr->select( 'text', array( 'old_id', 'old_flags' ),
|
| 87 | + 'old_id IN (' . implode( ',', $this->oldIdMap ) . ')', $fname );
|
| 88 | + while ( $row = $dbr->fetchObject( $res ) ) {
|
| 89 | + $flags = $row->old_flags;
|
| 90 | + $id = $row->old_id;
|
| 91 | +
|
| 92 | + // Create flagStats row if it doesn't exist
|
| 93 | + $flagStats = $flagStats + array( $flags => 0 );
|
| 94 | + // Increment counter
|
| 95 | + $flagStats[$flags]++;
|
| 96 | +
|
| 97 | + // Not missing
|
| 98 | + unset( $missingTextRows[$row->old_id] );
|
| 99 | +
|
| 100 | + // Check for external or object
|
| 101 | + if ( $flags == '' ) {
|
| 102 | + $flagArray = array();
|
| 103 | + } else {
|
| 104 | + $flagArray = explode( ',', $flags );
|
| 105 | + }
|
| 106 | + if ( in_array( 'external', $flagArray ) ) {
|
| 107 | + $externalRevs[] = $id;
|
| 108 | + } elseif ( in_array( 'object', $flagArray ) ) {
|
| 109 | + $objectRevs[] = $id;
|
| 110 | + }
|
| 111 | +
|
| 112 | + // Check for unrecognised flags
|
| 113 | + if ( $flags == '0' ) {
|
| 114 | + // This is a known bug from 2004
|
| 115 | + // It's safe to just erase the old_flags field
|
| 116 | + if ( $fix ) {
|
| 117 | + $this->error( 'fixed', "Warning: old_flags set to 0", $id );
|
| 118 | + $dbw->ping();
|
| 119 | + $dbw->update( 'text', array( 'old_flags' => '' ),
|
| 120 | + array( 'old_id' => $id ), $fname );
|
| 121 | + echo "Fixed\n";
|
| 122 | + } else {
|
| 123 | + $this->error( 'fixable', "Warning: old_flags set to 0", $id );
|
| 124 | + }
|
| 125 | + } elseif ( count( array_diff( $flagArray, $knownFlags ) ) ) {
|
| 126 | + $this->error( 'unfixable', "Error: invalid flags field \"$flags\"", $id );
|
| 127 | + }
|
| 128 | + }
|
| 129 | + $dbr->freeResult( $res );
|
| 130 | +
|
| 131 | + // Output errors for any missing text rows
|
| 132 | + foreach ( $missingTextRows as $oldId => $revId ) {
|
| 133 | + $this->error( 'restore revision', "Error: missing text row", $oldId );
|
| 134 | + }
|
| 135 | +
|
| 136 | + // Verify external revisions
|
| 137 | + $externalConcatBlobs = array();
|
| 138 | + $externalNormalBlobs = array();
|
| 139 | + if ( count( $externalRevs ) ) {
|
| 140 | + $res = $dbr->select( 'text', array( 'old_id', 'old_flags', 'old_text' ),
|
| 141 | + array( 'old_id IN (' . implode( ',', $externalRevs ) . ')' ), $fname );
|
| 142 | + while ( $row = $dbr->fetchObject( $res ) ) {
|
| 143 | + $urlParts = explode( '://', $row->old_text, 2 );
|
| 144 | + if ( count( $urlParts ) !== 2 || $urlParts[1] == '' ) {
|
| 145 | + $this->error( 'restore text', "Error: invalid URL \"{$row->old_text}\"", $row->old_id );
|
| 146 | + continue;
|
| 147 | + }
|
| 148 | + list( $proto, $path ) = $urlParts;
|
| 149 | + if ( $proto != 'DB' ) {
|
| 150 | + $this->error( 'restore text', "Error: invalid external protocol \"$proto\"", $row->old_id );
|
| 151 | + continue;
|
| 152 | + }
|
| 153 | + $path = explode( '/', $row->old_text );
|
| 154 | + $cluster = $path[2];
|
| 155 | + $id = $path[3];
|
| 156 | + if ( isset( $path[4] ) ) {
|
| 157 | + $externalConcatBlobs[$cluster][$id][] = $row->old_id;
|
| 158 | + } else {
|
| 159 | + $externalNormalBlobs[$cluster][$id][] = $row->old_id;
|
| 160 | + }
|
| 161 | + }
|
| 162 | + $dbr->freeResult( $res );
|
| 163 | + }
|
| 164 | +
|
| 165 | + // Check external concat blobs for the right header
|
| 166 | + $this->checkExternalConcatBlobs( $externalConcatBlobs );
|
| 167 | +
|
| 168 | + // Check external normal blobs for existence
|
| 169 | + if ( count( $externalNormalBlobs ) ) {
|
| 170 | + if ( is_null( $this->dbStore ) ) {
|
| 171 | + $this->dbStore = new ExternalStoreDB;
|
| 172 | + }
|
| 173 | + foreach ( $externalConcatBlobs as $cluster => $xBlobIds ) {
|
| 174 | + $blobIds = array_keys( $xBlobIds );
|
| 175 | + $extDb =& $this->dbStore->getSlave( $cluster );
|
| 176 | + $blobsTable = $this->dbStore->getTable( $extDb );
|
| 177 | + $res = $extDb->select( $blobsTable,
|
| 178 | + array( 'blob_id' ),
|
| 179 | + array( 'blob_id IN( ' . implode( ',', $blobIds ) . ')' ), $fname );
|
| 180 | + while ( $row = $extDb->fetchObject( $res ) ) {
|
| 181 | + unset( $xBlobIds[$row->blob_id] );
|
| 182 | + }
|
| 183 | + $extDb->freeResult( $res );
|
| 184 | + // Print errors for missing blobs rows
|
| 185 | + foreach ( $xBlobIds as $blobId => $oldId ) {
|
| 186 | + $this->error( 'restore text', "Error: missing target $blobId for one-part ES URL", $oldId );
|
| 187 | + }
|
| 188 | + }
|
| 189 | + }
|
| 190 | +
|
| 191 | + // Check local objects
|
| 192 | + $dbr->ping();
|
| 193 | + $concatBlobs = array();
|
| 194 | + $curIds = array();
|
| 195 | + if ( count( $objectRevs ) ) {
|
| 196 | + $headerLength = 300;
|
| 197 | + $res = $dbr->select( 'text', array( 'old_id', 'old_flags', "LEFT(old_text, $headerLength) AS header" ),
|
| 198 | + array( 'old_id IN (' . implode( ',', $objectRevs ) . ')' ), $fname );
|
| 199 | + while ( $row = $dbr->fetchObject( $res ) ) {
|
| 200 | + $oldId = $row->old_id;
|
| 201 | + if ( !preg_match( '/^O:(\d+):"(\w+)"/', $row->header, $matches ) ) {
|
| 202 | + $this->error( 'restore text', "Error: invalid object header", $oldId );
|
| 203 | + continue;
|
| 204 | + }
|
| 205 | +
|
| 206 | + $className = strtolower( $matches[2] );
|
| 207 | + if ( strlen( $className ) != $matches[1] ) {
|
| 208 | + $this->error( 'restore text', "Error: invalid object header, wrong class name length", $oldId );
|
| 209 | + continue;
|
| 210 | + }
|
| 211 | +
|
| 212 | + $objectStats = $objectStats + array( $className => 0 );
|
| 213 | + $objectStats[$className]++;
|
| 214 | +
|
| 215 | + switch ( $className ) {
|
| 216 | + case 'concatenatedgziphistoryblob':
|
| 217 | + // Good
|
| 218 | + break;
|
| 219 | + case 'historyblobstub':
|
| 220 | + case 'historyblobcurstub':
|
| 221 | + if ( strlen( $row->header ) == $headerLength ) {
|
| 222 | + $this->error( 'unfixable', "Error: overlong stub header", $oldId );
|
| 223 | + continue;
|
| 224 | + }
|
| 225 | + $stubObj = unserialize( $row->header );
|
| 226 | + if ( !is_object( $stubObj ) ) {
|
| 227 | + $this->error( 'restore text', "Error: unable to unserialize stub object", $oldId );
|
| 228 | + continue;
|
| 229 | + }
|
| 230 | + if ( $className == 'historyblobstub' ) {
|
| 231 | + $concatBlobs[$stubObj->mOldId][] = $oldId;
|
| 232 | + } else {
|
| 233 | + $curIds[$stubObj->mCurId][] = $oldId;
|
| 234 | + }
|
| 235 | + break;
|
| 236 | + default:
|
| 237 | + $this->error( 'unfixable', "Error: unrecognised object class \"$className\"", $oldId );
|
| 238 | + }
|
| 239 | + }
|
| 240 | + $dbr->freeResult( $res );
|
| 241 | + }
|
| 242 | +
|
| 243 | + // Check local concat blob validity
|
| 244 | + $externalConcatBlobs = array();
|
| 245 | + if ( count( $concatBlobs ) ) {
|
| 246 | + $headerLength = 300;
|
| 247 | + $res = $dbr->select( 'text', array( 'old_id', 'old_flags', "LEFT(old_text, $headerLength) AS header" ),
|
| 248 | + array( 'old_id IN (' . implode( ',', array_keys( $concatBlobs ) ) . ')' ), $fname );
|
| 249 | + while ( $row = $dbr->fetchObject( $res ) ) {
|
| 250 | + $flags = explode( ',', $row->old_flags );
|
| 251 | + if ( in_array( 'external', $flags ) ) {
|
| 252 | + // Concat blob is in external storage?
|
| 253 | + if ( in_array( 'object', $flags ) ) {
|
| 254 | + $urlParts = explode( '/', $row->header );
|
| 255 | + if ( $urlParts[0] != 'DB:' ) {
|
| 256 | + $this->error( 'unfixable', "Error: unrecognised external storage type \"{$urlParts[0]}", $row->old_id );
|
| 257 | + } else {
|
| 258 | + $cluster = $urlParts[2];
|
| 259 | + $id = $urlParts[3];
|
| 260 | + if ( !isset( $externalConcatBlobs[$cluster][$id] ) ) {
|
| 261 | + $externalConcatBlobs[$cluster][$id] = array();
|
| 262 | + }
|
| 263 | + $externalConcatBlobs[$cluster][$id] = array_merge(
|
| 264 | + $externalConcatBlobs[$cluster][$id], $concatBlobs[$row->old_id]
|
| 265 | + );
|
| 266 | + }
|
| 267 | + } else {
|
| 268 | + $this->error( 'unfixable', "Error: invalid flags \"{$row->old_flags}\" on concat bulk row {$row->old_id}",
|
| 269 | + $concatBlobs[$row->old_id] );
|
| 270 | + }
|
| 271 | + } elseif ( strcasecmp( substr( $row->header, 0, strlen( CONCAT_HEADER ) ), CONCAT_HEADER ) ) {
|
| 272 | + $this->error( 'restore text', "Error: Incorrect object header for concat bulk row {$row->old_id}",
|
| 273 | + $concatBlobs[$row->old_id] );
|
| 274 | + } # else good
|
| 275 | +
|
| 276 | + unset( $concatBlobs[$row->old_id] );
|
| 277 | + }
|
| 278 | + $dbr->freeResult( $res );
|
| 279 | + }
|
| 280 | +
|
| 281 | + // Check targets of unresolved stubs
|
| 282 | + $this->checkExternalConcatBlobs( $externalConcatBlobs );
|
| 283 | +
|
| 284 | + // next chunk
|
| 285 | + }
|
| 286 | +
|
| 287 | + print "\n\nErrors:\n";
|
| 288 | + foreach( $this->errors as $name => $errors ) {
|
| 289 | + if ( count( $errors ) ) {
|
| 290 | + $description = $this->errorDescriptions[$name];
|
| 291 | + echo "$description: " . implode( ',', array_keys( $errors ) ) . "\n";
|
| 292 | + }
|
| 293 | + }
|
| 294 | +
|
| 295 | + if ( count( $this->errors['restore text'] ) && $fix ) {
|
| 296 | + if ( (string)$xml !== '' ) {
|
| 297 | + $this->restoreText( array_keys( $this->errors['restore text'] ), $xml );
|
| 298 | + } else {
|
| 299 | + echo "Can't fix text, no XML backup specified\n";
|
| 300 | + }
|
| 301 | + }
|
| 302 | +
|
| 303 | + print "\nFlag statistics:\n";
|
| 304 | + $total = array_sum( $flagStats );
|
| 305 | + foreach ( $flagStats as $flag => $count ) {
|
| 306 | + printf( "%-30s %10d %5.2f%%\n", $flag, $count, $count / $total * 100 );
|
| 307 | + }
|
| 308 | + print "\nLocal object statistics:\n";
|
| 309 | + $total = array_sum( $objectStats );
|
| 310 | + foreach ( $objectStats as $className => $count ) {
|
| 311 | + printf( "%-30s %10d %5.2f%%\n", $className, $count, $count / $total * 100 );
|
| 312 | + }
|
| 313 | + }
|
| 314 | +
|
| 315 | +
|
| 316 | + function error( $type, $msg, $ids ) {
|
| 317 | + if ( is_array( $ids ) && count( $ids ) == 1 ) {
|
| 318 | + $ids = reset( $ids );
|
| 319 | + }
|
| 320 | + if ( is_array( $ids ) ) {
|
| 321 | + $revIds = array();
|
| 322 | + foreach ( $ids as $id ) {
|
| 323 | + $revIds = array_merge( $revIds, array_keys( $this->oldIdMap, $id ) );
|
| 324 | + }
|
| 325 | + print "$msg in text rows " . implode( ', ', $ids ) .
|
| 326 | + ", revisions " . implode( ', ', $revIds ) . "\n";
|
| 327 | + } else {
|
| 328 | + $id = $ids;
|
| 329 | + $revIds = array_keys( $this->oldIdMap, $id );
|
| 330 | + if ( count( $revIds ) == 1 ) {
|
| 331 | + print "$msg in old_id $id, rev_id {$revIds[0]}\n";
|
| 332 | + } else {
|
| 333 | + print "$msg in old_id $id, revisions " . implode( ', ', $revIds ) . "\n";
|
| 334 | + }
|
| 335 | + }
|
| 336 | + $this->errors[$type] = $this->errors[$type] + array_flip( $revIds );
|
| 337 | + }
|
| 338 | +
|
| 339 | + function checkExternalConcatBlobs( $externalConcatBlobs ) {
|
| 340 | + $fname = 'CheckStorage::checkExternalConcatBlobs';
|
| 341 | + if ( !count( $externalConcatBlobs ) ) {
|
| 342 | + return;
|
| 343 | + }
|
| 344 | +
|
| 345 | + if ( is_null( $this->dbStore ) ) {
|
| 346 | + $this->dbStore = new ExternalStoreDB;
|
| 347 | + }
|
| 348 | +
|
| 349 | + foreach ( $externalConcatBlobs as $cluster => $oldIds ) {
|
| 350 | + $blobIds = array_keys( $oldIds );
|
| 351 | + $extDb =& $this->dbStore->getSlave( $cluster );
|
| 352 | + $blobsTable = $this->dbStore->getTable( $extDb );
|
| 353 | + $headerLength = strlen( CONCAT_HEADER );
|
| 354 | + $res = $extDb->select( $blobsTable,
|
| 355 | + array( 'blob_id', "LEFT(blob_text, $headerLength) AS header" ),
|
| 356 | + array( 'blob_id IN( ' . implode( ',', $blobIds ) . ')' ), $fname );
|
| 357 | + while ( $row = $extDb->fetchObject( $res ) ) {
|
| 358 | + if ( strcasecmp( $row->header, CONCAT_HEADER ) ) {
|
| 359 | + $this->error( 'restore text', "Error: invalid header on target $cluster/{$row->blob_id} of two-part ES URL",
|
| 360 | + $oldIds[$row->blob_id] );
|
| 361 | + }
|
| 362 | + unset( $oldIds[$row->blob_id] );
|
| 363 | +
|
| 364 | + }
|
| 365 | + $extDb->freeResult( $res );
|
| 366 | +
|
| 367 | + // Print errors for missing blobs rows
|
| 368 | + foreach ( $oldIds as $blobId => $oldIds ) {
|
| 369 | + $this->error( 'restore text', "Error: missing target $cluster/$blobId for two-part ES URL", $oldIds );
|
| 370 | + }
|
| 371 | + }
|
| 372 | + }
|
| 373 | +
|
| 374 | + function restoreText( $revIds, $xml ) {
|
| 375 | + global $wgTmpDirectory, $wgDBname;
|
| 376 | +
|
| 377 | + if ( !count( $revIds ) ) {
|
| 378 | + return;
|
| 379 | + }
|
| 380 | +
|
| 381 | + print "Restoring text from XML backup...\n";
|
| 382 | +
|
| 383 | + $revFileName = "$wgTmpDirectory/broken-revlist-$wgDBname";
|
| 384 | + $filteredXmlFileName = "$wgTmpDirectory/filtered-$wgDBname.xml";
|
| 385 | +
|
| 386 | + // Write revision list
|
| 387 | + if ( !file_put_contents( $revFileName, implode( "\n", $revIds ) ) ) {
|
| 388 | + echo "Error writing revision list, can't restore text\n";
|
| 389 | + return;
|
| 390 | + }
|
| 391 | +
|
| 392 | + // Run mwdumper
|
| 393 | + echo "Filtering XML dump...\n";
|
| 394 | + $exitStatus = 0;
|
| 395 | + passthru( 'mwdumper ' .
|
| 396 | + wfEscapeShellArg(
|
| 397 | + "--output=file:$filteredXmlFileName",
|
| 398 | + "--filter=revlist:$revFileName",
|
| 399 | + $xml
|
| 400 | + ), $exitStatus
|
| 401 | + );
|
| 402 | +
|
| 403 | + if ( $exitStatus ) {
|
| 404 | + echo "mwdumper died with exit status $exitStatus\n";
|
| 405 | + return;
|
| 406 | + }
|
| 407 | +
|
| 408 | + $file = fopen( $filteredXmlFileName, 'r' );
|
| 409 | + if ( !$file ) {
|
| 410 | + echo "Unable to open filtered XML file\n";
|
| 411 | + return;
|
| 412 | + }
|
| 413 | +
|
| 414 | + $dbr =& wfGetDB( DB_SLAVE );
|
| 415 | + $dbw =& wfGetDB( DB_MASTER );
|
| 416 | + $dbr->ping();
|
| 417 | + $dbw->ping();
|
| 418 | +
|
| 419 | + $source = new ImportStreamSource( $file );
|
| 420 | + $importer = new WikiImporter( $source );
|
| 421 | + $importer->setRevisionCallback( array( &$this, 'importRevision' ) );
|
| 422 | + $importer->doImport();
|
| 423 | + }
|
| 424 | +
|
| 425 | + function importRevision( &$revision, &$importer ) {
|
| 426 | + $fname = 'CheckStorage::importRevision';
|
| 427 | +
|
| 428 | + $id = $revision->getID();
|
| 429 | + $text = $revision->getText();
|
| 430 | + if ( $text === '' ) {
|
| 431 | + // This is what happens if the revision was broken at the time the
|
| 432 | + // dump was made. Unfortunately, it also happens if the revision was
|
| 433 | + // legitimately blank, so there's no way to tell the difference. To
|
| 434 | + // be safe, we'll skip it and leave it broken
|
| 435 | + $id = $id ? $id : '';
|
| 436 | + echo "Revision $id is blank in the dump, may have been broken before export\n";
|
| 437 | + return;
|
| 438 | + }
|
| 439 | +
|
| 440 | + if ( !$id ) {
|
| 441 | + // No ID, can't import
|
| 442 | + echo "No id tag in revision, can't import\n";
|
| 443 | + return;
|
| 444 | + }
|
| 445 | +
|
| 446 | + // Find text row again
|
| 447 | + $dbr =& wfGetDB( DB_SLAVE );
|
| 448 | + $oldId = $dbr->selectField( 'revision', 'rev_text_id', array( 'rev_id' => $id ), $fname );
|
| 449 | + if ( !$oldId ) {
|
| 450 | + echo "Missing revision row for rev_id $id\n";
|
| 451 | + return;
|
| 452 | + }
|
| 453 | +
|
| 454 | + // Compress the text
|
| 455 | + $flags = Revision::compressRevisionText( $text );
|
| 456 | +
|
| 457 | + // Update the text row
|
| 458 | + $dbw->update( 'text',
|
| 459 | + array( 'old_flags' => $flags, 'old_text' => $text ),
|
| 460 | + array( 'old_id' => $oldId ),
|
| 461 | + $fname, array( 'LIMIT' => 1 )
|
| 462 | + );
|
| 463 | +
|
| 464 | + // Remove it from the unfixed list and add it to the fixed list
|
| 465 | + unset( $this->errors['restore text'][$id] );
|
| 466 | + $this->errors['fixed'][$id] = true;
|
| 467 | + }
|
| 468 | +}
|
| 469 | +?>
|