diff options
Diffstat (limited to 'includes/actions')
-rw-r--r-- | includes/actions/CachedAction.php | 36 | ||||
-rw-r--r-- | includes/actions/CreditsAction.php | 13 | ||||
-rw-r--r-- | includes/actions/DeleteAction.php | 11 | ||||
-rw-r--r-- | includes/actions/EditAction.php | 21 | ||||
-rw-r--r-- | includes/actions/HistoryAction.php | 77 | ||||
-rw-r--r-- | includes/actions/InfoAction.php | 320 | ||||
-rw-r--r-- | includes/actions/MarkpatrolledAction.php | 5 | ||||
-rw-r--r-- | includes/actions/ProtectAction.php | 20 | ||||
-rw-r--r-- | includes/actions/PurgeAction.php | 15 | ||||
-rw-r--r-- | includes/actions/RawAction.php | 35 | ||||
-rw-r--r-- | includes/actions/RenderAction.php | 11 | ||||
-rw-r--r-- | includes/actions/RevertAction.php | 6 | ||||
-rw-r--r-- | includes/actions/RevisiondeleteAction.php | 5 | ||||
-rw-r--r-- | includes/actions/RollbackAction.php | 35 | ||||
-rw-r--r-- | includes/actions/ViewAction.php | 11 | ||||
-rw-r--r-- | includes/actions/WatchAction.php | 10 |
16 files changed, 454 insertions, 177 deletions
diff --git a/includes/actions/CachedAction.php b/includes/actions/CachedAction.php index d21f9aeb..bfdda7b9 100644 --- a/includes/actions/CachedAction.php +++ b/includes/actions/CachedAction.php @@ -1,22 +1,8 @@ <?php - /** * Abstract action class with scaffolding for caching HTML and other values * in a single blob. * - * Before using any of the caching functionality, call startCache. - * After the last call to either getCachedValue or addCachedHTML, call saveCache. - * - * To get a cached value or compute it, use getCachedValue like this: - * $this->getCachedValue( $callback ); - * - * To add HTML that should be cached, use addCachedHTML like this: - * $this->addCachedHTML( $callback ); - * - * The callback function is only called when needed, so do all your expensive - * computations here. This function should returns the HTML to be cached. - * It should not add anything to the PageOutput object! - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -33,10 +19,30 @@ * http://www.gnu.org/copyleft/gpl.html * * @file - * @ingroup Action + * @ingroup Actions * @author Jeroen De Dauw < jeroendedauw@gmail.com > * @since 1.20 */ + +/** + * Abstract action class with scaffolding for caching HTML and other values + * in a single blob. + * + * Before using any of the caching functionality, call startCache. + * After the last call to either getCachedValue or addCachedHTML, call saveCache. + * + * To get a cached value or compute it, use getCachedValue like this: + * $this->getCachedValue( $callback ); + * + * To add HTML that should be cached, use addCachedHTML like this: + * $this->addCachedHTML( $callback ); + * + * The callback function is only called when needed, so do all your expensive + * computations here. This function should returns the HTML to be cached. + * It should not add anything to the PageOutput object! + * + * @ingroup Actions + */ abstract class CachedAction extends FormlessAction implements ICacheHelper { /** diff --git a/includes/actions/CreditsAction.php b/includes/actions/CreditsAction.php index f7152297..4d3c41be 100644 --- a/includes/actions/CreditsAction.php +++ b/includes/actions/CreditsAction.php @@ -23,6 +23,9 @@ * @author <evan@wikitravel.org> */ +/** + * @ingroup Actions + */ class CreditsAction extends FormlessAction { public function getName() { @@ -55,8 +58,8 @@ class CreditsAction extends FormlessAction { /** * Get a list of contributors * - * @param $cnt Int: maximum list of contributors to show - * @param $showIfMax Bool: whether to contributors if there more than $cnt + * @param int $cnt maximum list of contributors to show + * @param bool $showIfMax whether to contributors if there more than $cnt * @return String: html */ public function getCredits( $cnt, $showIfMax = true ) { @@ -97,8 +100,8 @@ class CreditsAction extends FormlessAction { /** * Get a list of contributors of $article - * @param $cnt Int: maximum list of contributors to show - * @param $showIfMax Bool: whether to contributors if there more than $cnt + * @param int $cnt maximum list of contributors to show + * @param bool $showIfMax whether to contributors if there more than $cnt * @return String: html */ protected function getContributors( $cnt, $showIfMax ) { @@ -122,7 +125,7 @@ class CreditsAction extends FormlessAction { # Sift for real versus user names foreach ( $contributors as $user ) { - $cnt--; + $cnt--; if ( $user->isLoggedIn() ) { $link = $this->link( $user ); if ( !in_array( 'realname', $wgHiddenPrefs ) && $user->getRealName() ) { diff --git a/includes/actions/DeleteAction.php b/includes/actions/DeleteAction.php index 5a5a382b..db7123db 100644 --- a/includes/actions/DeleteAction.php +++ b/includes/actions/DeleteAction.php @@ -23,17 +23,24 @@ * @author Timo Tijhof */ +/** + * Handle page deletion + * + * This is a wrapper that will call Article::delete(). + * + * @ingroup Actions + */ class DeleteAction extends FormlessAction { public function getName() { return 'delete'; } - public function onView(){ + public function onView() { return null; } - public function show(){ + public function show() { $this->page->delete(); diff --git a/includes/actions/EditAction.php b/includes/actions/EditAction.php index 08a33f4c..dec3d841 100644 --- a/includes/actions/EditAction.php +++ b/includes/actions/EditAction.php @@ -23,17 +23,25 @@ * @author Timo Tijhof */ +/** + * Page edition handler + * + * This is a wrapper that will call the EditPage class, or ExternalEdit + * if $wgUseExternalEditor is set to true and requested by the user. + * + * @ingroup Actions + */ class EditAction extends FormlessAction { public function getName() { return 'edit'; } - public function onView(){ + public function onView() { return null; } - public function show(){ + public function show() { $page = $this->page; $request = $this->getRequest(); $user = $this->getUser(); @@ -56,13 +64,20 @@ class EditAction extends FormlessAction { } +/** + * Edit submission handler + * + * This is the same as EditAction; except that it sets the session cookie. + * + * @ingroup Actions + */ class SubmitAction extends EditAction { public function getName() { return 'submit'; } - public function show(){ + public function show() { if ( session_id() == '' ) { // Send a cookie so anons get talk message notifications wfSetupSession(); diff --git a/includes/actions/HistoryAction.php b/includes/actions/HistoryAction.php index dcd6fe55..245a5bdc 100644 --- a/includes/actions/HistoryAction.php +++ b/includes/actions/HistoryAction.php @@ -20,16 +20,18 @@ * http://www.gnu.org/copyleft/gpl.html * * @file + * @ingroup Actions */ /** - * This class handles printing the history page for an article. In order to + * This class handles printing the history page for an article. In order to * be efficient, it uses timestamps rather than offsets for paging, to avoid * costly LIMIT,offset queries. * * Construct it by passing in an Article, and call $h->history() to print the * history. * + * @ingroup Actions */ class HistoryAction extends FormlessAction { const DIR_PREV = 0; @@ -132,7 +134,7 @@ class HistoryAction extends FormlessAction { array( 'delete', 'move' ), $this->getTitle(), '', - array( 'lim' => 10, + array( 'lim' => 10, 'conds' => array( "log_action != 'revision'" ), 'showIfEmpty' => false, 'msgKey' => array( 'moveddeleted-notice' ) @@ -145,21 +147,25 @@ class HistoryAction extends FormlessAction { /** * Add date selector to quickly get to a certain time */ - $year = $request->getInt( 'year' ); - $month = $request->getInt( 'month' ); - $tagFilter = $request->getVal( 'tagfilter' ); + $year = $request->getInt( 'year' ); + $month = $request->getInt( 'month' ); + $tagFilter = $request->getVal( 'tagfilter' ); $tagSelector = ChangeTags::buildTagFilterSelector( $tagFilter ); /** * Option to show only revisions that have been (partially) hidden via RevisionDelete */ if ( $request->getBool( 'deleted' ) ) { - $conds = array( "rev_deleted != '0'" ); + $conds = array( 'rev_deleted != 0' ); } else { $conds = array(); } - $checkDeleted = Xml::checkLabel( $this->msg( 'history-show-deleted' )->text(), + if ( $this->getUser()->isAllowed( 'deletedhistory' ) ) { + $checkDeleted = Xml::checkLabel( $this->msg( 'history-show-deleted' )->text(), 'deleted', 'mw-show-deleted-only', $request->getBool( 'deleted' ) ) . "\n"; + } else { + $checkDeleted = ''; + } // Add the general form $action = htmlspecialchars( $wgScript ); @@ -170,16 +176,16 @@ class HistoryAction extends FormlessAction { false, array( 'id' => 'mw-history-search' ) ) . - Html::hidden( 'title', $this->getTitle()->getPrefixedDBKey() ) . "\n" . + Html::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ) . "\n" . Html::hidden( 'action', 'history' ) . "\n" . - Xml::dateMenu( $year, $month ) . ' ' . + Xml::dateMenu( ( $year == null ? date( "Y" ) : $year ), $month ) . ' ' . ( $tagSelector ? ( implode( ' ', $tagSelector ) . ' ' ) : '' ) . $checkDeleted . Xml::submitButton( $this->msg( 'allpagessubmit' )->text() ) . "\n" . '</fieldset></form>' ); - wfRunHooks( 'PageHistoryBeforeList', array( &$this->page ) ); + wfRunHooks( 'PageHistoryBeforeList', array( &$this->page, $this->getContext() ) ); // Create and output the list. $pager = new HistoryPager( $this, $year, $month, $tagFilter, $conds ); @@ -218,7 +224,7 @@ class HistoryAction extends FormlessAction { } if ( $offset ) { - $offsets = array( "rev_timestamp $oper '$offset'" ); + $offsets = array( "rev_timestamp $oper " . $dbr->addQuotes( $dbr->timestamp( $offset ) ) ); } else { $offsets = array(); } @@ -227,7 +233,7 @@ class HistoryAction extends FormlessAction { return $dbr->select( 'revision', Revision::selectFields(), - array_merge( array( "rev_page=$page_id" ), $offsets ), + array_merge( array( 'rev_page' => $page_id ), $offsets ), __METHOD__, array( 'ORDER BY' => "rev_timestamp $dirs", 'USE INDEX' => 'page_timestamp', 'LIMIT' => $limit ) @@ -237,7 +243,7 @@ class HistoryAction extends FormlessAction { /** * Output a subscription feed listing recent edits to this page. * - * @param $type String: feed type + * @param string $type feed type */ function feed( $type ) { global $wgFeedClasses, $wgFeedLimit; @@ -327,6 +333,7 @@ class HistoryAction extends FormlessAction { /** * @ingroup Pager + * @ingroup Actions */ class HistoryPager extends ReverseChronologicalPager { public $lastRow = false, $counter, $historyPage, $buttons, $conds; @@ -436,7 +443,7 @@ class HistoryPager extends ReverseChronologicalPager { $this->getOutput()->wrapWikiMsg( "<div class='mw-history-legend'>\n$1\n</div>", 'histlegend' ); $s = Html::openElement( 'form', array( 'action' => $wgScript, 'id' => 'mw-history-compare' ) ) . "\n"; - $s .= Html::hidden( 'title', $this->getTitle()->getPrefixedDbKey() ) . "\n"; + $s .= Html::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ) . "\n"; $s .= Html::hidden( 'action', 'historysubmit' ) . "\n"; // Button container stored in $this->buttons for re-use in getEndBody() @@ -504,14 +511,14 @@ class HistoryPager extends ReverseChronologicalPager { /** * Creates a submit button * - * @param $message String: text of the submit button, will be escaped - * @param $attributes Array: attributes + * @param string $message text of the submit button, will be escaped + * @param array $attributes attributes * @return String: HTML output for the submit button */ function submitButton( $message, $attributes = array() ) { # Disable submit button if history has 1 revision only if ( $this->getNumRows() > 1 ) { - return Xml::submitButton( $message , $attributes ); + return Xml::submitButton( $message, $attributes ); } else { return ''; } @@ -572,11 +579,11 @@ class HistoryPager extends ReverseChronologicalPager { } elseif ( $rev->getVisibility() && $user->isAllowed( 'deletedhistory' ) ) { // If revision was hidden from sysops, disable the link if ( !$rev->userCan( Revision::DELETED_RESTRICTED, $user ) ) { - $cdel = Linker::revDeleteLinkDisabled( false ); + $del = Linker::revDeleteLinkDisabled( false ); // Otherwise, show the link... } else { $query = array( 'type' => 'revision', - 'target' => $this->getTitle()->getPrefixedDbkey(), 'ids' => $rev->getId() ); + 'target' => $this->getTitle()->getPrefixedDBkey(), 'ids' => $rev->getId() ); $del .= Linker::revDeleteLink( $query, $rev->isDeleted( Revision::DELETED_RESTRICTED ), false ); } @@ -598,19 +605,22 @@ class HistoryPager extends ReverseChronologicalPager { $s .= ' ' . ChangesList::flag( 'minor' ); } - # Size is always public data - $prevSize = isset( $this->parentLens[$row->rev_parent_id] ) - ? $this->parentLens[$row->rev_parent_id] - : 0; - $sDiff = ChangesList::showCharacterDifference( $prevSize, $rev->getSize() ); - $fSize = Linker::formatRevisionSize($rev->getSize()); - $s .= ' <span class="mw-changeslist-separator">. .</span> ' . "$fSize $sDiff"; + # Sometimes rev_len isn't populated + if ( $rev->getSize() !== null ) { + # Size is always public data + $prevSize = isset( $this->parentLens[$row->rev_parent_id] ) + ? $this->parentLens[$row->rev_parent_id] + : 0; + $sDiff = ChangesList::showCharacterDifference( $prevSize, $rev->getSize() ); + $fSize = Linker::formatRevisionSize( $rev->getSize() ); + $s .= ' <span class="mw-changeslist-separator">. .</span> ' . "$fSize $sDiff"; + } # Text following the character difference is added just before running hooks $s2 = Linker::revComment( $rev, false, true ); if ( $notificationtimestamp && ( $row->rev_timestamp >= $notificationtimestamp ) ) { - $s2 .= ' <span class="updatedmarker">' . $this->msg( 'updatedmarker' )->escaped() . '</span>'; + $s2 .= ' <span class="updatedmarker">' . $this->msg( 'updatedmarker' )->escaped() . '</span>'; $classes[] = 'mw-history-line-updated'; } @@ -619,9 +629,12 @@ class HistoryPager extends ReverseChronologicalPager { # Rollback and undo links if ( $prevRev && $this->getTitle()->quickUserCan( 'edit', $user ) ) { if ( $latest && $this->getTitle()->quickUserCan( 'rollback', $user ) ) { - $this->preventClickjacking(); - $tools[] = '<span class="mw-rollback-link">' . - Linker::buildRollbackLink( $rev, $this->getContext() ) . '</span>'; + // Get a rollback link without the brackets + $rollbackLink = Linker::generateRollback( $rev, $this->getContext(), array( 'verify', 'noBrackets' ) ); + if ( $rollbackLink ) { + $this->preventClickjacking(); + $tools[] = $rollbackLink; + } } if ( !$rev->isDeleted( Revision::DELETED_TEXT ) @@ -644,6 +657,8 @@ class HistoryPager extends ReverseChronologicalPager { $tools[] = "<span class=\"mw-history-undo\">{$undolink}</span>"; } } + // Allow extension to add their own links here + wfRunHooks( 'HistoryRevisionTools', array( $rev, &$tools ) ); if ( $tools ) { $s2 .= ' '. $this->msg( 'parentheses' )->rawParams( $lang->pipeList( $tools ) )->escaped(); @@ -661,7 +676,7 @@ class HistoryPager extends ReverseChronologicalPager { $s .= ' <span class="mw-changeslist-separator">. .</span> ' . $s2; } - wfRunHooks( 'PageHistoryLineEnding', array( $this, &$row , &$s, &$classes ) ); + wfRunHooks( 'PageHistoryLineEnding', array( $this, &$row, &$s, &$classes ) ); $attribs = array(); if ( $classes ) { diff --git a/includes/actions/InfoAction.php b/includes/actions/InfoAction.php index ae550391..1e312d7a 100644 --- a/includes/actions/InfoAction.php +++ b/includes/actions/InfoAction.php @@ -22,6 +22,11 @@ * @ingroup Actions */ +/** + * Displays information about a page. + * + * @ingroup Actions + */ class InfoAction extends FormlessAction { /** * Returns the name of the action this object responds to. @@ -81,11 +86,11 @@ class InfoAction extends FormlessAction { // Hide "This page is a member of # hidden categories" explanation $content .= Html::element( 'style', array(), - '.mw-hiddenCategoriesExplanation { display: none; }' ); + '.mw-hiddenCategoriesExplanation { display: none; }' ) . "\n"; // Hide "Templates used on this page" explanation $content .= Html::element( 'style', array(), - '.mw-templatesUsedExplanation { display: none; }' ); + '.mw-templatesUsedExplanation { display: none; }' ) . "\n"; // Get page information $pageInfo = $this->pageInfo(); @@ -95,14 +100,14 @@ class InfoAction extends FormlessAction { // Render page information foreach ( $pageInfo as $header => $infoTable ) { - $content .= $this->makeHeader( $this->msg( "pageinfo-${header}" )->escaped() ); - $table = ''; + $content .= $this->makeHeader( $this->msg( "pageinfo-${header}" )->escaped() ) . "\n"; + $table = "\n"; foreach ( $infoTable as $infoRow ) { $name = ( $infoRow[0] instanceof Message ) ? $infoRow[0]->escaped() : $infoRow[0]; $value = ( $infoRow[1] instanceof Message ) ? $infoRow[1]->escaped() : $infoRow[1]; - $table = $this->addRow( $table, $name, $value ); + $table = $this->addRow( $table, $name, $value ) . "\n"; } - $content = $this->addTable( $content, $table ); + $content = $this->addTable( $content, $table ) . "\n"; } // Page footer @@ -125,17 +130,16 @@ class InfoAction extends FormlessAction { * @return string The HTML. */ protected function makeHeader( $header ) { - global $wgParser; - $spanAttribs = array( 'class' => 'mw-headline', 'id' => $wgParser->guessSectionNameFromWikiText( $header ) ); + $spanAttribs = array( 'class' => 'mw-headline', 'id' => Sanitizer::escapeId( $header ) ); return Html::rawElement( 'h2', array(), Html::element( 'span', $spanAttribs, $header ) ); } /** * Adds a row to a table that will be added to the content. * - * @param $table string The table that will be added to the content - * @param $name string The name of the row - * @param $value string The value of the row + * @param string $table The table that will be added to the content + * @param string $name The name of the row + * @param string $value The value of the row * @return string The table with the row added */ protected function addRow( $table, $name, $value ) { @@ -148,8 +152,8 @@ class InfoAction extends FormlessAction { /** * Adds a table to the content that will be added to the output. * - * @param $content string The content that will be added to the output - * @param $table string The table + * @param string $content The content that will be added to the output + * @param string $table The table * @return string The content with the table added */ protected function addTable( $content, $table ) { @@ -161,17 +165,25 @@ class InfoAction extends FormlessAction { * Returns page information in an easily-manipulated format. Array keys are used so extensions * may add additional information in arbitrary positions. Array values are arrays with one * element to be rendered as a header, arrays with two elements to be rendered as a table row. + * + * @return array */ protected function pageInfo() { - global $wgContLang, $wgRCMaxAge; + global $wgContLang, $wgRCMaxAge, $wgMemc, $wgUnwatchedPageThreshold, $wgPageInfoTransclusionLimit; $user = $this->getUser(); $lang = $this->getLanguage(); $title = $this->getTitle(); $id = $title->getArticleID(); - // Get page information that would be too "expensive" to retrieve by normal means - $pageCounts = self::pageCounts( $title, $user ); + $memcKey = wfMemcKey( 'infoaction', sha1( $title->getPrefixedText() ), $this->page->getLatest() ); + $pageCounts = $wgMemc->get( $memcKey ); + if ( $pageCounts === false ) { + // Get page information that would be too "expensive" to retrieve by normal means + $pageCounts = self::pageCounts( $title ); + + $wgMemc->set( $memcKey, $pageCounts ); + } // Get page properties $dbr = wfGetDB( DB_SLAVE ); @@ -201,6 +213,21 @@ class InfoAction extends FormlessAction { $this->msg( 'pageinfo-display-title' ), $displayTitle ); + // Is it a redirect? If so, where to? + if ( $title->isRedirect() ) { + $pageInfo['header-basic'][] = array( + $this->msg( 'pageinfo-redirectsto' ), + Linker::link( $this->page->getRedirectTarget() ) . + $this->msg( 'word-separator' )->text() . + $this->msg( 'parentheses', Linker::link( + $this->page->getRedirectTarget(), + $this->msg( 'pageinfo-redirectsto-info' )->escaped(), + array(), + array( 'action' => 'info' ) + ) )->text() + ); + } + // Default sort key $sortKey = $title->getCategorySortKey(); if ( !empty( $pageProperties['defaultsort'] ) ) { @@ -217,6 +244,12 @@ class InfoAction extends FormlessAction { // Page ID (number not localised, as it's a database ID) $pageInfo['header-basic'][] = array( $this->msg( 'pageinfo-article-id' ), $id ); + // Language in which the page content is (supposed to be) written + $pageLang = $title->getPageLanguage()->getCode(); + $pageInfo['header-basic'][] = array( $this->msg( 'pageinfo-language' ), + Language::fetchLanguageName( $pageLang, $lang->getCode() ) + . ' ' . $this->msg( 'parentheses', $pageLang ) ); + // Search engine status $pOutput = new ParserOutput(); if ( isset( $pageProperties['noindex'] ) ) { @@ -236,11 +269,20 @@ class InfoAction extends FormlessAction { ); } - if ( isset( $pageCounts['watchers'] ) ) { + if ( + $user->isAllowed( 'unwatchedpages' ) || + ( $wgUnwatchedPageThreshold !== false && + $pageCounts['watchers'] >= $wgUnwatchedPageThreshold ) + ) { // Number of page watchers $pageInfo['header-basic'][] = array( $this->msg( 'pageinfo-watchers' ), $lang->formatNum( $pageCounts['watchers'] ) ); + } elseif ( $wgUnwatchedPageThreshold !== false ) { + $pageInfo['header-basic'][] = array( + $this->msg( 'pageinfo-watchers' ), + $this->msg( 'pageinfo-few-watchers' )->numParams( $wgUnwatchedPageThreshold ) + ); } // Redirects to this page @@ -256,6 +298,14 @@ class InfoAction extends FormlessAction { ->numParams( count( $title->getRedirectsHere() ) ) ); + // Is it counted as a content page? + if ( $this->page->isCountable() ) { + $pageInfo['header-basic'][] = array( + $this->msg( 'pageinfo-contentpage' ), + $this->msg( 'pageinfo-contentpage-yes' ) + ); + } + // Subpages of this page, if subpages are enabled for the current NS if ( MWNamespace::hasSubpages( $title->getNamespace() ) ) { $prefixIndex = SpecialPage::getTitleFor( 'Prefixindex', $title->getPrefixedText() . '/' ); @@ -269,9 +319,51 @@ class InfoAction extends FormlessAction { ); } + if ( $title->inNamespace( NS_CATEGORY ) ) { + $category = Category::newFromTitle( $title ); + $pageInfo['category-info'] = array( + array( + $this->msg( 'pageinfo-category-pages' ), + $lang->formatNum( $category->getPageCount() ) + ), + array( + $this->msg( 'pageinfo-category-subcats' ), + $lang->formatNum( $category->getSubcatCount() ) + ), + array( + $this->msg( 'pageinfo-category-files' ), + $lang->formatNum( $category->getFileCount() ) + ) + ); + } + // Page protection $pageInfo['header-restrictions'] = array(); + // Is this page effected by the cascading protection of something which includes it? + if ( $title->isCascadeProtected() ) { + $cascadingFrom = ''; + $sources = $title->getCascadeProtectionSources(); // Array deferencing is in PHP 5.4 :( + + foreach ( $sources[0] as $sourceTitle ) { + $cascadingFrom .= Html::rawElement( 'li', array(), Linker::linkKnown( $sourceTitle ) ); + } + + $cascadingFrom = Html::rawElement( 'ul', array(), $cascadingFrom ); + $pageInfo['header-restrictions'][] = array( + $this->msg( 'pageinfo-protect-cascading-from' ), + $cascadingFrom + ); + } + + // Is out protection set to cascade to other pages? + if ( $title->areRestrictionsCascading() ) { + $pageInfo['header-restrictions'][] = array( + $this->msg( 'pageinfo-protect-cascading' ), + $this->msg( 'pageinfo-protect-cascading-yes' ) + ); + } + // Page protection foreach ( $title->getRestrictionTypes() as $restrictionType ) { $protectionLevel = implode( ', ', $title->getRestrictions( $restrictionType ) ); @@ -303,40 +395,64 @@ class InfoAction extends FormlessAction { $pageInfo['header-edits'] = array(); $firstRev = $this->page->getOldestRevision(); + $lastRev = $this->page->getRevision(); + $batch = new LinkBatch; + + if ( $firstRev ) { + $firstRevUser = $firstRev->getUserText( Revision::FOR_THIS_USER ); + if ( $firstRevUser !== '' ) { + $batch->add( NS_USER, $firstRevUser ); + $batch->add( NS_USER_TALK, $firstRevUser ); + } + } - // Page creator - $pageInfo['header-edits'][] = array( - $this->msg( 'pageinfo-firstuser' ), - Linker::revUserTools( $firstRev ) - ); + if ( $lastRev ) { + $lastRevUser = $lastRev->getUserText( Revision::FOR_THIS_USER ); + if ( $lastRevUser !== '' ) { + $batch->add( NS_USER, $lastRevUser ); + $batch->add( NS_USER_TALK, $lastRevUser ); + } + } - // Date of page creation - $pageInfo['header-edits'][] = array( - $this->msg( 'pageinfo-firsttime' ), - Linker::linkKnown( - $title, - $lang->userTimeAndDate( $firstRev->getTimestamp(), $user ), - array(), - array( 'oldid' => $firstRev->getId() ) - ) - ); + $batch->execute(); - // Latest editor - $pageInfo['header-edits'][] = array( - $this->msg( 'pageinfo-lastuser' ), - Linker::revUserTools( $this->page->getRevision() ) - ); + if ( $firstRev ) { + // Page creator + $pageInfo['header-edits'][] = array( + $this->msg( 'pageinfo-firstuser' ), + Linker::revUserTools( $firstRev ) + ); - // Date of latest edit - $pageInfo['header-edits'][] = array( - $this->msg( 'pageinfo-lasttime' ), - Linker::linkKnown( - $title, - $lang->userTimeAndDate( $this->page->getTimestamp(), $user ), - array(), - array( 'oldid' => $this->page->getLatest() ) - ) - ); + // Date of page creation + $pageInfo['header-edits'][] = array( + $this->msg( 'pageinfo-firsttime' ), + Linker::linkKnown( + $title, + $lang->userTimeAndDate( $firstRev->getTimestamp(), $user ), + array(), + array( 'oldid' => $firstRev->getId() ) + ) + ); + } + + if ( $lastRev ) { + // Latest editor + $pageInfo['header-edits'][] = array( + $this->msg( 'pageinfo-lastuser' ), + Linker::revUserTools( $lastRev ) + ); + + // Date of latest edit + $pageInfo['header-edits'][] = array( + $this->msg( 'pageinfo-lasttime' ), + Linker::linkKnown( + $title, + $lang->userTimeAndDate( $this->page->getTimestamp(), $user ), + array(), + array( 'oldid' => $this->page->getLatest() ) + ) + ); + } // Total number of edits $pageInfo['header-edits'][] = array( @@ -377,11 +493,17 @@ class InfoAction extends FormlessAction { $localizedList = Html::rawElement( 'ul', array(), implode( '', $listItems ) ); $hiddenCategories = $this->page->getHiddenCategories(); - $transcludedTemplates = $title->getTemplateLinksFrom(); - if ( count( $listItems ) > 0 - || count( $hiddenCategories ) > 0 - || count( $transcludedTemplates ) > 0 ) { + if ( + count( $listItems ) > 0 || + count( $hiddenCategories ) > 0 || + $pageCounts['transclusion']['from'] > 0 || + $pageCounts['transclusion']['to'] > 0 + ) { + $options = array( 'LIMIT' => $wgPageInfoTransclusionLimit ); + $transcludedTemplates = $title->getTemplateLinksFrom( $options ); + $transcludedTargets = $title->getTemplateLinksTo( $options ); + // Page properties $pageInfo['header-properties'] = array(); @@ -403,11 +525,44 @@ class InfoAction extends FormlessAction { } // Transcluded templates - if ( count( $transcludedTemplates ) > 0 ) { + if ( $pageCounts['transclusion']['from'] > 0 ) { + if ( $pageCounts['transclusion']['from'] > count( $transcludedTemplates ) ) { + $more = $this->msg( 'morenotlisted' )->escaped(); + } else { + $more = null; + } + $pageInfo['header-properties'][] = array( $this->msg( 'pageinfo-templates' ) - ->numParams( count( $transcludedTemplates ) ), - Linker::formatTemplates( $transcludedTemplates ) + ->numParams( $pageCounts['transclusion']['from'] ), + Linker::formatTemplates( + $transcludedTemplates, + false, + false, + $more ) + ); + } + + if ( $pageCounts['transclusion']['to'] > 0 ) { + if ( $pageCounts['transclusion']['to'] > count( $transcludedTargets ) ) { + $more = Linker::link( + $whatLinksHere, + $this->msg( 'moredotdotdot' )->escaped(), + array(), + array( 'hidelinks' => 1, 'hideredirs' => 1 ) + ); + } else { + $more = null; + } + + $pageInfo['header-properties'][] = array( + $this->msg( 'pageinfo-transclusions' ) + ->numParams( $pageCounts['transclusion']['to'] ), + Linker::formatTemplates( + $transcludedTargets, + false, + false, + $more ) ); } } @@ -418,11 +573,10 @@ class InfoAction extends FormlessAction { /** * Returns page counts that would be too "expensive" to retrieve by normal means. * - * @param $title Title object - * @param $user User object + * @param Title $title Title to get counts for * @return array */ - protected static function pageCounts( $title, $user ) { + protected static function pageCounts( Title $title ) { global $wgRCMaxAge, $wgDisableCounters; wfProfileIn( __METHOD__ ); @@ -442,19 +596,17 @@ class InfoAction extends FormlessAction { $result['views'] = $views; } - if ( $user->isAllowed( 'unwatchedpages' ) ) { - // Number of page watchers - $watchers = (int) $dbr->selectField( - 'watchlist', - 'COUNT(*)', - array( - 'wl_namespace' => $title->getNamespace(), - 'wl_title' => $title->getDBkey(), - ), - __METHOD__ - ); - $result['watchers'] = $watchers; - } + // Number of page watchers + $watchers = (int) $dbr->selectField( + 'watchlist', + 'COUNT(*)', + array( + 'wl_namespace' => $title->getNamespace(), + 'wl_title' => $title->getDBkey(), + ), + __METHOD__ + ); + $result['watchers'] = $watchers; // Total number of edits $edits = (int) $dbr->selectField( @@ -482,8 +634,8 @@ class InfoAction extends FormlessAction { 'revision', 'COUNT(rev_page)', array( - 'rev_page' => $id , - "rev_timestamp >= $threshold" + 'rev_page' => $id, + "rev_timestamp >= " . $dbr->addQuotes( $threshold ) ), __METHOD__ ); @@ -495,7 +647,7 @@ class InfoAction extends FormlessAction { 'COUNT(DISTINCT rev_user_text)', array( 'rev_page' => $id, - "rev_timestamp >= $threshold" + "rev_timestamp >= " . $dbr->addQuotes( $threshold ) ), __METHOD__ ); @@ -528,12 +680,30 @@ class InfoAction extends FormlessAction { + $result['subpages']['nonredirects']; } + // Counts for the number of transclusion links (to/from) + $result['transclusion']['to'] = (int) $dbr->selectField( + 'templatelinks', + 'COUNT(tl_from)', + array( + 'tl_namespace' => $title->getNamespace(), + 'tl_title' => $title->getDBkey() + ), + __METHOD__ + ); + + $result['transclusion']['from'] = (int) $dbr->selectField( + 'templatelinks', + 'COUNT(*)', + array( 'tl_from' => $title->getArticleID() ), + __METHOD__ + ); + wfProfileOut( __METHOD__ ); return $result; } /** - * Returns the name that goes in the <h1> page title. + * Returns the name that goes in the "<h1>" page title. * * @return string */ @@ -604,7 +774,7 @@ class InfoAction extends FormlessAction { } /** - * Returns the description that goes below the <h1> tag. + * Returns the description that goes below the "<h1>" tag. * * @return string */ diff --git a/includes/actions/MarkpatrolledAction.php b/includes/actions/MarkpatrolledAction.php index ae9223f4..ff6cf13a 100644 --- a/includes/actions/MarkpatrolledAction.php +++ b/includes/actions/MarkpatrolledAction.php @@ -22,6 +22,11 @@ * @ingroup Actions */ +/** + * Mark a revision as patrolled on a page + * + * @ingroup Actions + */ class MarkpatrolledAction extends FormlessAction { public function getName() { diff --git a/includes/actions/ProtectAction.php b/includes/actions/ProtectAction.php index f053ede7..ec6648e2 100644 --- a/includes/actions/ProtectAction.php +++ b/includes/actions/ProtectAction.php @@ -23,17 +23,24 @@ * @author Timo Tijhof */ +/** + * Handle page protection + * + * This is a wrapper that will call Article::protect(). + * + * @ingroup Actions + */ class ProtectAction extends FormlessAction { public function getName() { return 'protect'; } - public function onView(){ + public function onView() { return null; } - public function show(){ + public function show() { $this->page->protect(); @@ -41,13 +48,20 @@ class ProtectAction extends FormlessAction { } +/** + * Handle page unprotection + * + * This is a wrapper that will call Article::unprotect(). + * + * @ingroup Actions + */ class UnprotectAction extends ProtectAction { public function getName() { return 'unprotect'; } - public function show(){ + public function show() { $this->page->unprotect(); diff --git a/includes/actions/PurgeAction.php b/includes/actions/PurgeAction.php index cd58889d..00bb961d 100644 --- a/includes/actions/PurgeAction.php +++ b/includes/actions/PurgeAction.php @@ -1,8 +1,6 @@ <?php /** - * Formats credits for articles - * - * Copyright 2004, Evan Prodromou <evan@wikitravel.org>. + * User-requested page cache purging. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,9 +18,16 @@ * * @file * @ingroup Actions - * @author <evan@wikitravel.org> */ +/** + * User-requested page cache purging. + * + * For users with 'purge', this will directly trigger the cache purging and + * for users without that right, it will show a confirmation form. + * + * @ingroup Actions + */ class PurgeAction extends FormAction { private $redirectParams; @@ -62,7 +67,7 @@ class PurgeAction extends FormAction { $this->checkCanExecute( $this->getUser() ); if ( $this->getUser()->isAllowed( 'purge' ) ) { - $this->redirectParams = wfArrayToCGI( array_diff_key( + $this->redirectParams = wfArrayToCgi( array_diff_key( $this->getRequest()->getQueryValues(), array( 'title' => null, 'action' => null ) ) ); diff --git a/includes/actions/RawAction.php b/includes/actions/RawAction.php index 174ca3f8..d1d457c0 100644 --- a/includes/actions/RawAction.php +++ b/includes/actions/RawAction.php @@ -29,6 +29,8 @@ /** * A simple method to retrieve the plain source of an article, * using "action=raw" in the GET request string. + * + * @ingroup Actions */ class RawAction extends FormlessAction { private $mGen; @@ -46,7 +48,7 @@ class RawAction extends FormlessAction { } function onView() { - global $wgGroupPermissions, $wgSquidMaxage, $wgForcedRawSMaxage, $wgJsMimeType; + global $wgSquidMaxage, $wgForcedRawSMaxage, $wgJsMimeType; $this->getOutput()->disable(); $request = $this->getRequest(); @@ -91,7 +93,7 @@ class RawAction extends FormlessAction { $response->header( 'Content-type: ' . $contentType . '; charset=UTF-8' ); # Output may contain user-specific data; # vary generated content for open sessions on private wikis - $privateCache = !$wgGroupPermissions['*']['read'] && ( $smaxage == 0 || session_id() != '' ); + $privateCache = !User::groupHasPermission( '*', 'read' ) && ( $smaxage == 0 || session_id() != '' ); # allow the client to cache this for 24 hours $mode = $privateCache ? 'private' : 'public'; $response->header( 'Cache-Control: ' . $mode . ', s-maxage=' . $smaxage . ', max-age=' . $maxage ); @@ -148,10 +150,29 @@ class RawAction extends FormlessAction { $request->response()->header( "Last-modified: $lastmod" ); // Public-only due to cache headers - $text = $rev->getText(); - $section = $request->getIntOrNull( 'section' ); - if ( $section !== null ) { - $text = $wgParser->getSection( $text, $section ); + $content = $rev->getContent(); + + if ( $content === null ) { + // revision not found (or suppressed) + $text = false; + } elseif ( !$content instanceof TextContent ) { + // non-text content + wfHttpError( 415, "Unsupported Media Type", "The requested page uses the content model `" + . $content->getModel() . "` which is not supported via this interface." ); + die(); + } else { + // want a section? + $section = $request->getIntOrNull( 'section' ); + if ( $section !== null ) { + $content = $content->getSection( $section ); + } + + if ( $content === null || $content === false ) { + // section not found (or section not supported, e.g. for JS and CSS) + $text = false; + } else { + $text = $content->getNativeData(); + } } } } @@ -185,7 +206,7 @@ class RawAction extends FormlessAction { $oldid = $this->page->getLatest(); } $prev = $this->getTitle()->getPreviousRevisionId( $oldid ); - $oldid = $prev ? $prev : -1 ; + $oldid = $prev ? $prev : -1; break; case 'cur': $oldid = 0; diff --git a/includes/actions/RenderAction.php b/includes/actions/RenderAction.php index 80af79cc..3d244fb3 100644 --- a/includes/actions/RenderAction.php +++ b/includes/actions/RenderAction.php @@ -23,17 +23,24 @@ * @author Timo Tijhof */ +/** + * Handle action=render + * + * This is a wrapper that will call Article::render(). + * + * @ingroup Actions + */ class RenderAction extends FormlessAction { public function getName() { return 'render'; } - public function onView(){ + public function onView() { return null; } - public function show(){ + public function show() { $this->page->render(); diff --git a/includes/actions/RevertAction.php b/includes/actions/RevertAction.php index 77434384..a5fc4e17 100644 --- a/includes/actions/RevertAction.php +++ b/includes/actions/RevertAction.php @@ -115,7 +115,7 @@ class RevertFileAction extends FormAction { $source = $this->page->getFile()->getArchiveVirtualUrl( $this->getRequest()->getText( 'oldimage' ) ); $comment = $data['comment']; // TODO: Preserve file properties from database instead of reloading from file - return $this->page->getFile()->upload( $source, $comment, $comment ); + return $this->page->getFile()->upload( $source, $comment, $comment, 0, false, false, $this->getUser() ); } public function onSuccess() { @@ -124,7 +124,7 @@ class RevertFileAction extends FormAction { $lang = $this->getLanguage(); $userDate = $lang->userDate( $timestamp, $user ); $userTime = $lang->userTime( $timestamp, $user ); - + $this->getOutput()->addWikiMsg( 'filerevert-success', $this->getTitle()->getText(), $userDate, $userTime, wfExpandUrl( $this->page->getFile()->getArchiveUrl( $this->getRequest()->getText( 'oldimage' ) ), @@ -136,7 +136,7 @@ class RevertFileAction extends FormAction { protected function getPageTitle() { return $this->msg( 'filerevert', $this->getTitle()->getText() ); } - + protected function getDescription() { $this->getOutput()->addBacklinkSubtitle( $this->getTitle() ); return ''; diff --git a/includes/actions/RevisiondeleteAction.php b/includes/actions/RevisiondeleteAction.php index 14da2fcf..2949fa95 100644 --- a/includes/actions/RevisiondeleteAction.php +++ b/includes/actions/RevisiondeleteAction.php @@ -23,6 +23,11 @@ * @author Alexandre Emsenhuber */ +/** + * An action that just pass the request to Special:RevisionDelete + * + * @ingroup Actions + */ class RevisiondeleteAction extends FormlessAction { public function getName() { diff --git a/includes/actions/RollbackAction.php b/includes/actions/RollbackAction.php index 0d9a9027..81bad9da 100644 --- a/includes/actions/RollbackAction.php +++ b/includes/actions/RollbackAction.php @@ -71,45 +71,32 @@ class RollbackAction extends FormlessAction { return; } - # Display permissions errors before read-only message -- there's no - # point in misleading the user into thinking the inability to rollback - # is only temporary. - if ( !empty( $result ) && $result !== array( array( 'readonlytext' ) ) ) { - # array_diff is completely broken for arrays of arrays, sigh. - # Remove any 'readonlytext' error manually. - $out = array(); - foreach ( $result as $error ) { - if ( $error != array( 'readonlytext' ) ) { - $out [] = $error; - } - } - throw new PermissionsError( 'rollback', $out ); - } + #NOTE: Permission errors already handled by Action::checkExecute. if ( $result == array( array( 'readonlytext' ) ) ) { throw new ReadOnlyError; } + #XXX: Would be nice if ErrorPageError could take multiple errors, and/or a status object. + # Right now, we only show the first error + foreach ( $result as $error ) { + throw new ErrorPageError( 'rollbackfailed', $error[0], array_slice( $error, 1 ) ); + } + $current = $details['current']; $target = $details['target']; $newId = $details['newid']; $this->getOutput()->setPageTitle( $this->msg( 'actioncomplete' ) ); $this->getOutput()->setRobotPolicy( 'noindex,nofollow' ); - if ( $current->getUserText() === '' ) { - $old = $this->msg( 'rev-deleted-user' )->escaped(); - } else { - $old = Linker::userLink( $current->getUser(), $current->getUserText() ) - . Linker::userToolLinks( $current->getUser(), $current->getUserText() ); - } - - $new = Linker::userLink( $target->getUser(), $target->getUserText() ) - . Linker::userToolLinks( $target->getUser(), $target->getUserText() ); + $old = Linker::revUserTools( $current ); + $new = Linker::revUserTools( $target ); $this->getOutput()->addHTML( $this->msg( 'rollback-success' )->rawParams( $old, $new )->parseAsBlock() ); $this->getOutput()->returnToMain( false, $this->getTitle() ); if ( !$request->getBool( 'hidediff', false ) && !$this->getUser()->getBoolOption( 'norollbackdiff', false ) ) { - $de = new DifferenceEngine( $this->getContext(), $current->getId(), $newId, false, true ); + $contentHandler = $current->getContentHandler(); + $de = $contentHandler->createDifferenceEngine( $this->getContext(), $current->getId(), $newId, false, true ); $de->showDiff( '', '' ); } } diff --git a/includes/actions/ViewAction.php b/includes/actions/ViewAction.php index d57585ee..e227197d 100644 --- a/includes/actions/ViewAction.php +++ b/includes/actions/ViewAction.php @@ -23,17 +23,24 @@ * @author Timo Tijhof */ +/** + * An action that views article content + * + * This is a wrapper that will call Article::render(). + * + * @ingroup Actions + */ class ViewAction extends FormlessAction { public function getName() { return 'view'; } - public function onView(){ + public function onView() { return null; } - public function show(){ + public function show() { $this->page->view(); } diff --git a/includes/actions/WatchAction.php b/includes/actions/WatchAction.php index e2636452..ae5f76c6 100644 --- a/includes/actions/WatchAction.php +++ b/includes/actions/WatchAction.php @@ -20,6 +20,11 @@ * @ingroup Actions */ +/** + * Page addition to a user's watchlist + * + * @ingroup Actions + */ class WatchAction extends FormAction { public function getName() { @@ -148,6 +153,11 @@ class WatchAction extends FormAction { } } +/** + * Page removal from a user's watchlist + * + * @ingroup Actions + */ class UnwatchAction extends WatchAction { public function getName() { |