From 22c813ad9784bceca4220a558910fd5de00dbd4b Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 31 Mar 2005 11:40:05 +0000 Subject: [PATCH] * Add wfElement() for handy generation of properly-escaped XML/HTML elements with attributes. * Begin infrastructure for deleted-revision tag; add rev_deleted field, some display support for it in history and contribs. Not yet used for deletions. Run update.php or source maintenance/archives/patch-rev_deleted.sql --- includes/GlobalFunctions.php | 30 ++++ includes/PageHistory.php | 199 ++++++++++++++------- includes/Revision.php | 13 +- includes/SpecialContributions.php | 48 +++-- languages/Language.php | 1 + maintenance/archives/patch-rev_deleted.sql | 11 ++ maintenance/tables.sql | 1 + maintenance/updaters.inc | 1 + skins/common/common.css | 8 +- skins/monobook/main.css | 8 +- 10 files changed, 225 insertions(+), 95 deletions(-) create mode 100644 maintenance/archives/patch-rev_deleted.sql diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php index 31b72a760b..ff859c6918 100644 --- a/includes/GlobalFunctions.php +++ b/includes/GlobalFunctions.php @@ -1084,4 +1084,34 @@ function wfGetSiteNotice() { return $notice; } +/** + * Format an XML element with given attributes and, optionally, text content. + * Element and attribute names are assumed to be ready for literal inclusion. + * Strings are assumed to not contain XML-illegal characters; special + * characters (<, >, &) are escaped but illegals are not touched. + * + * @param string $element + * @param array $attribs Name=>value pairs. Values will be escaped. + * @param bool $contents NULL to make an open tag only; '' for a contentless closed tag (default) + * @return string + */ +function wfElement( $element, $attribs = array(), $contents = '') { + $out = '<' . $element; + foreach( $attribs as $name => $val ) { + $out .= ' ' . $name . '="' . htmlspecialchars( $val ) . '"'; + } + if( is_null( $contents ) ) { + $out .= '>'; + } else { + if( $contents == '' ) { + $out .= ' />'; + } else { + $out .= '>'; + $out .= htmlspecialchars( $contents ); + $out .= ""; + } + } + return $out; +} + ?> diff --git a/includes/PageHistory.php b/includes/PageHistory.php index 6f88b8af22..1dcaa58b7e 100644 --- a/includes/PageHistory.php +++ b/includes/PageHistory.php @@ -15,7 +15,7 @@ include_once ( "SpecialValidate.php" ) ; class PageHistory { var $mArticle, $mTitle, $mSkin; - var $lastline, $lastdate; + var $lastdate; var $linesonpage; function PageHistory( $article ) { $this->mArticle =& $article; @@ -88,7 +88,7 @@ class PageHistory { $limits .= " LIMIT $limitplus "; $sql = "SELECT rev_id,rev_user," . - "rev_comment,rev_user_text,rev_timestamp,rev_minor_edit ". + "rev_comment,rev_user_text,rev_timestamp,rev_minor_edit,rev_deleted ". "FROM $revision $use_index " . "WHERE rev_page=$id " . $offsets . @@ -133,24 +133,12 @@ class PageHistory { $bits); $s = $numbar; - if($this->linesonpage > 0) { - $submitpart1 = 'submitbuttonhtml1 = $submitpart1 . ' />'; - $this->submitbuttonhtml2 = $submitpart1 . ' id="historysubmit" />'; - } $s .= $this->beginHistoryList(); $counter = 1; - foreach($pages as $line) { - $s .= $this->historyLine( - $line->rev_timestamp, $line->rev_user, - $line->rev_user_text, $namespace, - $title, $line->rev_id, - $line->rev_comment, ( $line->rev_minor_edit > 0 ), - $counter, - $notificationtimestamp, - ($counter == 1 && $offset == 0) - ); + foreach($pages as $i => $line) { + $first = ($counter == 1 && $offset == 0); + $next = isset( $pages[$i + 1] ) ? $pages[$i + 1 ] : null; + $s .= $this->historyLine( $line, $next, $counter, $notificationtimestamp, $first ); $counter++; } $s .= $this->endHistoryList( !$atend ); @@ -167,27 +155,39 @@ class PageHistory { function beginHistoryList() { global $wgTitle; - $this->lastdate = $this->lastline = ''; + $this->lastdate = ''; $s = '

' . wfMsg( 'histlegend' ) . '

'; $s .= '
'; $prefixedkey = htmlspecialchars($wgTitle->getPrefixedDbKey()); $s .= "\n"; - $s .= !empty($this->submitbuttonhtml1) ? $this->submitbuttonhtml1."\n":''; + $s .= $this->submitButton(); $s .= ''; - $s .= !empty($this->submitbuttonhtml2) ? $this->submitbuttonhtml2 : ''; + $s = ''; + $s .= $this->submitButton( array( 'id' => 'historysubmit' ) ); $s .= '
'; return $s; } + + function submitButton( $bits = array() ) { + return ( $this->linesonpage > 0 ) + ? wfElement( 'input', array_merge( $bits, + array( + 'class' => 'historysubmit', + 'type' => 'submit', + 'accesskey' => wfMsg( 'accesskey-compareselectedversions' ), + 'title' => wfMsg( 'tooltip-compareselectedversions' ), + 'value' => wfMsg( 'compareselectedversions' ), + ) ) ) + : ''; + } - function historyLine( $ts, $u, $ut, $ns, $ttl, $oid, $c, $isminor, $counter = '', $notificationtimestamp = false, $latest = false ) { + function historyLine( $row, $next, $counter = '', $notificationtimestamp = false, $latest = false ) { global $wgLang, $wgContLang; static $message; @@ -197,65 +197,130 @@ class PageHistory { } } - if ( $oid && $this->lastline ) { - $ret = preg_replace( "/!OLDID!([0-9]+)!/", $this->mSkin->makeKnownLinkObj( - $this->mTitle, $message['last'], "diff=\\1&oldid={$oid}",'' ,'' ,' tabindex="'.$counter.'"' ), $this->lastline ); - } else { - $ret = ''; - } - $dt = $wgLang->timeanddate( $ts, true ); + $link = $this->revLink( $row ); - if ( $oid ) { - $q = 'oldid='.$oid; - } else { - $q = ''; - } - $link = $this->mSkin->makeKnownLinkObj( $this->mTitle, $dt, $q ); - - if ( 0 == $u ) { + if ( 0 == $row->rev_user ) { $contribsPage =& Title::makeTitle( NS_SPECIAL, 'Contributions' ); $ul = $this->mSkin->makeKnownLinkObj( $contribsPage, - htmlspecialchars( $ut ), 'target=' . urlencode( $ut ) ); + htmlspecialchars( $row->rev_user_text ), + 'target=' . urlencode( $row->rev_user_text ) ); } else { - $userPage =& Title::makeTitle( NS_USER, $ut ); - $ul = $this->mSkin->makeLinkObj( $userPage , htmlspecialchars( $ut ) ); + $userPage =& Title::makeTitle( NS_USER, $row->rev_user_text ); + $ul = $this->mSkin->makeLinkObj( $userPage , htmlspecialchars( $row->rev_user_text ) ); } $s = '
  • '; - if ( $oid && !$latest ) { - $curlink = $this->mSkin->makeKnownLinkObj( $this->mTitle, $message['cur'], - 'diff=0&oldid='.$oid ); - } else { - $curlink = $message['cur']; + if( $row->rev_deleted ) { + $s .= ''; } - $arbitrary = ''; - if( $this->linesonpage > 1) { - # XXX: move title texts to javascript - $checkmark = ''; - if ( !$oid || $latest ) { - $arbitrary = ''; - $checkmark = ' checked="checked"'; - } else { - if( $counter == 2 ) $checkmark = ' checked="checked"'; - $arbitrary = ''; - $checkmark = ''; - } - $arbitrary .= ''; + $curlink = $this->curLink( $row, $latest ); + $lastlink = $this->lastLink( $row, $next, $counter ); + $arbitrary = $this->diffButtons( $row, $latest, $counter ); + $s .= "({$curlink}) ({$lastlink}) $arbitrary {$link} {$ul}"; + + if( $row->rev_minor_edit ) { + $s .= ' ' . wfElement( 'span', array( 'class' => 'minor' ), $message['minoreditletter'] ); } - $s .= "({$curlink}) (!OLDID!{$oid}!) $arbitrary {$link} {$ul}"; - $s .= $isminor ? ' '.$message['minoreditletter'].'': '' ; - $s .= $this->mSkin->commentBlock( $c, $this->mTitle ); - if ($notificationtimestamp && ($ts >= $notificationtimestamp)) { + $s .= $this->mSkin->commentBlock( $row->rev_comment, $this->mTitle ); + if ($notificationtimestamp && ($row->rev_timestamp >= $notificationtimestamp)) { $s .= wfMsg( 'updatedmarker' ); } + if( $row->rev_deleted ) { + $s .= " " . htmlspecialchars( wfMsg( 'deletedrev' ) ); + } $s .= '
  • '; - $this->lastline = $s; - return $ret; + return $s; } + function revLink( $row ) { + global $wgUser, $wgLang; + $date = $wgLang->timeanddate( $row->rev_timestamp, true ); + if( $row->rev_deleted && !$wgUser->isAllowed( 'undelete' ) ) { + return $date; + } else { + return $this->mSkin->makeKnownLinkObj( + $this->mTitle, + $date, + 'oldid='.$row->rev_id ); + } + } + + function curLink( $row, $latest ) { + global $wgUser; + $cur = htmlspecialchars( wfMsg( 'cur' ) ); + if( $latest + || ( $row->rev_deleted && !$wgUser->isAllowed( 'undelete' ) ) ) { + return $cur; + } else { + return $this->mSkin->makeKnownLinkObj( + $this->mTitle, + $cur, + 'diff=0&oldid=' . $row->rev_id ); + } + } + + function lastLink( $row, $next, $counter ) { + global $wgUser; + $last = htmlspecialchars( wfMsg( 'last' ) ); + if( is_null( $next ) + || ( $row->rev_deleted && !$wgUser->isAllowed( 'undelete' ) ) ) { + return $last; + } else { + return $this->mSkin->makeKnownLinkObj( + $this->mTitle, + $last, + "diff={$next->rev_id}&oldid={$row->rev_id}", + '', + '', + ' tabindex="'.$counter.'"' ); + } + } + + function diffButtons( $row, $latest, $counter ) { + global $wgUser; + if( $this->linesonpage > 1) { + $radio = array( + 'type' => 'radio', + 'value' => $row->rev_id, + 'title' => wfMsg( 'selectolderversionfordiff' ) + ); + if( $row->rev_deleted && !$wgUser->isAllowed( 'undelete' ) ) { + $radio['disabled'] = 'disabled'; + } + + # XXX: move title texts to javascript + if ( $latest ) { + $first = wfElement( 'input', array_merge( + $radio, + array( + 'style' => 'visibility:hidden', + 'name' => 'oldid' ) ) ); + $checkmark = array( 'checked' => 'checked' ); + } else { + if( $counter == 2 ) { + $checkmark = array( 'checked' => 'checked' ); + } else { + $checkmark = array(); + } + $first = wfElement( 'input', array_merge( + $radio, + $checkmark, + array( 'name' => 'oldid' ) ) ); + $checkmark = array(); + } + $second = wfElement( 'input', array_merge( + $radio, + $checkmark, + array( 'name' => 'diff' ) ) ); + return $first . $second; + } else { + return ''; + } + } + } ?> diff --git a/includes/Revision.php b/includes/Revision.php index 02d7c520fe..d6418e3508 100644 --- a/includes/Revision.php +++ b/includes/Revision.php @@ -170,7 +170,8 @@ class Revision { 'rev_user_text', 'rev_user', 'rev_minor_edit', - 'rev_timestamp' ), + 'rev_timestamp', + 'rev_deleted' ), $conditions, 'Revision::fetchRow' ); return $db->resultObject( $res ); @@ -190,6 +191,7 @@ class Revision { $this->mUser = IntVal( $row->rev_user ); $this->mMinorEdit = IntVal( $row->rev_minor_edit ); $this->mTimestamp = $row->rev_timestamp; + $this->mDeleted = IntVal( $row->rev_deleted ); $this->mCurrent = ( $row->rev_id == $row->page_latest ); $this->mTitle = Title::makeTitle( $row->page_namespace, @@ -212,6 +214,7 @@ class Revision { $this->mUser = isset( $row['user'] ) ? IntVal( $row['user'] ) : $wgUser->getId(); $this->mMinorEdit = isset( $row['minor_edit'] ) ? IntVal( $row['minor_edit'] ) : 0; $this->mTimestamp = isset( $row['timestamp'] ) ? StrVal( $row['timestamp'] ) : wfTimestamp( TS_MW ); + $this->mDeleted = isset( $row['deleted'] ) ? IntVal( $row['deleted'] ) : 0; $this->mText = isset( $row['text'] ) ? StrVal( $row['text'] ) : null; $this->mTitle = null; # Load on demand if needed @@ -296,6 +299,13 @@ class Revision { return (bool)$this->mMinorEdit; } + /** + * @return bool + */ + function isDeleted() { + return (bool)$this->mDeleted; + } + /** * @return string */ @@ -469,6 +479,7 @@ class Revision { 'rev_user' => $this->mUser, 'rev_user_text' => $this->mUserText, 'rev_timestamp' => $dbw->timestamp( $this->mTimestamp ), + 'rev_deleted' => $this->mDeleted, ), $fname ); diff --git a/includes/SpecialContributions.php b/includes/SpecialContributions.php index e670797529..492cf4e4b3 100644 --- a/includes/SpecialContributions.php +++ b/includes/SpecialContributions.php @@ -101,7 +101,8 @@ function wfSpecialContributions( $par = '' ) { $use_index = $dbr->useIndexClause( $index ); $sql = "SELECT page_namespace,page_title,page_is_new,page_latest, - rev_id,rev_timestamp,rev_comment,rev_minor_edit,rev_user_text + rev_id,rev_timestamp,rev_comment,rev_minor_edit,rev_user_text, + rev_deleted FROM $page,$revision $use_index WHERE page_id=rev_page AND $condition $minorQuery " . "ORDER BY rev_timestamp DESC LIMIT {$querylimit}"; @@ -129,16 +130,7 @@ function wfSpecialContributions( $par = '' ) { $wgOut->addHTML( "\n" ); @@ -162,7 +154,7 @@ function wfSpecialContributions( $par = '' ) { * * @todo This would probably look a lot nicer in a table. */ -function ucListEdit( $sk, $ns, $t, $ts, $topmark, $comment, $isminor, $isnew, $target, $oldid ) { +function ucListEdit( $sk, $row ) { $fname = 'ucListEdit'; wfProfileIn( $fname ); @@ -174,12 +166,12 @@ function ucListEdit( $sk, $ns, $t, $ts, $topmark, $comment, $isminor, $isnew, $t } } - $page =& Title::makeTitle( $ns, $t ); + $page =& Title::makeTitle( $row->page_namespace, $row->page_title ); $link = $sk->makeKnownLinkObj( $page, '' ); $difftext = $topmarktext = ''; - if($topmark) { + if( $row->rev_id == $row->page_latest ) { $topmarktext .= '' . $messages['uctop'] . ''; - if(!$isnew) { + if( !$row->page_is_new ) { $difftext .= $sk->makeKnownLinkObj( $page, '(' . $messages['diff'] . ')', 'diff=0' ); } else { $difftext .= $messages['newarticle']; @@ -188,30 +180,36 @@ function ucListEdit( $sk, $ns, $t, $ts, $topmark, $comment, $isminor, $isnew, $t if( $wgUser->isAllowed('rollback') ) { $extraRollback = $wgRequest->getBool( 'bot' ) ? '&bot=1' : ''; $extraRollback .= '&token=' . urlencode( - $wgUser->editToken( array( $page->getPrefixedText(), $target ) ) ); - # $target = $wgRequest->getText( 'target' ); + $wgUser->editToken( array( $page->getPrefixedText(), $row->rev_user_text ) ) ); $topmarktext .= ' ['. $sk->makeKnownLinkObj( $page, $messages['rollbacklink'], - 'action=rollback&from=' . urlencode( $target ) . $extraRollback ) .']'; + 'action=rollback&from=' . urlencode( $row->rev_user_text ) . $extraRollback ) .']'; } } - if ( $oldid ) { - $difftext= $sk->makeKnownLinkObj( $page, '(' . $messages['diff'].')', 'diff=prev&oldid='.$oldid ); - } + if( $row->rev_deleted && !$wgUser->isAllowed( 'undelete' ) ) { + $difftext = '(' . $messages['diff'] . ')'; + } else { + $difftext = $sk->makeKnownLinkObj( $page, '(' . $messages['diff'].')', 'diff=prev&oldid='.$row->rev_id ); + } $histlink='('.$sk->makeKnownLinkObj( $page, $messages['hist'], 'action=history' ) . ')'; - $comment = $sk->commentBlock( $comment, $page ); - $d = $wgLang->timeanddate( $ts, true ); + $comment = $sk->commentBlock( $row->rev_comment, $page ); + $d = $wgLang->timeanddate( $row->rev_timestamp, true ); - if ($isminor) { + if( $row->rev_minor_edit ) { $mflag = '' . $messages['minoreditletter'] . ' '; } else { $mflag = ''; } - $wgOut->addHTML( "
  • {$d} {$histlink} {$difftext} {$mflag} {$link} {$comment} {$topmarktext}
  • \n" ); + $ret = "{$d} {$histlink} {$difftext} {$mflag} {$link} {$comment} {$topmarktext}"; + if( $row->rev_deleted ) { + $ret = '' . $ret . ' ' . htmlspecialchars( wfMsg( 'deletedrev' ) ); + } + $ret = "
  • $ret
  • \n"; wfProfileOut( $fname ); + return $ret; } /** diff --git a/languages/Language.php b/languages/Language.php index e4ac864191..a4ca3df982 100644 --- a/languages/Language.php +++ b/languages/Language.php @@ -765,6 +765,7 @@ Please check the URL you used to access this page.\n", Legend: (cur) = difference with current version, (last) = difference with preceding version, M = minor edit.', 'history_copyright' => '-', +'deletedrev' => '[deleted]', # Diffs # diff --git a/maintenance/archives/patch-rev_deleted.sql b/maintenance/archives/patch-rev_deleted.sql new file mode 100644 index 0000000000..3af0c1d726 --- /dev/null +++ b/maintenance/archives/patch-rev_deleted.sql @@ -0,0 +1,11 @@ +-- +-- Add rev_deleted flag to revision table. +-- Deleted revisions can thus continue to be listed in history +-- and user contributions, and their text storage doesn't have +-- to be disturbed. +-- +-- 2005-03-31 +-- + +ALTER TABLE /*$wgDBprefix*/revision + ADD rev_deleted tinyint(1) unsigned NOT NULL default '0'; diff --git a/maintenance/tables.sql b/maintenance/tables.sql index 15cda2be92..f5fdae3207 100644 --- a/maintenance/tables.sql +++ b/maintenance/tables.sql @@ -70,6 +70,7 @@ CREATE TABLE /*$wgDBprefix*/revision ( rev_user_text varchar(255) binary NOT NULL default '', rev_timestamp char(14) binary NOT NULL default '', rev_minor_edit tinyint(1) unsigned NOT NULL default '0', + rev_deleted tinyint(1) unsigned NOT NULL default '0', PRIMARY KEY rev_page_id (rev_page, rev_id), UNIQUE INDEX rev_id (rev_id), diff --git a/maintenance/updaters.inc b/maintenance/updaters.inc index b388c3fdde..d8e967642d 100644 --- a/maintenance/updaters.inc +++ b/maintenance/updaters.inc @@ -36,6 +36,7 @@ $wgNewFields = array( array( 'logging', 'log_params', 'patch-log_params.sql' ), array( 'archive', 'ar_rev_id', 'patch-archive-rev_id.sql' ), array( 'page', 'page_len', 'patch-page_len.sql' ), + array( 'revision', 'rev_deleted', 'patch-rev_deleted.sql' ), ); function add_table( $name, $patch ) { diff --git a/skins/common/common.css b/skins/common/common.css index 53f95ecc3d..8ff514ba3a 100644 --- a/skins/common/common.css +++ b/skins/common/common.css @@ -317,4 +317,10 @@ span.changedby { .editExternallyHelp { font-style:italic; color:gray; -} \ No newline at end of file +} + +li span.deleted { + text-decoration: line-through; + color: #888; + font-style: italic; +} diff --git a/skins/monobook/main.css b/skins/monobook/main.css index d754f58880..373aea1d88 100644 --- a/skins/monobook/main.css +++ b/skins/monobook/main.css @@ -1103,4 +1103,10 @@ span.changedby { .editExternallyHelp { font-style:italic; color:gray; -} \ No newline at end of file +} + +li span.deleted { + text-decoration: line-through; + color: #888; + font-style: italic; +} -- 2.20.1