| 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 <brion@pobox.com> |
| | 3 | +# Copyright (C) 2003, 2005, 2006 Brion Vibber <brion@pobox.com> |
| 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)', |