Index: trunk/phase3/skins/monobook/main.css |
— | — | @@ -341,7 +341,8 @@ |
342 | 342 | */ |
343 | 343 | |
344 | 344 | #toc, |
345 | | -.toc { |
| 345 | +.toc, |
| 346 | +.mw-warning { |
346 | 347 | border: 1px solid #aaa; |
347 | 348 | background-color: #f9f9f9; |
348 | 349 | padding: 5px; |
— | — | @@ -378,6 +379,11 @@ |
379 | 380 | font-size: 94%; |
380 | 381 | } |
381 | 382 | |
| 383 | +.mw-warning { |
| 384 | + margin-left: 50px; |
| 385 | + margin-right: 50px; |
| 386 | + text-align: center; |
| 387 | +} |
382 | 388 | |
383 | 389 | /* images */ |
384 | 390 | div.floatright, table.floatright { |
Index: trunk/phase3/includes/XmlFunctions.php |
— | — | @@ -0,0 +1,273 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * Format an XML element with given attributes and, optionally, text content. |
| 6 | + * Element and attribute names are assumed to be ready for literal inclusion. |
| 7 | + * Strings are assumed to not contain XML-illegal characters; special |
| 8 | + * characters (<, >, &) are escaped but illegals are not touched. |
| 9 | + * |
| 10 | + * @param string $element |
| 11 | + * @param array $attribs Name=>value pairs. Values will be escaped. |
| 12 | + * @param string $contents NULL to make an open tag only; '' for a contentless closed tag (default) |
| 13 | + * @return string |
| 14 | + */ |
| 15 | +function wfElement( $element, $attribs = null, $contents = '') { |
| 16 | + $out = '<' . $element; |
| 17 | + if( !is_null( $attribs ) ) { |
| 18 | + foreach( $attribs as $name => $val ) { |
| 19 | + $out .= ' ' . $name . '="' . htmlspecialchars( $val ) . '"'; |
| 20 | + } |
| 21 | + } |
| 22 | + if( is_null( $contents ) ) { |
| 23 | + $out .= '>'; |
| 24 | + } else { |
| 25 | + if( $contents == '' ) { |
| 26 | + $out .= ' />'; |
| 27 | + } else { |
| 28 | + $out .= '>' . htmlspecialchars( $contents ) . "</$element>"; |
| 29 | + } |
| 30 | + } |
| 31 | + return $out; |
| 32 | +} |
| 33 | + |
| 34 | +/** |
| 35 | + * Format an XML element as with wfElement(), but run text through the |
| 36 | + * UtfNormal::cleanUp() validator first to ensure that no invalid UTF-8 |
| 37 | + * is passed. |
| 38 | + * |
| 39 | + * @param string $element |
| 40 | + * @param array $attribs Name=>value pairs. Values will be escaped. |
| 41 | + * @param string $contents NULL to make an open tag only; '' for a contentless closed tag (default) |
| 42 | + * @return string |
| 43 | + */ |
| 44 | +function wfElementClean( $element, $attribs = array(), $contents = '') { |
| 45 | + if( $attribs ) { |
| 46 | + $attribs = array_map( array( 'UtfNormal', 'cleanUp' ), $attribs ); |
| 47 | + } |
| 48 | + if( $contents ) { |
| 49 | + $contents = UtfNormal::cleanUp( $contents ); |
| 50 | + } |
| 51 | + return wfElement( $element, $attribs, $contents ); |
| 52 | +} |
| 53 | + |
| 54 | +/ Shortcuts |
| 55 | +function wfOpenElement( $element, $attribs = null ) { return wfElement( $element, $attribs, null ); } |
| 56 | +function wfCloseElement( $element ) { return "</$element>"; } |
| 57 | + |
| 58 | +/** |
| 59 | + * Create a namespace selector |
| 60 | + * |
| 61 | + * @param mixed $selected The namespace which should be selected, default '' |
| 62 | + * @param string $allnamespaces Value of a special item denoting all namespaces. Null to not include (default) |
| 63 | + * @param bool $includehidden Include hidden namespaces? |
| 64 | + * @return Html string containing the namespace selector |
| 65 | + */ |
| 66 | +function &HTMLnamespaceselector($selected = '', $allnamespaces = null, $includehidden=false) { |
| 67 | + global $wgContLang; |
| 68 | + if( $selected !== '' ) { |
| 69 | + if( is_null( $selected ) ) { |
| 70 | + / No namespace selected; let exact match work without hitting Main |
| 71 | + $selected = ''; |
| 72 | + } else { |
| 73 | + / Let input be numeric strings without breaking the empty match. |
| 74 | + $selected = intval( $selected ); |
| 75 | + } |
| 76 | + } |
| 77 | + $s = "<select name='namespace' class='namespaceselector'>\n\t"; |
| 78 | + $arr = $wgContLang->getFormattedNamespaces(); |
| 79 | + if( !is_null($allnamespaces) ) { |
| 80 | + $arr = array($allnamespaces => wfMsgHtml('namespacesall')) + $arr; |
| 81 | + } |
| 82 | + foreach ($arr as $index => $name) { |
| 83 | + if ($index < NS_MAIN) continue; |
| 84 | + |
| 85 | + $name = $index !== 0 ? $name : wfMsgHtml('blanknamespace'); |
| 86 | + |
| 87 | + if ($index === $selected) { |
| 88 | + $s .= wfElement("option", |
| 89 | + array("value" => $index, "selected" => "selected"), |
| 90 | + $name); |
| 91 | + } else { |
| 92 | + $s .= wfElement("option", array("value" => $index), $name); |
| 93 | + } |
| 94 | + } |
| 95 | + $s .= "\n</select>\n"; |
| 96 | + return $s; |
| 97 | +} |
| 98 | + |
| 99 | +function wfSpan( $text, $class, $attribs=array() ) { |
| 100 | + return wfElement( 'span', array( 'class' => $class ) + $attribs, $text ); |
| 101 | +} |
| 102 | + |
| 103 | +/** |
| 104 | + * Convenience function to build an HTML text input field |
| 105 | + * @return string HTML |
| 106 | + */ |
| 107 | +function wfInput( $name, $size=false, $value=false, $attribs=array() ) { |
| 108 | + return wfElement( 'input', array( |
| 109 | + 'name' => $name, |
| 110 | + 'size' => $size, |
| 111 | + 'value' => $value ) + $attribs ); |
| 112 | +} |
| 113 | + |
| 114 | +/** |
| 115 | + * Internal function for use in checkboxes and radio buttons and such. |
| 116 | + * @return array |
| 117 | + */ |
| 118 | +function wfAttrib( $name, $present = true ) { |
| 119 | + return $present ? array( $name => $name ) : array(); |
| 120 | +} |
| 121 | + |
| 122 | +/** |
| 123 | + * Convenience function to build an HTML checkbox |
| 124 | + * @return string HTML |
| 125 | + */ |
| 126 | +function wfCheck( $name, $checked=false, $attribs=array() ) { |
| 127 | + return wfElement( 'input', array( |
| 128 | + 'name' => $name, |
| 129 | + 'type' => 'checkbox', |
| 130 | + 'value' => 1 ) + wfAttrib( 'checked', $checked ) + $attribs ); |
| 131 | +} |
| 132 | + |
| 133 | +/** |
| 134 | + * Convenience function to build an HTML radio button |
| 135 | + * @return string HTML |
| 136 | + */ |
| 137 | +function wfRadio( $name, $value, $checked=false, $attribs=array() ) { |
| 138 | + return wfElement( 'input', array( |
| 139 | + 'name' => $name, |
| 140 | + 'type' => 'radio', |
| 141 | + 'value' => $value ) + wfAttrib( 'checked', $checked ) + $attribs ); |
| 142 | +} |
| 143 | + |
| 144 | +/** |
| 145 | + * Convenience function to build an HTML form label |
| 146 | + * @return string HTML |
| 147 | + */ |
| 148 | +function wfLabel( $label, $id ) { |
| 149 | + return wfElement( 'label', array( 'for' => $id ), $label ); |
| 150 | +} |
| 151 | + |
| 152 | +/** |
| 153 | + * Convenience function to build an HTML text input field with a label |
| 154 | + * @return string HTML |
| 155 | + */ |
| 156 | +function wfInputLabel( $label, $name, $id, $size=false, $value=false, $attribs=array() ) { |
| 157 | + return wfLabel( $label, $id ) . |
| 158 | + ' ' . |
| 159 | + wfInput( $name, $size, $value, array( 'id' => $id ) + $attribs ); |
| 160 | +} |
| 161 | + |
| 162 | +/** |
| 163 | + * Convenience function to build an HTML checkbox with a label |
| 164 | + * @return string HTML |
| 165 | + */ |
| 166 | +function wfCheckLabel( $label, $name, $id, $checked=false, $attribs=array() ) { |
| 167 | + return wfCheck( $name, $checked, array( 'id' => $id ) + $attribs ) . |
| 168 | + ' ' . |
| 169 | + wfLabel( $label, $id ); |
| 170 | +} |
| 171 | + |
| 172 | +/** |
| 173 | + * Convenience function to build an HTML radio button with a label |
| 174 | + * @return string HTML |
| 175 | + */ |
| 176 | +function wfRadioLabel( $label, $name, $value, $id, $checked=false, $attribs=array() ) { |
| 177 | + return wfRadio( $name, $checked, $value, array( 'id' => $id ) + $attribs ) . |
| 178 | + ' ' . |
| 179 | + wfLabel( $label, $id ); |
| 180 | +} |
| 181 | + |
| 182 | +/** |
| 183 | + * Convenience function to build an HTML submit button |
| 184 | + * @param string $value Label text for the button |
| 185 | + * @param array $attribs optional custom attributes |
| 186 | + * @return string HTML |
| 187 | + */ |
| 188 | +function wfSubmitButton( $value, $attribs=array() ) { |
| 189 | + return wfElement( 'input', array( 'type' => 'submit', 'value' => $value ) + $attribs ); |
| 190 | +} |
| 191 | + |
| 192 | +/** |
| 193 | + * Convenience function to build an HTML hidden form field |
| 194 | + * @param string $value Label text for the button |
| 195 | + * @param array $attribs optional custom attributes |
| 196 | + * @return string HTML |
| 197 | + */ |
| 198 | +function wfHidden( $name, $value, $attribs=array() ) { |
| 199 | + return wfElement( 'input', array( |
| 200 | + 'name' => $name, |
| 201 | + 'type' => 'hidden', |
| 202 | + 'value' => $value ) + $attribs ); |
| 203 | +} |
| 204 | + |
| 205 | +/** |
| 206 | + * Returns an escaped string suitable for inclusion in a string literal |
| 207 | + * for JavaScript source code. |
| 208 | + * Illegal control characters are assumed not to be present. |
| 209 | + * |
| 210 | + * @param string $string |
| 211 | + * @return string |
| 212 | + */ |
| 213 | +function wfEscapeJsString( $string ) { |
| 214 | + / See ECMA 262 section 7.8.4 for string literal format |
| 215 | + $pairs = array( |
| 216 | + "\\" => "\\\\", |
| 217 | + "\"" => "\\\"", |
| 218 | + '\'' => '\\\'', |
| 219 | + "\n" => "\\n", |
| 220 | + "\r" => "\\r", |
| 221 | + |
| 222 | + # To avoid closing the element or CDATA section |
| 223 | + "<" => "\\x3c", |
| 224 | + ">" => "\\x3e", |
| 225 | + ); |
| 226 | + return strtr( $string, $pairs ); |
| 227 | +} |
| 228 | + |
| 229 | +/** |
| 230 | + * Check if a string is well-formed XML. |
| 231 | + * Must include the surrounding tag. |
| 232 | + * |
| 233 | + * @param string $text |
| 234 | + * @return bool |
| 235 | + * |
| 236 | + * @todo Error position reporting return |
| 237 | + */ |
| 238 | +function wfIsWellFormedXml( $text ) { |
| 239 | + $parser = xml_parser_create( "UTF-8" ); |
| 240 | + |
| 241 | + # case folding violates XML standard, turn it off |
| 242 | + xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false ); |
| 243 | + |
| 244 | + if( !xml_parse( $parser, $text, true ) ) { |
| 245 | + $err = xml_error_string( xml_get_error_code( $parser ) ); |
| 246 | + $position = xml_get_current_byte_index( $parser ); |
| 247 | + /$fragment = $this->extractFragment( $html, $position ); |
| 248 | + /$this->mXmlError = "$err at byte $position:\n$fragment"; |
| 249 | + xml_parser_free( $parser ); |
| 250 | + return false; |
| 251 | + } |
| 252 | + xml_parser_free( $parser ); |
| 253 | + return true; |
| 254 | +} |
| 255 | + |
| 256 | +/** |
| 257 | + * Check if a string is a well-formed XML fragment. |
| 258 | + * Wraps fragment in an <html> bit and doctype, so it can be a fragment |
| 259 | + * and can use HTML named entities. |
| 260 | + * |
| 261 | + * @param string $text |
| 262 | + * @return bool |
| 263 | + */ |
| 264 | +function wfIsWellFormedXmlFragment( $text ) { |
| 265 | + $html = |
| 266 | + Sanitizer::hackDocType() . |
| 267 | + '<html>' . |
| 268 | + $text . |
| 269 | + '</html>'; |
| 270 | + return wfIsWellFormedXml( $html ); |
| 271 | +} |
| 272 | + |
| 273 | + |
| 274 | +?> |
\ No newline at end of file |
Property changes on: trunk/phase3/includes/XmlFunctions.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 275 | + native |
Added: svn:keywords |
2 | 276 | + Author Date Id Revision |
Index: trunk/phase3/includes/Article.php |
— | — | @@ -495,7 +495,10 @@ |
496 | 496 | } |
497 | 497 | } |
498 | 498 | |
499 | | - $this->mContent = $revision->getText(); |
| 499 | + / FIXME: Horrible, horrible! This content-loading interface just plain sucks. |
| 500 | + / We should instead work with the Revision object when we need it... |
| 501 | + $this->mContent = $revision->userCan( MW_REV_DELETED_TEXT ) ? $revision->getRawText() : ""; |
| 502 | + /$this->mContent = $revision->getText(); |
500 | 503 | |
501 | 504 | $this->mUser = $revision->getUser(); |
502 | 505 | $this->mUserText = $revision->getUserText(); |
— | — | @@ -767,7 +770,7 @@ |
768 | 771 | wfProfileOut( $fname ); |
769 | 772 | return; |
770 | 773 | } |
771 | | - |
| 774 | + |
772 | 775 | if ( empty( $oldid ) && $this->checkTouched() ) { |
773 | 776 | $wgOut->setETag($parserCache->getETag($this, $wgUser)); |
774 | 777 | |
— | — | @@ -846,7 +849,18 @@ |
847 | 850 | |
848 | 851 | if ( !empty( $oldid ) ) { |
849 | 852 | $this->setOldSubtitle( isset($this->mOldId) ? $this->mOldId : $oldid ); |
850 | | - $wgOut->setRobotpolicy( 'noindex,follow' ); |
| 853 | + $wgOut->setRobotpolicy( 'noindex,nofollow' ); |
| 854 | + if( $this->mRevision->isDeleted( MW_REV_DELETED_TEXT ) ) { |
| 855 | + if( !$this->mRevision->userCan( MW_REV_DELETED_TEXT ) ) { |
| 856 | + $wgOut->addWikiText( wfMsg( 'rev-deleted-text-permission' ) ); |
| 857 | + $wgOut->setPageTitle( $this->mTitle->getPrefixedText() ); |
| 858 | + return; |
| 859 | + } else { |
| 860 | + $wgOut->addWikiText( wfMsg( 'rev-deleted-text-view' ) ); |
| 861 | + / and we are allowed to see... |
| 862 | + } |
| 863 | + } |
| 864 | + |
851 | 865 | } |
852 | 866 | } |
853 | 867 | if( !$outputDone ) { |
Index: trunk/phase3/includes/GlobalFunctions.php |
— | — | @@ -30,6 +30,7 @@ |
31 | 31 | require_once( 'UpdateClasses.php' ); |
32 | 32 | require_once( 'LogPage.php' ); |
33 | 33 | require_once( 'normal/UtfNormalUtil.php' ); |
| 34 | +require_once( 'XmlFunctions.php' ); |
34 | 35 | |
35 | 36 | /** |
36 | 37 | * Compatibility functions |
— | — | @@ -839,30 +840,7 @@ |
840 | 841 | return $out; |
841 | 842 | } |
842 | 843 | |
843 | | -/** |
844 | | - * Returns an escaped string suitable for inclusion in a string literal |
845 | | - * for JavaScript source code. |
846 | | - * Illegal control characters are assumed not to be present. |
847 | | - * |
848 | | - * @param string $string |
849 | | - * @return string |
850 | | - */ |
851 | | -function wfEscapeJsString( $string ) { |
852 | | - / See ECMA 262 section 7.8.4 for string literal format |
853 | | - $pairs = array( |
854 | | - "\\" => "\\\\", |
855 | | - "\"" => "\\\"", |
856 | | - '\'' => '\\\'', |
857 | | - "\n" => "\\n", |
858 | | - "\r" => "\\r", |
859 | 844 | |
860 | | - # To avoid closing the element or CDATA section |
861 | | - "<" => "\\x3c", |
862 | | - ">" => "\\x3e", |
863 | | - ); |
864 | | - return strtr( $string, $pairs ); |
865 | | -} |
866 | | - |
867 | 845 | /** |
868 | 846 | * @todo document |
869 | 847 | * @return float |
— | — | @@ -873,15 +851,6 @@ |
874 | 852 | } |
875 | 853 | |
876 | 854 | /** |
877 | | - * Changes the first character to an HTML entity |
878 | | - */ |
879 | | -function wfHtmlEscapeFirst( $text ) { |
880 | | - $ord = ord($text); |
881 | | - $newText = substr($text, 1); |
882 | | - return "&#$ord;$newText"; |
883 | | -} |
884 | | - |
885 | | -/** |
886 | 855 | * Sets dest to source and returns the original value of dest |
887 | 856 | * If source is NULL, it just returns the value, it doesn't set the variable |
888 | 857 | */ |
— | — | @@ -1453,100 +1422,6 @@ |
1454 | 1423 | return( $siteNotice ); |
1455 | 1424 | } |
1456 | 1425 | |
1457 | | -/** |
1458 | | - * Format an XML element with given attributes and, optionally, text content. |
1459 | | - * Element and attribute names are assumed to be ready for literal inclusion. |
1460 | | - * Strings are assumed to not contain XML-illegal characters; special |
1461 | | - * characters (<, >, &) are escaped but illegals are not touched. |
1462 | | - * |
1463 | | - * @param string $element |
1464 | | - * @param array $attribs Name=>value pairs. Values will be escaped. |
1465 | | - * @param string $contents NULL to make an open tag only; '' for a contentless closed tag (default) |
1466 | | - * @return string |
1467 | | - */ |
1468 | | -function wfElement( $element, $attribs = null, $contents = '') { |
1469 | | - $out = '<' . $element; |
1470 | | - if( !is_null( $attribs ) ) { |
1471 | | - foreach( $attribs as $name => $val ) { |
1472 | | - $out .= ' ' . $name . '="' . htmlspecialchars( $val ) . '"'; |
1473 | | - } |
1474 | | - } |
1475 | | - if( is_null( $contents ) ) { |
1476 | | - $out .= '>'; |
1477 | | - } else { |
1478 | | - if( $contents == '' ) { |
1479 | | - $out .= ' />'; |
1480 | | - } else { |
1481 | | - $out .= '>' . htmlspecialchars( $contents ) . "</$element>"; |
1482 | | - } |
1483 | | - } |
1484 | | - return $out; |
1485 | | -} |
1486 | | - |
1487 | | -/** |
1488 | | - * Format an XML element as with wfElement(), but run text through the |
1489 | | - * UtfNormal::cleanUp() validator first to ensure that no invalid UTF-8 |
1490 | | - * is passed. |
1491 | | - * |
1492 | | - * @param string $element |
1493 | | - * @param array $attribs Name=>value pairs. Values will be escaped. |
1494 | | - * @param string $contents NULL to make an open tag only; '' for a contentless closed tag (default) |
1495 | | - * @return string |
1496 | | - */ |
1497 | | -function wfElementClean( $element, $attribs = array(), $contents = '') { |
1498 | | - if( $attribs ) { |
1499 | | - $attribs = array_map( array( 'UtfNormal', 'cleanUp' ), $attribs ); |
1500 | | - } |
1501 | | - if( $contents ) { |
1502 | | - $contents = UtfNormal::cleanUp( $contents ); |
1503 | | - } |
1504 | | - return wfElement( $element, $attribs, $contents ); |
1505 | | -} |
1506 | | - |
1507 | | -/ Shortcuts |
1508 | | -function wfOpenElement( $element, $attribs = null ) { return wfElement( $element, $attribs, null ); } |
1509 | | -function wfCloseElement( $element ) { return "</$element>"; } |
1510 | | - |
1511 | | -/** |
1512 | | - * Create a namespace selector |
1513 | | - * |
1514 | | - * @param mixed $selected The namespace which should be selected, default '' |
1515 | | - * @param string $allnamespaces Value of a special item denoting all namespaces. Null to not include (default) |
1516 | | - * @return Html string containing the namespace selector |
1517 | | - */ |
1518 | | -function &HTMLnamespaceselector($selected = '', $allnamespaces = null) { |
1519 | | - global $wgContLang; |
1520 | | - if( $selected !== '' ) { |
1521 | | - if( is_null( $selected ) ) { |
1522 | | - / No namespace selected; let exact match work without hitting Main |
1523 | | - $selected = ''; |
1524 | | - } else { |
1525 | | - / Let input be numeric strings without breaking the empty match. |
1526 | | - $selected = intval( $selected ); |
1527 | | - } |
1528 | | - } |
1529 | | - $s = "<select id='namespace' name='namespace' class='namespaceselector'>\n\t"; |
1530 | | - $arr = $wgContLang->getFormattedNamespaces(); |
1531 | | - if( !is_null($allnamespaces) ) { |
1532 | | - $arr = array($allnamespaces => wfMsgHtml('namespacesall')) + $arr; |
1533 | | - } |
1534 | | - foreach ($arr as $index => $name) { |
1535 | | - if ($index < NS_MAIN) continue; |
1536 | | - |
1537 | | - $name = $index !== 0 ? $name : wfMsgHtml('blanknamespace'); |
1538 | | - |
1539 | | - if ($index === $selected) { |
1540 | | - $s .= wfElement("option", |
1541 | | - array("value" => $index, "selected" => "selected"), |
1542 | | - $name); |
1543 | | - } else { |
1544 | | - $s .= wfElement("option", array("value" => $index), $name); |
1545 | | - } |
1546 | | - } |
1547 | | - $s .= "\n</select>\n"; |
1548 | | - return $s; |
1549 | | -} |
1550 | | - |
1551 | 1426 | /** Global singleton instance of MimeMagic. This is initialized on demand, |
1552 | 1427 | * please always use the wfGetMimeMagic() function to get the instance. |
1553 | 1428 | * |
— | — | @@ -1727,50 +1602,6 @@ |
1728 | 1603 | } |
1729 | 1604 | |
1730 | 1605 | /** |
1731 | | - * Check if a string is well-formed XML. |
1732 | | - * Must include the surrounding tag. |
1733 | | - * |
1734 | | - * @param string $text |
1735 | | - * @return bool |
1736 | | - * |
1737 | | - * @todo Error position reporting return |
1738 | | - */ |
1739 | | -function wfIsWellFormedXml( $text ) { |
1740 | | - $parser = xml_parser_create( "UTF-8" ); |
1741 | | - |
1742 | | - # case folding violates XML standard, turn it off |
1743 | | - xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false ); |
1744 | | - |
1745 | | - if( !xml_parse( $parser, $text, true ) ) { |
1746 | | - $err = xml_error_string( xml_get_error_code( $parser ) ); |
1747 | | - $position = xml_get_current_byte_index( $parser ); |
1748 | | - /$fragment = $this->extractFragment( $html, $position ); |
1749 | | - /$this->mXmlError = "$err at byte $position:\n$fragment"; |
1750 | | - xml_parser_free( $parser ); |
1751 | | - return false; |
1752 | | - } |
1753 | | - xml_parser_free( $parser ); |
1754 | | - return true; |
1755 | | -} |
1756 | | - |
1757 | | -/** |
1758 | | - * Check if a string is a well-formed XML fragment. |
1759 | | - * Wraps fragment in an <html> bit and doctype, so it can be a fragment |
1760 | | - * and can use HTML named entities. |
1761 | | - * |
1762 | | - * @param string $text |
1763 | | - * @return bool |
1764 | | - */ |
1765 | | -function wfIsWellFormedXmlFragment( $text ) { |
1766 | | - $html = |
1767 | | - Sanitizer::hackDocType() . |
1768 | | - '<html>' . |
1769 | | - $text . |
1770 | | - '</html>'; |
1771 | | - return wfIsWellFormedXml( $html ); |
1772 | | -} |
1773 | | - |
1774 | | -/** |
1775 | 1606 | * shell_exec() with time and memory limits mirrored from the PHP configuration, |
1776 | 1607 | * if supported. |
1777 | 1608 | */ |
Index: trunk/phase3/includes/SpecialRevisiondelete.php |
— | — | @@ -0,0 +1,258 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * Not quite ready for production use yet; need to fix up the restricted mode, |
| 6 | + * and provide for preservation across delete/undelete of the page. |
| 7 | + * |
| 8 | + * To try this out, set up extra permissions something like: |
| 9 | + * $wgGroupPermissions['sysop']['deleterevision'] = true; |
| 10 | + * $wgGroupPermissions['bureaucrat']['hiderevision'] = true; |
| 11 | + */ |
| 12 | + |
| 13 | +function wfSpecialRevisiondelete( $par = null ) { |
| 14 | + global $wgOut, $wgRequest, $wgUser, $wgContLang; |
| 15 | + |
| 16 | + $target = $wgRequest->getVal( 'target' ); |
| 17 | + $oldid = $wgRequest->getInt( 'oldid' ); |
| 18 | + |
| 19 | + $sk = $wgUser->getSkin(); |
| 20 | + $page = Title::newFromUrl( $target ); |
| 21 | + |
| 22 | + if( is_null( $page ) ) { |
| 23 | + $wgOut->errorpage( 'notargettitle', 'notargettext' ); |
| 24 | + return; |
| 25 | + } |
| 26 | + |
| 27 | + $form = new RevisionDeleteForm( $wgRequest ); |
| 28 | + if( $wgRequest->wasPosted() ) { |
| 29 | + $form->submit( $wgRequest ); |
| 30 | + } else { |
| 31 | + $form->show( $wgRequest ); |
| 32 | + } |
| 33 | +} |
| 34 | + |
| 35 | +class RevisionDeleteForm { |
| 36 | + /** |
| 37 | + * @param Title $page |
| 38 | + * @param int $oldid |
| 39 | + */ |
| 40 | + function __construct( $request ) { |
| 41 | + global $wgUser; |
| 42 | + |
| 43 | + $target = $request->getVal( 'target' ); |
| 44 | + $this->page = Title::newFromUrl( $target ); |
| 45 | + |
| 46 | + $this->revisions = $request->getIntArray( 'oldid', array() ); |
| 47 | + |
| 48 | + $this->skin = $wgUser->getSkin(); |
| 49 | + $this->checks = array( |
| 50 | + array( 'revdelete-hide-text', 'wpHideText', MW_REV_DELETED_TEXT ), |
| 51 | + array( 'revdelete-hide-comment', 'wpHideComment', MW_REV_DELETED_COMMENT ), |
| 52 | + array( 'revdelete-hide-user', 'wpHideUser', MW_REV_DELETED_USER ), |
| 53 | + array( 'revdelete-hide-restricted', 'wpHideRestricted', MW_REV_DELETED_RESTRICTED ) ); |
| 54 | + } |
| 55 | + |
| 56 | + /** |
| 57 | + * @param WebRequest $request |
| 58 | + */ |
| 59 | + function show( $request ) { |
| 60 | + global $wgOut, $wgUser; |
| 61 | + |
| 62 | + $first = $this->revisions[0]; |
| 63 | + |
| 64 | + $wgOut->addWikiText( wfMsg( 'revdelete-selected', $this->page->getPrefixedText() ) ); |
| 65 | + |
| 66 | + $wgOut->addHtml( "<ul>" ); |
| 67 | + foreach( $this->revisions as $revid ) { |
| 68 | + $rev = Revision::newFromTitle( $this->page, $revid ); |
| 69 | + $wgOut->addHtml( $this->historyLine( $rev ) ); |
| 70 | + $bitfields[] = $rev->mDeleted; / FIXME |
| 71 | + } |
| 72 | + $wgOut->addHtml( "</ul>" ); |
| 73 | + |
| 74 | + $wgOut->addWikiText( wfMsg( 'revdelete-text' ) ); |
| 75 | + |
| 76 | + $items = array( |
| 77 | + wfInputLabel( wfMsg( 'revdelete-log' ), 'wpReason', 'wpReason', 60 ), |
| 78 | + wfSubmitButton( wfMsg( 'revdelete-submit' ) ) ); |
| 79 | + $hidden = array( |
| 80 | + wfHidden( 'wpEditToken', $wgUser->editToken() ), |
| 81 | + wfHidden( 'target', $this->page->getPrefixedText() ) ); |
| 82 | + foreach( $this->revisions as $revid ) { |
| 83 | + $hidden[] = wfHidden( 'oldid[]', $revid ); |
| 84 | + } |
| 85 | + |
| 86 | + $special = Title::makeTitle( NS_SPECIAL, 'Revisiondelete' ); |
| 87 | + $wgOut->addHtml( wfElement( 'form', array( |
| 88 | + 'method' => 'post', |
| 89 | + 'action' => $special->getLocalUrl( 'action=submit' ) ) ) ); |
| 90 | + |
| 91 | + $wgOut->addHtml( '<fieldset><legend>' . wfMsgHtml( 'revdelete-legend' ) . '</legend>' ); |
| 92 | + foreach( $this->checks as $item ) { |
| 93 | + list( $message, $name, $field ) = $item; |
| 94 | + $wgOut->addHtml( '<div>' . |
| 95 | + wfCheckLabel( wfMsg( $message), $name, $name, $rev->isDeleted( $field ) ) . |
| 96 | + '</div>' ); |
| 97 | + } |
| 98 | + $wgOut->addHtml( '</fieldset>' ); |
| 99 | + foreach( $items as $item ) { |
| 100 | + $wgOut->addHtml( '<p>' . $item . '</p>' ); |
| 101 | + } |
| 102 | + foreach( $hidden as $item ) { |
| 103 | + $wgOut->addHtml( $item ); |
| 104 | + } |
| 105 | + |
| 106 | + $wgOut->addHtml( '</form>' ); |
| 107 | + } |
| 108 | + |
| 109 | + /** |
| 110 | + * @param Revision $rev |
| 111 | + * @returns string |
| 112 | + */ |
| 113 | + private function historyLine( $rev ) { |
| 114 | + global $wgContLang; |
| 115 | + $date = $wgContLang->timeanddate( $rev->getTimestamp() ); |
| 116 | + return |
| 117 | + "<li>" . |
| 118 | + $this->skin->makeLinkObj( $this->page, $date, 'oldid=' . $rev->getId() ) . |
| 119 | + " " . |
| 120 | + $this->skin->revUserLink( $rev ) . |
| 121 | + " " . |
| 122 | + $this->skin->revComment( $rev ) . |
| 123 | + "</li>"; |
| 124 | + } |
| 125 | + |
| 126 | + /** |
| 127 | + * @param WebRequest $request |
| 128 | + */ |
| 129 | + function submit( $request ) { |
| 130 | + $bitfield = $this->extractBitfield( $request ); |
| 131 | + $comment = $request->getText( 'wpReason' ); |
| 132 | + if( $this->save( $bitfield, $comment ) ) { |
| 133 | + return $this->success( $request ); |
| 134 | + } else { |
| 135 | + return $this->show( $request ); |
| 136 | + } |
| 137 | + } |
| 138 | + |
| 139 | + function success( $request ) { |
| 140 | + global $wgOut; |
| 141 | + $wgOut->addWikiText( 'woo' ); |
| 142 | + } |
| 143 | + |
| 144 | + /** |
| 145 | + * Put together a rev_deleted bitfield from the submitted checkboxes |
| 146 | + * @param WebRequest $request |
| 147 | + * @return int |
| 148 | + */ |
| 149 | + function extractBitfield( $request ) { |
| 150 | + $bitfield = 0; |
| 151 | + foreach( $this->checks as $item ) { |
| 152 | + list( $message, $name, $field ) = $item; |
| 153 | + if( $request->getCheck( $name ) ) { |
| 154 | + $bitfield |= $field; |
| 155 | + } |
| 156 | + } |
| 157 | + return $bitfield; |
| 158 | + } |
| 159 | + |
| 160 | + function save( $bitfield, $reason ) { |
| 161 | + $dbw = wfGetDB( DB_MASTER ); |
| 162 | + $deleter = new RevisionDeleter( $dbw ); |
| 163 | + $ok = $deleter->setVisibility( $this->revisions, $bitfield, $reason ); |
| 164 | + } |
| 165 | +} |
| 166 | + |
| 167 | + |
| 168 | +class RevisionDeleter { |
| 169 | + function __construct( $db ) { |
| 170 | + $this->db = $db; |
| 171 | + } |
| 172 | + |
| 173 | + /** |
| 174 | + * @param array $items list of revision ID numbers |
| 175 | + * @param int $bitfield new rev_deleted value |
| 176 | + * @param string $comment Comment for log records |
| 177 | + */ |
| 178 | + function setVisibility( $items, $bitfield, $comment ) { |
| 179 | + $pages = array(); |
| 180 | + |
| 181 | + / To work! |
| 182 | + foreach( $items as $revid ) { |
| 183 | + $rev = Revision::newFromId( $revid ); |
| 184 | + $this->updateRevision( $rev, $bitfield ); |
| 185 | + $this->updateRecentChanges( $rev, $bitfield ); |
| 186 | + |
| 187 | + / For logging, maintain a count of revisions per page |
| 188 | + $pageid = $rev->getPage(); |
| 189 | + if( isset( $pages[$pageid] ) ) { |
| 190 | + $pages[$pageid]++; |
| 191 | + } else { |
| 192 | + $pages[$pageid] = 1; |
| 193 | + } |
| 194 | + } |
| 195 | + |
| 196 | + / Clear caches... |
| 197 | + foreach( $pages as $pageid => $count ) { |
| 198 | + $title = Title::newFromId( $pageid ); |
| 199 | + $this->updatePage( $title ); |
| 200 | + $this->updateLog( $title, $count, $bitfield, $comment ); |
| 201 | + } |
| 202 | + |
| 203 | + return true; |
| 204 | + } |
| 205 | + |
| 206 | + /** |
| 207 | + * Update the revision's rev_deleted field |
| 208 | + * @param Revision $rev |
| 209 | + * @param int $bitfield new rev_deleted bitfield value |
| 210 | + */ |
| 211 | + function updateRevision( $rev, $bitfield ) { |
| 212 | + $this->db->update( 'revision', |
| 213 | + array( 'rev_deleted' => $bitfield ), |
| 214 | + array( 'rev_id' => $rev->getId() ), |
| 215 | + 'RevisionDeleter::updateRevision' ); |
| 216 | + } |
| 217 | + |
| 218 | + /** |
| 219 | + * Update the revision's recentchanges record if fields have been hidden |
| 220 | + * @param Revision $rev |
| 221 | + * @param int $bitfield new rev_deleted bitfield value |
| 222 | + */ |
| 223 | + function updateRecentChanges( $rev, $bitfield ) { |
| 224 | + $this->db->update( 'recentchanges', |
| 225 | + array( |
| 226 | + 'rc_user' => ($bitfield & MW_REV_DELETED_USER) ? 0 : $rev->getUser(), |
| 227 | + 'rc_user_text' => ($bitfield & MW_REV_DELETED_USER) ? wfMsg( 'rev-deleted-user' ) : $rev->getUserText(), |
| 228 | + 'rc_comment' => ($bitfield & MW_REV_DELETED_COMMENT) ? wfMsg( 'rev-deleted-comment' ) : $rev->getComment() ), |
| 229 | + array( |
| 230 | + 'rc_this_oldid' => $rev->getId() ), |
| 231 | + 'RevisionDeleter::updateRecentChanges' ); |
| 232 | + } |
| 233 | + |
| 234 | + /** |
| 235 | + * Touch the page's cache invalidation timestamp; this forces cached |
| 236 | + * history views to refresh, so any newly hidden or shown fields will |
| 237 | + * update properly. |
| 238 | + * @param Title $title |
| 239 | + */ |
| 240 | + function updatePage( $title ) { |
| 241 | + $title->invalidateCache(); |
| 242 | + } |
| 243 | + |
| 244 | + /** |
| 245 | + * Record a log entry on the action |
| 246 | + * @param Title $title |
| 247 | + * @param int $count the number of revisions altered for this page |
| 248 | + * @param int $bitfield the new rev_deleted value |
| 249 | + * @param string $comment |
| 250 | + */ |
| 251 | + function updateLog( $title, $count, $bitfield, $comment ) { |
| 252 | + $log = new LogPage( 'delete' ); |
| 253 | + $reason = "changed $count revisions to $bitfield"; |
| 254 | + $reason .= ": $comment"; |
| 255 | + $log->addEntry( 'revision', $title, $reason ); |
| 256 | + } |
| 257 | +} |
| 258 | + |
| 259 | +?> |
Property changes on: trunk/phase3/includes/SpecialRevisiondelete.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 260 | + native |
Added: svn:keywords |
2 | 261 | + Author Date Id Revision |
Index: trunk/phase3/includes/Linker.php |
— | — | @@ -737,6 +737,115 @@ |
738 | 738 | } |
739 | 739 | |
740 | 740 | /** |
| 741 | + * Make user link (or user contributions for unregistered users) |
| 742 | + * @param int $userId |
| 743 | + * @param string $userText |
| 744 | + * @return string HTML fragment |
| 745 | + * @access private |
| 746 | + */ |
| 747 | + function userLink( $userId, $userText ) { |
| 748 | + $encName = htmlspecialchars( $userText ); |
| 749 | + if( $userId == 0 ) { |
| 750 | + $contribsPage = Title::makeTitle( NS_SPECIAL, 'Contributions' ); |
| 751 | + return $this->makeKnownLinkObj( $contribsPage, |
| 752 | + $encName, 'target=' . urlencode( $userText ) ); |
| 753 | + } else { |
| 754 | + $userPage = Title::makeTitle( NS_USER, $userText ); |
| 755 | + return $this->makeLinkObj( $userPage, $encName ); |
| 756 | + } |
| 757 | + } |
| 758 | + |
| 759 | + /** |
| 760 | + * @param int $userId |
| 761 | + * @param string $userText |
| 762 | + * @return string HTML fragment with talk and/or block links |
| 763 | + * @access private |
| 764 | + */ |
| 765 | + function userToolLinks( $userId, $userText ) { |
| 766 | + global $wgUser, $wgDisableAnonTalk, $wgSysopUserBans; |
| 767 | + $talkable = !( $wgDisableAnonTalk && 0 == $userId ); |
| 768 | + $blockable = ( $wgSysopUserBans || 0 == $userId ); |
| 769 | + |
| 770 | + $items = array(); |
| 771 | + if( $talkable ) { |
| 772 | + $items[] = $this->userTalkLink( $userId, $userText ); |
| 773 | + } |
| 774 | + if( $blockable && $wgUser->isAllowed( 'block' ) ) { |
| 775 | + $items[] = $this->blockLink( $userId, $userText ); |
| 776 | + } |
| 777 | + |
| 778 | + if( $items ) { |
| 779 | + return ' (' . implode( ' | ', $items ) . ')'; |
| 780 | + } else { |
| 781 | + return ''; |
| 782 | + } |
| 783 | + } |
| 784 | + |
| 785 | + /** |
| 786 | + * @param int $userId |
| 787 | + * @param string $userText |
| 788 | + * @return string HTML fragment with user talk link |
| 789 | + * @access private |
| 790 | + */ |
| 791 | + function userTalkLink( $userId, $userText ) { |
| 792 | + global $wgContLang; |
| 793 | + $talkname = $wgContLang->getNsText( NS_TALK ); # use the shorter name |
| 794 | + |
| 795 | + $userTalkPage = Title::makeTitle( NS_USER_TALK, $userText ); |
| 796 | + $userTalkLink = $this->makeLinkObj( $userTalkPage, $talkname ); |
| 797 | + return $userTalkLink; |
| 798 | + } |
| 799 | + |
| 800 | + /** |
| 801 | + * @param int $userId |
| 802 | + * @param string $userText |
| 803 | + * @return string HTML fragment with block link |
| 804 | + * @access private |
| 805 | + */ |
| 806 | + function blockLink( $userId, $userText ) { |
| 807 | + $blockPage = Title::makeTitle( NS_SPECIAL, 'Blockip' ); |
| 808 | + $blockLink = $this->makeKnownLinkObj( $blockPage, |
| 809 | + wfMsgHtml( 'blocklink' ), 'ip=' . urlencode( $userText ) ); |
| 810 | + return $blockLink; |
| 811 | + } |
| 812 | + |
| 813 | + /** |
| 814 | + * Generate a user link if the current user is allowed to view it |
| 815 | + * @param Revision $rev |
| 816 | + * @return string HTML |
| 817 | + */ |
| 818 | + function revUserLink( $rev ) { |
| 819 | + if( $rev->userCan( MW_REV_DELETED_USER ) ) { |
| 820 | + $link = $this->userLink( $rev->getRawUser(), $rev->getRawUserText() ); |
| 821 | + } else { |
| 822 | + $link = wfMsgHtml( 'rev-deleted-user' ); |
| 823 | + } |
| 824 | + if( $rev->isDeleted( MW_REV_DELETED_USER ) ) { |
| 825 | + return '<span class="history-deleted">' . $link . '</span>'; |
| 826 | + } |
| 827 | + return $link; |
| 828 | + } |
| 829 | + |
| 830 | + /** |
| 831 | + * Generate a user tool link cluster if the current user is allowed to view it |
| 832 | + * @param Revision $rev |
| 833 | + * @return string HTML |
| 834 | + */ |
| 835 | + function revUserTools( $rev ) { |
| 836 | + if( $rev->userCan( MW_REV_DELETED_USER ) ) { |
| 837 | + $link = $this->userLink( $rev->getRawUser(), $rev->getRawUserText() ) . |
| 838 | + ' ' . |
| 839 | + $this->userToolLinks( $rev->getRawUser(), $rev->getRawUserText() ); |
| 840 | + } else { |
| 841 | + $link = wfMsgHtml( 'rev-deleted-user' ); |
| 842 | + } |
| 843 | + if( $rev->isDeleted( MW_REV_DELETED_USER ) ) { |
| 844 | + return '<span class="history-deleted">' . $link . '</span>'; |
| 845 | + } |
| 846 | + return $link; |
| 847 | + } |
| 848 | + |
| 849 | + /** |
741 | 850 | * This function is called by all recent changes variants, by the page history, |
742 | 851 | * and by the user contributions list. It is responsible for formatting edit |
743 | 852 | * comments. It escapes any HTML in the comment, but adds some CSS to format |
— | — | @@ -823,25 +932,39 @@ |
824 | 933 | * |
825 | 934 | * @param string $comment |
826 | 935 | * @param Title $title |
827 | | - * @param bool $deleted |
828 | 936 | * |
829 | 937 | * @return string |
830 | 938 | */ |
831 | | - function commentBlock( $comment, $title = NULL, $deleted = false ) { |
| 939 | + function commentBlock( $comment, $title = NULL ) { |
832 | 940 | / '*' used to be the comment inserted by the software way back |
833 | 941 | / in antiquity in case none was provided, here for backwards |
834 | 942 | / compatability, acc. to brion -ævar |
835 | 943 | if( $comment == '' || $comment == '*' ) { |
836 | 944 | return ''; |
837 | 945 | } else { |
838 | | - if ( $deleted ) |
839 | | - return " <span class='comment'>(...)</span>"; |
840 | | - else { |
841 | | - $formatted = $this->formatComment( $comment, $title ); |
842 | | - return " <span class='comment'>($formatted)</span>"; |
843 | | - } |
| 946 | + $formatted = $this->formatComment( $comment, $title ); |
| 947 | + return " <span class='comment'>($formatted)</span>"; |
844 | 948 | } |
845 | 949 | } |
| 950 | + |
| 951 | + /** |
| 952 | + * Wrap and format the given revision's comment block, if the current |
| 953 | + * user is allowed to view it. |
| 954 | + * @param Revision $rev |
| 955 | + * @return string HTML |
| 956 | + */ |
| 957 | + function revComment( $rev ) { |
| 958 | + if( $rev->userCan( MW_REV_DELETED_COMMENT ) ) { |
| 959 | + $block = $this->commentBlock( $rev->getRawComment(), $rev->getTitle() ); |
| 960 | + } else { |
| 961 | + $block = " <span class='comment'>" . |
| 962 | + wfMsgHtml( 'rev-deleted-comment' ) . "</span>"; |
| 963 | + } |
| 964 | + if( $rev->isDeleted( MW_REV_DELETED_COMMENT ) ) { |
| 965 | + return " <span class='history-deleted'>$block</span>"; |
| 966 | + } |
| 967 | + return $block; |
| 968 | + } |
846 | 969 | |
847 | 970 | /** @todo document */ |
848 | 971 | function tocIndent() { |
Index: trunk/phase3/includes/SpecialContributions.php |
— | — | @@ -128,7 +128,7 @@ |
129 | 129 | $use_index = $this->dbr->useIndexClause($index); |
130 | 130 | $sql = "SELECT |
131 | 131 | page_namespace,page_title,page_is_new,page_latest, |
132 | | - rev_id,rev_timestamp,rev_comment,rev_minor_edit,rev_user_text, |
| 132 | + rev_id,rev_page,rev_text_id,rev_timestamp,rev_comment,rev_minor_edit,rev_user,rev_user_text, |
133 | 133 | rev_deleted |
134 | 134 | FROM $page,$revision $use_index |
135 | 135 | WHERE page_id=rev_page AND $userCond $nscond $offsetQuery |
— | — | @@ -358,8 +358,10 @@ |
359 | 359 | } |
360 | 360 | } |
361 | 361 | |
362 | | - $page =& Title::makeTitle( $row->page_namespace, $row->page_title ); |
363 | | - $link = $sk->makeKnownLinkObj( $page, '' ); |
| 362 | + $rev = new Revision( $row ); |
| 363 | + |
| 364 | + $page = Title::makeTitle( $row->page_namespace, $row->page_title ); |
| 365 | + $link = $sk->makeKnownLinkObj( $page ); |
364 | 366 | $difftext = $topmarktext = ''; |
365 | 367 | if( $row->rev_id == $row->page_latest ) { |
366 | 368 | $topmarktext .= '<strong>' . $messages['uctop'] . '</strong>'; |
— | — | @@ -379,15 +381,19 @@ |
380 | 382 | } |
381 | 383 | |
382 | 384 | } |
383 | | - if( $row->rev_deleted && !$wgUser->isAllowed( 'delete' ) ) { |
| 385 | + if( $rev->userCan( MW_REV_DELETED_TEXT ) ) { |
| 386 | + $difftext = '(' . $sk->makeKnownLinkObj( $page, $messages['diff'], 'diff=prev&oldid='.$row->rev_id ) . ')'; |
| 387 | + } else { |
384 | 388 | $difftext = '(' . $messages['diff'] . ')'; |
385 | | - } else { |
386 | | - $difftext = '(' . $sk->makeKnownLinkObj( $page, $messages['diff'], 'diff=prev&oldid='.$row->rev_id ) . ')'; |
387 | 389 | } |
388 | 390 | $histlink='('.$sk->makeKnownLinkObj( $page, $messages['hist'], 'action=history' ) . ')'; |
389 | 391 | |
390 | | - $comment = $sk->commentBlock( $row->rev_comment, $page, (bool)$row->rev_deleted ); |
| 392 | + $comment = $sk->revComment( $rev ); |
391 | 393 | $d = $wgLang->timeanddate( wfTimestamp(TS_MW, $row->rev_timestamp), true ); |
| 394 | + |
| 395 | + if( $rev->isDeleted( MW_REV_DELETED_TEXT ) ) { |
| 396 | + $d = '<span class="history-deleted">' . $d . '</span>'; |
| 397 | + } |
392 | 398 | |
393 | 399 | if( $row->rev_minor_edit ) { |
394 | 400 | $mflag = '<span class="minor">' . $messages['minoreditletter'] . '</span> '; |
— | — | @@ -396,8 +402,8 @@ |
397 | 403 | } |
398 | 404 | |
399 | 405 | $ret = "{$d} {$histlink} {$difftext} {$mflag} {$link} {$comment} {$topmarktext}"; |
400 | | - if( $row->rev_deleted ) { |
401 | | - $ret = "<span class='deleted'>$ret</span>"; |
| 406 | + if( $rev->isDeleted( MW_REV_DELETED_TEXT ) ) { |
| 407 | + $ret .= ' ' . wfMsgHtml( 'deletedrev' ); |
402 | 408 | } |
403 | 409 | $ret = "<li>$ret</li>\n"; |
404 | 410 | wfProfileOut( $fname ); |
Index: trunk/phase3/includes/Export.php |
— | — | @@ -1,5 +1,5 @@ |
2 | 2 | <?php |
3 | | -# Copyright (C) 2003, 2005 Brion Vibber <[email protected]> |
| 3 | +# Copyright (C) 2003, 2005, 2006 Brion Vibber <[email protected]> |
4 | 4 | # http://www.mediawiki.org/ |
5 | 5 | # |
6 | 6 | # This program is free software; you can redistribute it and/or modify |
— | — | @@ -232,7 +232,7 @@ |
233 | 233 | * @return string |
234 | 234 | */ |
235 | 235 | function schemaVersion() { |
236 | | - return "0.3"; |
| 236 | + return "0.3"; / FIXME: upgrade to 0.4 when updated XSD is ready, for the revision deletion bits |
237 | 237 | } |
238 | 238 | |
239 | 239 | /** |
— | — | @@ -360,23 +360,31 @@ |
361 | 361 | $ts = wfTimestamp( TS_ISO_8601, $row->rev_timestamp ); |
362 | 362 | $out .= " " . wfElement( 'timestamp', null, $ts ) . "\n"; |
363 | 363 | |
364 | | - $out .= " <contributor>\n"; |
365 | | - if( $row->rev_user ) { |
366 | | - $out .= " " . wfElementClean( 'username', null, strval( $row->rev_user_text ) ) . "\n"; |
367 | | - $out .= " " . wfElement( 'id', null, strval( $row->rev_user ) ) . "\n"; |
| 364 | + if( $row->rev_deleted & MW_REV_DELETED_USER ) { |
| 365 | + $out .= " " . wfElement( 'contributor', array( 'deleted' => 'deleted' ) ) . "\n"; |
368 | 366 | } else { |
369 | | - $out .= " " . wfElementClean( 'ip', null, strval( $row->rev_user_text ) ) . "\n"; |
| 367 | + $out .= " <contributor>\n"; |
| 368 | + if( $row->rev_user ) { |
| 369 | + $out .= " " . wfElementClean( 'username', null, strval( $row->rev_user_text ) ) . "\n"; |
| 370 | + $out .= " " . wfElement( 'id', null, strval( $row->rev_user ) ) . "\n"; |
| 371 | + } else { |
| 372 | + $out .= " " . wfElementClean( 'ip', null, strval( $row->rev_user_text ) ) . "\n"; |
| 373 | + } |
| 374 | + $out .= " </contributor>\n"; |
370 | 375 | } |
371 | | - $out .= " </contributor>\n"; |
372 | 376 | |
373 | 377 | if( $row->rev_minor_edit ) { |
374 | 378 | $out .= " <minor/>\n"; |
375 | 379 | } |
376 | | - if( $row->rev_comment != '' ) { |
| 380 | + if( $row->rev_deleted & MW_REV_DELETED_COMMENT ) { |
| 381 | + $out .= " " . wfElement( 'comment', array( 'deleted' => 'deleted' ) ) . "\n"; |
| 382 | + } elseif( $row->rev_comment != '' ) { |
377 | 383 | $out .= " " . wfElementClean( 'comment', null, strval( $row->rev_comment ) ) . "\n"; |
378 | 384 | } |
379 | 385 | |
380 | | - if( isset( $row->old_text ) ) { |
| 386 | + if( $row->rev_deleted & MW_REV_DELETED_TEXT ) { |
| 387 | + $out .= " " . wfElement( 'text', array( 'deleted' => 'deleted' ) ) . "\n"; |
| 388 | + } elseif( isset( $row->old_text ) ) { |
381 | 389 | / Raw text from the database may have invalid chars |
382 | 390 | $text = strval( Revision::getRevisionText( $row ) ); |
383 | 391 | $out .= " " . wfElementClean( 'text', |
Index: trunk/phase3/includes/WebRequest.php |
— | — | @@ -162,6 +162,24 @@ |
163 | 163 | return (array)$val; |
164 | 164 | } |
165 | 165 | } |
| 166 | + |
| 167 | + /** |
| 168 | + * Fetch an array of integers, or return $default if it's not set. |
| 169 | + * If source was scalar, will return an array with a single element. |
| 170 | + * If no source and no default, returns NULL. |
| 171 | + * If an array is returned, contents are guaranteed to be integers. |
| 172 | + * |
| 173 | + * @param string $name |
| 174 | + * @param array $default option default (or NULL) |
| 175 | + * @return array of ints |
| 176 | + */ |
| 177 | + function getIntArray( $name, $default = NULL ) { |
| 178 | + $val = $this->getArray( $name, $default ); |
| 179 | + if( is_array( $val ) ) { |
| 180 | + $val = array_map( 'intval', $val ); |
| 181 | + } |
| 182 | + return $val; |
| 183 | + } |
166 | 184 | |
167 | 185 | /** |
168 | 186 | * Fetch an integer value from the input or return $default if not set. |
Index: trunk/phase3/includes/Revision.php |
— | — | @@ -8,6 +8,13 @@ |
9 | 9 | require_once( 'Database.php' ); |
10 | 10 | require_once( 'Article.php' ); |
11 | 11 | |
| 12 | +/** @+ */ |
| 13 | +define( 'MW_REV_DELETED_TEXT', 1 ); |
| 14 | +define( 'MW_REV_DELETED_COMMENT', 2 ); |
| 15 | +define( 'MW_REV_DELETED_USER', 4 ); |
| 16 | +define( 'MW_REV_DELETED_RESTRICTED', 8 ); |
| 17 | +/** @- */ |
| 18 | + |
12 | 19 | /** |
13 | 20 | * @package MediaWiki |
14 | 21 | * @todo document |
— | — | @@ -246,9 +253,14 @@ |
247 | 254 | $this->mTimestamp = $row->rev_timestamp; |
248 | 255 | $this->mDeleted = intval( $row->rev_deleted ); |
249 | 256 | |
250 | | - $this->mCurrent = ( $row->rev_id == $row->page_latest ); |
251 | | - $this->mTitle = Title::makeTitle( $row->page_namespace, |
252 | | - $row->page_title ); |
| 257 | + if( isset( $row->page_latest ) ) { |
| 258 | + $this->mCurrent = ( $row->rev_id == $row->page_latest ); |
| 259 | + $this->mTitle = Title::makeTitle( $row->page_namespace, |
| 260 | + $row->page_title ); |
| 261 | + } else { |
| 262 | + $this->mCurrent = false; |
| 263 | + $this->mTitle = null; |
| 264 | + } |
253 | 265 | |
254 | 266 | if( isset( $row->old_text ) ) { |
255 | 267 | $this->mText = $this->getRevisionText( $row ); |
— | — | @@ -327,23 +339,62 @@ |
328 | 340 | } |
329 | 341 | |
330 | 342 | /** |
| 343 | + * Fetch revision's user id if it's available to all users |
331 | 344 | * @return int |
332 | 345 | */ |
333 | 346 | function getUser() { |
| 347 | + if( $this->isDeleted( MW_REV_DELETED_USER ) ) { |
| 348 | + return 0; |
| 349 | + } else { |
| 350 | + return $this->mUser; |
| 351 | + } |
| 352 | + } |
| 353 | + |
| 354 | + /** |
| 355 | + * Fetch revision's user id without regard for the current user's permissions |
| 356 | + * @return string |
| 357 | + */ |
| 358 | + function getRawUser() { |
334 | 359 | return $this->mUser; |
335 | 360 | } |
336 | 361 | |
337 | 362 | /** |
| 363 | + * Fetch revision's username if it's available to all users |
338 | 364 | * @return string |
339 | 365 | */ |
340 | 366 | function getUserText() { |
| 367 | + if( $this->isDeleted( MW_REV_DELETED_USER ) ) { |
| 368 | + return ""; |
| 369 | + } else { |
| 370 | + return $this->mUserText; |
| 371 | + } |
| 372 | + } |
| 373 | + |
| 374 | + /** |
| 375 | + * Fetch revision's username without regard for view restrictions |
| 376 | + * @return string |
| 377 | + */ |
| 378 | + function getRawUserText() { |
341 | 379 | return $this->mUserText; |
342 | 380 | } |
| 381 | + |
| 382 | + /** |
| 383 | + * Fetch revision comment if it's available to all users |
| 384 | + * @return string |
| 385 | + */ |
| 386 | + function getComment() { |
| 387 | + if( $this->isDeleted( MW_REV_DELETED_COMMENT ) ) { |
| 388 | + return ""; |
| 389 | + } else { |
| 390 | + return $this->mComment; |
| 391 | + } |
| 392 | + } |
343 | 393 | |
344 | 394 | /** |
| 395 | + * Fetch revision comment without regard for the current user's permissions |
345 | 396 | * @return string |
346 | 397 | */ |
347 | | - function getComment() { |
| 398 | + function getRawComment() { |
348 | 399 | return $this->mComment; |
349 | 400 | } |
350 | 401 | |
— | — | @@ -355,16 +406,30 @@ |
356 | 407 | } |
357 | 408 | |
358 | 409 | /** |
| 410 | + * int $field one of MW_REV_DELETED_* bitfield constants |
359 | 411 | * @return bool |
360 | 412 | */ |
361 | | - function isDeleted() { |
362 | | - return (bool)$this->mDeleted; |
| 413 | + function isDeleted( $field ) { |
| 414 | + return ($this->mDeleted & $field) == $field; |
363 | 415 | } |
364 | 416 | |
365 | 417 | /** |
| 418 | + * Fetch revision text if it's available to all users |
366 | 419 | * @return string |
367 | 420 | */ |
368 | 421 | function getText() { |
| 422 | + if( $this->isDeleted( MW_REV_DELETED_TEXT ) ) { |
| 423 | + return ""; |
| 424 | + } else { |
| 425 | + return $this->getRawText(); |
| 426 | + } |
| 427 | + } |
| 428 | + |
| 429 | + /** |
| 430 | + * Fetch revision text without regard for view restrictions |
| 431 | + * @return string |
| 432 | + */ |
| 433 | + function getRawText() { |
369 | 434 | if( is_null( $this->mText ) ) { |
370 | 435 | / Revision text is immutable. Load on demand: |
371 | 436 | $this->mText = $this->loadText(); |
— | — | @@ -650,6 +715,28 @@ |
651 | 716 | wfProfileOut( $fname ); |
652 | 717 | return $revision; |
653 | 718 | } |
| 719 | + |
| 720 | + /** |
| 721 | + * Determine if the current user is allowed to view a particular |
| 722 | + * field of this revision, if it's marked as deleted. |
| 723 | + * @param int $field one of MW_REV_DELETED_TEXT, |
| 724 | + * MW_REV_DELETED_COMMENT, |
| 725 | + * MW_REV_DELETED_USER |
| 726 | + * @return bool |
| 727 | + */ |
| 728 | + function userCan( $field ) { |
| 729 | + if( ( $this->mDeleted & $field ) == $field ) { |
| 730 | + global $wgUser; |
| 731 | + $permission = ( $this->mDeleted & MW_REV_DELETED_RESTRICTED ) == MW_REV_DELETED_RESTRICTED |
| 732 | + ? 'hiderevision' |
| 733 | + : 'deleterevision'; |
| 734 | + wfDebug( "Checking for $permission due to $field match on $this->mDeleted\n" ); |
| 735 | + return $wgUser->isAllowed( $permission ); |
| 736 | + } else { |
| 737 | + return true; |
| 738 | + } |
| 739 | + } |
654 | 740 | |
655 | 741 | } |
| 742 | + |
656 | 743 | ?> |
Index: trunk/phase3/includes/ChangesList.php |
— | — | @@ -181,8 +181,8 @@ |
182 | 182 | |
183 | 183 | /** Insert links to user page, user talk page and eventually a blocking link */ |
184 | 184 | function insertUserRelatedLinks(&$s, &$rc) { |
185 | | - $s .= $this->userLink( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] ); |
186 | | - $s .= $this->userToolLinks( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] ); |
| 185 | + $s .= $this->skin->userLink( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] ); |
| 186 | + $s .= $this->skin->userToolLinks( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] ); |
187 | 187 | } |
188 | 188 | |
189 | 189 | /** insert a formatted comment */ |
— | — | @@ -203,79 +203,7 @@ |
204 | 204 | ( !$wgOnlySysopsCanPatrol || $wgUser->isAllowed( 'patrol' ) ); |
205 | 205 | } |
206 | 206 | |
207 | | - /** |
208 | | - * Make user link (or user contributions for unregistered users) |
209 | | - * @param int $userId |
210 | | - * @param string $userText |
211 | | - * @return string HTML fragment |
212 | | - * @access private |
213 | | - */ |
214 | | - function userLink( $userId, $userText ) { |
215 | | - $encName = htmlspecialchars( $userText ); |
216 | | - if( $userId == 0 ) { |
217 | | - $contribsPage = Title::makeTitle( NS_SPECIAL, 'Contributions' ); |
218 | | - return $this->skin->makeKnownLinkObj( $contribsPage, |
219 | | - $encName, 'target=' . urlencode( $userText ) ); |
220 | | - } else { |
221 | | - $userPage = Title::makeTitle( NS_USER, $userText ); |
222 | | - return $this->skin->makeLinkObj( $userPage, $encName ); |
223 | | - } |
224 | | - } |
225 | 207 | |
226 | | - /** |
227 | | - * @param int $userId |
228 | | - * @param string $userText |
229 | | - * @return string HTML fragment with talk and/or block links |
230 | | - * @access private |
231 | | - */ |
232 | | - function userToolLinks( $userId, $userText ) { |
233 | | - global $wgUser, $wgDisableAnonTalk, $wgSysopUserBans; |
234 | | - $talkable = !( $wgDisableAnonTalk && 0 == $userId ); |
235 | | - $blockable = ( $wgSysopUserBans || 0 == $userId ); |
236 | | - |
237 | | - $items = array(); |
238 | | - if( $talkable ) { |
239 | | - $items[] = $this->userTalkLink( $userId, $userText ); |
240 | | - } |
241 | | - if( $blockable && $wgUser->isAllowed( 'block' ) ) { |
242 | | - $items[] = $this->blockLink( $userId, $userText ); |
243 | | - } |
244 | | - |
245 | | - if( $items ) { |
246 | | - return ' (' . implode( ' | ', $items ) . ')'; |
247 | | - } else { |
248 | | - return ''; |
249 | | - } |
250 | | - } |
251 | | - |
252 | | - /** |
253 | | - * @param int $userId |
254 | | - * @param string $userText |
255 | | - * @return string HTML fragment with user talk link |
256 | | - * @access private |
257 | | - */ |
258 | | - function userTalkLink( $userId, $userText ) { |
259 | | - global $wgContLang; |
260 | | - $talkname = $wgContLang->getNsText( NS_TALK ); # use the shorter name |
261 | | - |
262 | | - $userTalkPage = Title::makeTitle( NS_USER_TALK, $userText ); |
263 | | - $userTalkLink = $this->skin->makeLinkObj( $userTalkPage, $talkname ); |
264 | | - return $userTalkLink; |
265 | | - } |
266 | | - |
267 | | - /** |
268 | | - * @param int $userId |
269 | | - * @param string $userText |
270 | | - * @return string HTML fragment with block link |
271 | | - * @access private |
272 | | - */ |
273 | | - function blockLink( $userId, $userText ) { |
274 | | - $blockPage = Title::makeTitle( NS_SPECIAL, 'Blockip' ); |
275 | | - $blockLink = $this->skin->makeKnownLinkObj( $blockPage, |
276 | | - $this->message['blocklink'], 'ip=' . urlencode( $userText ) ); |
277 | | - return $blockLink; |
278 | | - } |
279 | | - |
280 | 208 | } |
281 | 209 | |
282 | 210 | |
— | — | @@ -429,13 +357,13 @@ |
430 | 358 | $curIdEq.'&diff='.$rc_this_oldid.'&oldid='.$rc_last_oldid . $rcIdQuery ); |
431 | 359 | } |
432 | 360 | |
433 | | - $rc->userlink = $this->userLink( $rc_user, $rc_user_text ); |
| 361 | + $rc->userlink = $this->skin->userLink( $rc_user, $rc_user_text ); |
434 | 362 | |
435 | 363 | $rc->lastlink = $lastLink; |
436 | 364 | $rc->curlink = $curLink; |
437 | 365 | $rc->difflink = $diffLink; |
438 | 366 | |
439 | | - $rc->usertalklink = $this->userToolLinks( $rc_user, $rc_user_text ); |
| 367 | + $rc->usertalklink = $this->skin->userToolLinks( $rc_user, $rc_user_text ); |
440 | 368 | |
441 | 369 | # Put accumulated information into the cache, for later display |
442 | 370 | # Page moves go on their own line |
Index: trunk/phase3/includes/DifferenceEngine.php |
— | — | @@ -24,8 +24,6 @@ |
25 | 25 | var $mOldid, $mNewid, $mTitle; |
26 | 26 | var $mOldtitle, $mNewtitle, $mPagetitle; |
27 | 27 | var $mOldtext, $mNewtext; |
28 | | - var $mOldUser, $mNewUser; |
29 | | - var $mOldComment, $mNewComment; |
30 | 28 | var $mOldPage, $mNewPage; |
31 | 29 | var $mRcidMarkPatrolled; |
32 | 30 | var $mOldRev, $mNewRev; |
— | — | @@ -153,21 +151,11 @@ |
154 | 152 | $talk = $wgContLang->getNsText( NS_TALK ); |
155 | 153 | $contribs = wfMsg( 'contribslink' ); |
156 | 154 | |
157 | | - $this->mOldComment = $sk->formatComment($this->mOldComment); |
158 | | - $this->mNewComment = $sk->formatComment($this->mNewComment); |
159 | | - |
160 | | - $oldUserLink = $sk->makeLinkObj( Title::makeTitleSafe( NS_USER, $this->mOldUser ), $this->mOldUser ); |
161 | | - $newUserLink = $sk->makeLinkObj( Title::makeTitleSafe( NS_USER, $this->mNewUser ), $this->mNewUser ); |
162 | | - $oldUTLink = $sk->makeLinkObj( Title::makeTitleSafe( NS_USER_TALK, $this->mOldUser ), $talk ); |
163 | | - $newUTLink = $sk->makeLinkObj( Title::makeTitleSafe( NS_USER_TALK, $this->mNewUser ), $talk ); |
164 | | - $oldContribs = $sk->makeKnownLinkObj( Title::makeTitle( NS_SPECIAL, 'Contributions' ), $contribs, |
165 | | - 'target=' . urlencode($this->mOldUser) ); |
166 | | - $newContribs = $sk->makeKnownLinkObj( Title::makeTitle( NS_SPECIAL, 'Contributions' ), $contribs, |
167 | | - 'target=' . urlencode($this->mNewUser) ); |
168 | 155 | if ( $this->mNewRev->isCurrent() && $wgUser->isAllowed('rollback') ) { |
| 156 | + $username = $this->mNewRev->getUserText(); |
169 | 157 | $rollback = ' <strong>[' . $sk->makeKnownLinkObj( $this->mTitle, wfMsg( 'rollbacklink' ), |
170 | | - 'action=rollback&from=' . urlencode($this->mNewUser) . |
171 | | - '&token=' . urlencode( $wgUser->editToken( array( $this->mTitle->getPrefixedText(), $this->mNewUser ) ) ) ) . |
| 158 | + 'action=rollback&from=' . urlencode( $username ) . |
| 159 | + '&token=' . urlencode( $wgUser->editToken( array( $this->mTitle->getPrefixedText(), $username ) ) ) ) . |
172 | 160 | ']</strong>'; |
173 | 161 | } else { |
174 | 162 | $rollback = ''; |
— | — | @@ -190,12 +178,14 @@ |
191 | 179 | 'diff=next&oldid='.$this->mNewid, '', '', 'id="differences-nextlink"' ); |
192 | 180 | } |
193 | 181 | |
194 | | - $oldHeader = "<strong>{$this->mOldtitle}</strong><br />$oldUserLink " . |
195 | | - "($oldUTLink | $oldContribs)<br />" . $this->mOldComment . |
196 | | - '<br />' . $prevlink; |
197 | | - $newHeader = "<strong>{$this->mNewtitle}</strong><br />$newUserLink " . |
198 | | - "($newUTLink | $newContribs) $rollback<br />" . $this->mNewComment . |
199 | | - '<br />' . $nextlink . $patrol; |
| 182 | + $oldHeader = "<strong>{$this->mOldtitle}</strong><br />" . |
| 183 | + $sk->revUserTools( $this->mOldRev ) . "<br />" . |
| 184 | + $sk->revComment( $this->mOldRev ) . "<br />" . |
| 185 | + $prevlink; |
| 186 | + $newHeader = "<strong>{$this->mNewtitle}</strong><br />" . |
| 187 | + $sk->revUserTools( $this->mNewRev ) . " $rollback<br />" . |
| 188 | + $sk->revComment( $this->mNewRev ) . "<br />" . |
| 189 | + $nextlink . $patrol; |
200 | 190 | |
201 | 191 | $this->showDiff( $oldHeader, $newHeader ); |
202 | 192 | $wgOut->addHTML( "<hr /><h2>{$this->mPagetitle}</h2>\n" ); |
— | — | @@ -255,19 +245,16 @@ |
256 | 246 | # |
257 | 247 | $sk = $wgUser->getSkin(); |
258 | 248 | |
259 | | - $uTLink = $sk->makeLinkObj( Title::makeTitleSafe( NS_USER_TALK, $this->mOldUser ), $wgLang->getNsText( NS_TALK ) ); |
260 | | - $userLink = $sk->makeLinkObj( Title::makeTitleSafe( NS_USER, $this->mOldUser ), $this->mOldUser ); |
261 | | - $contribs = $sk->makeKnownLinkObj( Title::makeTitle( NS_SPECIAL, 'Contributions' ), wfMsg( 'contribslink' ), |
262 | | - 'target=' . urlencode($this->mOldUser) ); |
263 | 249 | $nextlink = $sk->makeKnownLinkObj( $this->mTitle, wfMsgHtml( 'nextdiff' ), 'diff=next&oldid='.$this->mNewid, '', '', 'id="differences-nextlink"' ); |
264 | | - $header = "<div class=\"firstrevisionheader\" style=\"text-align: center\"><strong>{$this->mOldtitle}</strong><br />$userLink " . |
265 | | - "($uTLink | $contribs)<br />" . $this->mOldComment . |
266 | | - '<br />' . $nextlink. "</div>\n"; |
| 250 | + $header = "<div class=\"firstrevisionheader\" style=\"text-align: center\"><strong>{$this->mOldtitle}</strong><br />" . |
| 251 | + $sk->revUserTools( $this->mNewRev ) . "<br />" . |
| 252 | + $sk->revComment( $this->mNewRev ) . "<br />" . |
| 253 | + $nextlink . "</div>\n"; |
267 | 254 | |
268 | 255 | $wgOut->addHTML( $header ); |
269 | 256 | |
270 | 257 | $wgOut->setSubtitle( wfMsg( 'difference' ) ); |
271 | | - $wgOut->setRobotpolicy( 'noindex,follow' ); |
| 258 | + $wgOut->setRobotpolicy( 'noindex,nofollow' ); |
272 | 259 | |
273 | 260 | |
274 | 261 | # Show current revision |
— | — | @@ -289,7 +276,7 @@ |
290 | 277 | global $wgOut; |
291 | 278 | $diff = $this->getDiff( $otitle, $ntitle ); |
292 | 279 | if ( $diff === false ) { |
293 | | - $wgOut->addWikitext( wfMsg( 'missingarticle', "<nowiki>$t</nowiki>" ) ); |
| 280 | + $wgOut->addWikitext( wfMsg( 'missingarticle', "<nowiki>(fixme, bug)</nowiki>" ) ); |
294 | 281 | return false; |
295 | 282 | } else { |
296 | 283 | $wgOut->addHTML( $diff ); |
— | — | @@ -510,9 +497,6 @@ |
511 | 498 | $this->mNewtitle = "<a href='$newLink'>{$this->mPagetitle}</a>"; |
512 | 499 | } |
513 | 500 | |
514 | | - $this->mNewUser = $this->mNewRev->getUserText(); |
515 | | - $this->mNewComment = $this->mNewRev->getComment(); |
516 | | - |
517 | 501 | / Load the old revision object |
518 | 502 | $this->mOldRev = false; |
519 | 503 | if( $this->mOldid ) { |
— | — | @@ -539,10 +523,6 @@ |
540 | 524 | $t = $wgLang->timeanddate( $this->mOldRev->getTimestamp(), true ); |
541 | 525 | $oldLink = $this->mOldPage->escapeLocalUrl( 'oldid=' . $this->mOldid ); |
542 | 526 | $this->mOldtitle = "<a href='$oldLink'>" . htmlspecialchars( wfMsg( 'revisionasof', $t ) ) . '</a>'; |
543 | | - |
544 | | - |
545 | | - $this->mOldUser = $this->mOldRev->getUserText(); |
546 | | - $this->mOldComment = $this->mOldRev->getComment(); |
547 | 527 | } |
548 | 528 | |
549 | 529 | return true; |
— | — | @@ -563,6 +543,7 @@ |
564 | 544 | return false; |
565 | 545 | } |
566 | 546 | if ( $this->mOldRev ) { |
| 547 | + / FIXME: permission tests |
567 | 548 | $this->mOldtext = $this->mOldRev->getText(); |
568 | 549 | if ( $this->mOldtext === false ) { |
569 | 550 | return false; |
Index: trunk/phase3/includes/RawPage.php |
— | — | @@ -163,7 +163,7 @@ |
164 | 164 | if ( $rev ) { |
165 | 165 | $lastmod = wfTimestamp( TS_RFC2822, $rev->getTimestamp() ); |
166 | 166 | header( "Last-modified: $lastmod" ); |
167 | | - $text = $rev->isDeleted() ? '' : $rev->getText(); |
| 167 | + $text = $rev->getText(); |
168 | 168 | } else |
169 | 169 | $text = ''; |
170 | 170 | } |
Index: trunk/phase3/includes/DefaultSettings.php |
— | — | @@ -849,6 +849,10 @@ |
850 | 850 | / Permission to change users' group assignments |
851 | 851 | $wgGroupPermissions['bureaucrat']['userrights'] = true; |
852 | 852 | |
| 853 | +/ Experimental permissions, not ready for production use |
| 854 | +/$wgGroupPermissions['sysop']['deleterevision'] = true; |
| 855 | +/$wgGroupPermissions['bureaucrat']['hiderevision'] = true; |
| 856 | + |
853 | 857 | /** |
854 | 858 | * The developer group is deprecated, but can be activated if need be |
855 | 859 | * to use the 'lockdb' and 'unlockdb' special pages. Those require |
Index: trunk/phase3/includes/PageHistory.php |
— | — | @@ -222,93 +222,101 @@ |
223 | 223 | |
224 | 224 | /** @todo document */ |
225 | 225 | function historyLine( $row, $next, $counter = '', $notificationtimestamp = false, $latest = false, $firstInList = false ) { |
| 226 | + global $wgUser; |
| 227 | + $rev = new Revision( $row ); |
226 | 228 | |
227 | | - if ( 0 == $row->rev_user ) { |
228 | | - $contribsPage =& Title::makeTitle( NS_SPECIAL, 'Contributions' ); |
229 | | - $ul = $this->mSkin->makeKnownLinkObj( $contribsPage, |
230 | | - htmlspecialchars( $row->rev_user_text ), |
231 | | - 'target=' . urlencode( $row->rev_user_text ) ); |
232 | | - } else { |
233 | | - $userPage =& Title::makeTitle( NS_USER, $row->rev_user_text ); |
234 | | - $ul = $this->mSkin->makeLinkObj( $userPage , htmlspecialchars( $row->rev_user_text ) ); |
235 | | - } |
| 229 | + $s = '<li>'; |
| 230 | + $curlink = $this->curLink( $rev, $latest ); |
| 231 | + $lastlink = $this->lastLink( $rev, $next, $counter ); |
| 232 | + $arbitrary = $this->diffButtons( $rev, $firstInList, $counter ); |
| 233 | + $link = $this->revLink( $rev ); |
| 234 | + $user = $this->mSkin->revUserLink( $rev ); |
236 | 235 | |
237 | | - $s = '<li>'; |
238 | | - /* This feature is not yet used according to schema */ |
239 | | - if( $row->rev_deleted ) { |
240 | | - $s .= '<span class="history-deleted">'; |
| 236 | + $s .= "($curlink) ($lastlink) $arbitrary"; |
| 237 | + |
| 238 | + if( $wgUser->isAllowed( 'deleterevision' ) ) { |
| 239 | + $revdel = Title::makeTitle( NS_SPECIAL, 'Revisiondelete' ); |
| 240 | + if( $firstInList ) { |
| 241 | + / We don't currently handle well changing the top revision's settings |
| 242 | + $del = wfMsgHtml( 'rev-delundel' ); |
| 243 | + } else { |
| 244 | + $del = $this->mSkin->makeKnownLinkObj( $revdel, |
| 245 | + wfMsg( 'rev-delundel' ), |
| 246 | + 'target=' . urlencode( $this->mTitle->getPrefixedDbkey() ) . |
| 247 | + '&oldid=' . urlencode( $rev->getId() ) ); |
| 248 | + } |
| 249 | + $s .= "(<small>$del</small>) "; |
241 | 250 | } |
242 | | - $curlink = $this->curLink( $row, $latest ); |
243 | | - $lastlink = $this->lastLink( $row, $next, $counter ); |
244 | | - $arbitrary = $this->diffButtons( $row, $firstInList, $counter ); |
245 | | - $link = $this->revLink( $row ); |
| 251 | + |
| 252 | + $s .= " $link <span class='history-user'>$user</span>"; |
246 | 253 | |
247 | | - $s .= "($curlink) ($lastlink) $arbitrary $link <span class='history-user'>$ul</span>"; |
248 | | - |
249 | 254 | if( $row->rev_minor_edit ) { |
250 | 255 | $s .= ' ' . wfElement( 'span', array( 'class' => 'minor' ), wfMsgHtml( 'minoreditletter') ); |
251 | 256 | } |
252 | 257 | |
253 | | - $s .= $this->mSkin->commentBlock( $row->rev_comment, $this->mTitle ); |
| 258 | + $s .= $this->mSkin->revComment( $rev ); |
254 | 259 | if ($notificationtimestamp && ($row->rev_timestamp >= $notificationtimestamp)) { |
255 | 260 | $s .= ' <span class="updatedmarker">' . wfMsgHtml( 'updatedmarker' ) . '</span>'; |
256 | 261 | } |
257 | | - if( $row->rev_deleted ) { |
258 | | - $s .= '</span> ' . wfMsgHtml( 'deletedrev' ); |
| 262 | + if( $row->rev_deleted & MW_REV_DELETED_TEXT ) { |
| 263 | + $s .= ' ' . wfMsgHtml( 'deletedrev' ); |
259 | 264 | } |
260 | 265 | $s .= "</li>\n"; |
261 | 266 | |
262 | 267 | return $s; |
263 | 268 | } |
264 | | - |
| 269 | + |
265 | 270 | /** @todo document */ |
266 | | - function revLink( $row ) { |
| 271 | + function revLink( $rev ) { |
267 | 272 | global $wgUser, $wgLang; |
268 | | - $date = $wgLang->timeanddate( wfTimestamp(TS_MW, $row->rev_timestamp), true ); |
269 | | - if( $row->rev_deleted && !$wgUser->isAllowed( 'undelete' ) ) { |
270 | | - return $date; |
| 273 | + $date = $wgLang->timeanddate( wfTimestamp(TS_MW, $rev->getTimestamp()), true ); |
| 274 | + if( $rev->userCan( MW_REV_DELETED_TEXT ) ) { |
| 275 | + $link = $this->mSkin->makeKnownLinkObj( |
| 276 | + $this->mTitle, $date, "oldid=" . $rev->getId() ); |
271 | 277 | } else { |
272 | | - return $this->mSkin->makeKnownLinkObj( |
273 | | - $this->mTitle, $date, "oldid={$row->rev_id}" ); |
| 278 | + $link = $date; |
274 | 279 | } |
| 280 | + if( $rev->isDeleted( MW_REV_DELETED_TEXT ) ) { |
| 281 | + return '<span class="history-deleted">' . $link . '</span>'; |
| 282 | + } |
| 283 | + return $link; |
275 | 284 | } |
276 | 285 | |
277 | 286 | /** @todo document */ |
278 | | - function curLink( $row, $latest ) { |
| 287 | + function curLink( $rev, $latest ) { |
279 | 288 | global $wgUser; |
280 | 289 | $cur = wfMsgHtml( 'cur' ); |
281 | | - if( $latest |
282 | | - || ( $row->rev_deleted && !$wgUser->isAllowed( 'undelete' ) ) ) { |
| 290 | + if( $latest || !$rev->userCan( MW_REV_DELETED_TEXT ) ) { |
283 | 291 | return $cur; |
284 | 292 | } else { |
285 | 293 | return $this->mSkin->makeKnownLinkObj( |
286 | 294 | $this->mTitle, $cur, |
287 | 295 | 'diff=' . $this->getLatestID() . |
288 | | - "&oldid={$row->rev_id}" ); |
| 296 | + "&oldid=" . $rev->getId() ); |
289 | 297 | } |
290 | 298 | } |
291 | 299 | |
292 | 300 | /** @todo document */ |
293 | | - function lastLink( $row, $next, $counter ) { |
| 301 | + function lastLink( $rev, $next, $counter ) { |
294 | 302 | global $wgUser; |
295 | 303 | $last = htmlspecialchars( wfMsg( 'last' ) ); |
296 | 304 | if( is_null( $next ) ) { |
297 | | - if( $row->rev_timestamp == $this->getEarliestOffset() ) { |
| 305 | + if( $rev->getTimestamp() == $this->getEarliestOffset() ) { |
298 | 306 | return $last; |
299 | 307 | } else { |
300 | 308 | / Cut off by paging; there are more behind us... |
301 | 309 | return $this->mSkin->makeKnownLinkObj( |
302 | 310 | $this->mTitle, |
303 | 311 | $last, |
304 | | - "diff={$row->rev_id}&oldid=prev" ); |
| 312 | + "diff=" . $rev->getId() . "&oldid=prev" ); |
305 | 313 | } |
306 | | - } elseif( $row->rev_deleted && !$wgUser->isAllowed( 'undelete' ) ) { |
| 314 | + } elseif( !$rev->userCan( MW_REV_DELETED_TEXT ) ) { |
307 | 315 | return $last; |
308 | 316 | } else { |
309 | 317 | return $this->mSkin->makeKnownLinkObj( |
310 | 318 | $this->mTitle, |
311 | 319 | $last, |
312 | | - "diff={$row->rev_id}&oldid={$next->rev_id}" |
| 320 | + "diff=" . $rev->getId() . "&oldid={$next->rev_id}" |
313 | 321 | /*, |
314 | 322 | '', |
315 | 323 | '', |
— | — | @@ -317,17 +325,17 @@ |
318 | 326 | } |
319 | 327 | |
320 | 328 | /** @todo document */ |
321 | | - function diffButtons( $row, $firstInList, $counter ) { |
| 329 | + function diffButtons( $rev, $firstInList, $counter ) { |
322 | 330 | global $wgUser; |
323 | 331 | if( $this->linesonpage > 1) { |
324 | 332 | $radio = array( |
325 | 333 | 'type' => 'radio', |
326 | | - 'value' => $row->rev_id, |
| 334 | + 'value' => $rev->getId(), |
327 | 335 | # do we really need to flood this on every item? |
328 | 336 | # 'title' => wfMsgHtml( 'selectolderversionfordiff' ) |
329 | 337 | ); |
330 | 338 | |
331 | | - if( $row->rev_deleted && !$wgUser->isAllowed( 'undelete' ) ) { |
| 339 | + if( !$rev->userCan( MW_REV_DELETED_TEXT ) ) { |
332 | 340 | $radio['disabled'] = 'disabled'; |
333 | 341 | } |
334 | 342 | |
— | — | @@ -447,7 +455,7 @@ |
448 | 456 | |
449 | 457 | $res = $dbr->select( |
450 | 458 | 'revision', |
451 | | - array('rev_id', 'rev_user', 'rev_comment', 'rev_user_text', |
| 459 | + array('rev_id', 'rev_page', 'rev_text_id', 'rev_user', 'rev_comment', 'rev_user_text', |
452 | 460 | 'rev_timestamp', 'rev_minor_edit', 'rev_deleted'), |
453 | 461 | array_merge(array("rev_page=$page_id"), $offsets), |
454 | 462 | $fname, |
Index: trunk/phase3/includes/SpecialPage.php |
— | — | @@ -77,7 +77,8 @@ |
78 | 78 | 'Userrights' => new SpecialPage( 'Userrights', 'userrights' ), |
79 | 79 | 'MIMEsearch' => new SpecialPage( 'MIMEsearch' ), |
80 | 80 | 'Unwatchedpages' => new SpecialPage( 'Unwatchedpages', 'unwatchedpages' ), |
81 | | - 'Listredirects' => new SpecialPage( 'Listredirects' ) |
| 81 | + 'Listredirects' => new SpecialPage( 'Listredirects' ), |
| 82 | + 'Revisiondelete' => new SpecialPage( 'Revisiondelete', 'deleterevision' ), |
82 | 83 | ); |
83 | 84 | |
84 | 85 | if( !$wgDisableCounters ) { |
Index: trunk/phase3/includes/LogPage.php |
— | — | @@ -166,6 +166,7 @@ |
167 | 167 | |
168 | 168 | 'delete/delete' => 'deletedarticle', |
169 | 169 | 'delete/restore' => 'undeletedarticle', |
| 170 | + 'delete/revision' => 'revdelete-logentry', |
170 | 171 | 'upload/upload' => 'uploadedimage', |
171 | 172 | 'upload/revert' => 'uploadedimage', |
172 | 173 | 'move/move' => '1movedto2', |
Index: trunk/phase3/RELEASE-NOTES |
— | — | @@ -685,7 +685,12 @@ |
686 | 686 | unnecessary hidden UI work when watch/unwatch is performed on edit |
687 | 687 | * Fixed bogus master fallback in external storage |
688 | 688 | * (bug 5246) Add speak:none to "hiddenStructure" class in main.css |
| 689 | +* Further work on rev_deleted; changed to a bitfield with several data-hiding |
| 690 | + options. Not yet ready for production use; Special:Revisiondelete is |
| 691 | + incomplete, and the flags are not preserved across page deletion/undeletion. |
| 692 | + To try it; add the 'deleterevision' permission to a privileged group. |
689 | 693 | |
| 694 | + |
690 | 695 | === Caveats === |
691 | 696 | |
692 | 697 | Some output, particularly involving user-supplied inline HTML, may not |
Index: trunk/phase3/languages/Messages.php |
— | — | @@ -565,7 +565,39 @@ |
566 | 566 | 'deletedrev' => '[deleted]', |
567 | 567 | 'histfirst' => 'Earliest', |
568 | 568 | 'histlast' => 'Latest', |
| 569 | +'rev-deleted-comment' => '(comment removed)', |
| 570 | +'rev-deleted-user' => '(username removed)', |
| 571 | +'rev-deleted-text-permission' => '<div class="mw-warning plainlinks"> |
| 572 | +This page revision has been removed from the public archives. |
| 573 | +There may be details in the [{{fullurl:Special:Log/delete|page={{PAGENAMEE}}}} deletion log]. |
| 574 | +</div>', |
| 575 | +'rev-deleted-text-view' => '<div class="mw-warning plainlinks"> |
| 576 | +This page revision has been removed from the public archives. |
| 577 | +As an administrator on this site you can view it; |
| 578 | +there may be details in the [{{fullurl:Special:Log/delete|page={{PAGENAMEE}}}} deletion log]. |
| 579 | +</div>', |
| 580 | +#'rev-delundel' => 'del/undel', |
| 581 | +'rev-delundel' => 'show/hide', |
569 | 582 | |
| 583 | +# Revision deletion |
| 584 | +# |
| 585 | +'revisiondelete' => 'Delete/undelete revisions', |
| 586 | +'revdelete-selected' => 'Selected revision of [[:$1]]:', |
| 587 | +'revdelete-text' => "Deleted revisions will still appear in the page history, |
| 588 | +but their text contents will be inaccessible to the public. |
| 589 | + |
| 590 | +Other admins on this wiki will still be able to access the hidden content and can |
| 591 | +undelete it again through this same interface, unless an additional restriction |
| 592 | +is placed by the site operators.", |
| 593 | +'revdelete-legend' => 'Set revision restrictions:', |
| 594 | +'revdelete-hide-text' => 'Hide revision text', |
| 595 | +'revdelete-hide-comment' => 'Hide edit comment', |
| 596 | +'revdelete-hide-user' => 'Hide editor\'s username/IP', |
| 597 | +'revdelete-hide-restricted' => 'Apply these restrictions to sysops as well as others', |
| 598 | +'revdelete-log' => 'Log comment:', |
| 599 | +'revdelete-submit' => 'Apply to selected revision', |
| 600 | +'revdelete-logentry' => 'changed revision visibility for [[$1]]', |
| 601 | + |
570 | 602 | # Diffs |
571 | 603 | # |
572 | 604 | 'difference' => '(Difference between revisions)', |