From: daniel Date: Mon, 14 May 2012 21:24:18 +0000 (+0200) Subject: merged latest master X-Git-Tag: 1.31.0-rc.0~22097^2^2~172 X-Git-Url: http://git.cyclocoop.org/%22%20.%20generer_url_ecrire%28%22auteur_infos%22%2C%22id_auteur=%24connect_id_auteur%22%29%20.%20%22?a=commitdiff_plain;h=01f36b721f6fb60000b625fd681993d50ebd0e40;p=lhc%2Fweb%2Fwiklou.git merged latest master --- 01f36b721f6fb60000b625fd681993d50ebd0e40 diff --cc includes/Article.php index c766afa740,6a3e41cb3c..00b2107c3e --- a/includes/Article.php +++ b/includes/Article.php @@@ -196,34 -188,9 +196,33 @@@ class Article extends Page * This function has side effects! Do not use this function if you * only want the real revision text if any. * + * @deprecated in 1.WD; use getContentObject() instead + * - * @return string The text of this revision + * @return string Return the text of this revision */ public function getContent() { + wfDeprecated( __METHOD__, '1.WD' ); + $content = $this->getContentObject(); + return ContentHandler::getContentText( $content ); + } + + /** + * Returns a Content object representing the pages effective display content, + * not necessarily the revision's content! + * + * Note that getContent/loadContent do not follow redirects anymore. + * If you need to fetch redirectable content easily, try + * the shortcut in WikiPage::getRedirectTarget() + * + * This function has side effects! Do not use this function if you + * only want the real revision text if any. + * - * @return Content ++ * @return Content Return the content of this revision + * + * @since 1.WD + */ + protected function getContentObject() { + global $wgUser; - wfProfileIn( __METHOD__ ); if ( $this->mPage->getID() === 0 ) { @@@ -641,10 -571,10 +640,10 @@@ if ( $rt ) { wfDebug( __METHOD__ . ": showing redirect=no page\n" ); # Viewing a redirect page (e.g. with parameter redirect=no) - $wgOut->addHTML( $this->viewRedirect( $rt ) ); + $outputPage->addHTML( $this->viewRedirect( $rt ) ); # Parse just to get categories, displaytitle, etc. - $this->mParserOutput = $wgParser->parse( $text, $this->getTitle(), $parserOptions ); - $outputPage->addParserOutputNoText( $this->mParserOutput ); + $this->mParserOutput = $content->getParserOutput( $this->getContext(), $oldid, $parserOptions, false ); + $wgOut->addParserOutputNoText( $this->mParserOutput ); $outputDone = true; } } @@@ -740,18 -668,16 +738,18 @@@ * Article::view() only, other callers should use the DifferenceEngine class. */ public function showDiffPage() { - global $wgRequest, $wgUser; - - $diff = $wgRequest->getVal( 'diff' ); - $rcid = $wgRequest->getVal( 'rcid' ); - $diffOnly = $wgRequest->getBool( 'diffonly', $wgUser->getOption( 'diffonly' ) ); - $purge = $wgRequest->getVal( 'action' ) == 'purge'; - $unhide = $wgRequest->getInt( 'unhide' ) == 1; + $request = $this->getContext()->getRequest(); + $user = $this->getContext()->getUser(); + $diff = $request->getVal( 'diff' ); + $rcid = $request->getVal( 'rcid' ); + $diffOnly = $request->getBool( 'diffonly', $user->getOption( 'diffonly' ) ); + $purge = $request->getVal( 'action' ) == 'purge'; + $unhide = $request->getInt( 'unhide' ) == 1; $oldid = $this->getOldID(); - $de = new DifferenceEngine( $this->getContext(), $oldid, $diff, $rcid, $purge, $unhide ); + $contentHandler = ContentHandler::getForTitle( $this->getTitle() ); + $de = $contentHandler->createDifferenceEngine( $this->getContext(), $oldid, $diff, $rcid, $purge, $unhide ); + // DifferenceEngine directly fetched the revision: $this->mRevIdFetched = $de->mNewid; $de->showDiffPage( $diffOnly ); diff --cc includes/EditPage.php index b2c8ad4d17,5979ed4d45..72341b4f90 --- a/includes/EditPage.php +++ b/includes/EditPage.php @@@ -1284,145 -1179,175 +1284,146 @@@ class EditPage wfProfileOut( __METHOD__ . '-checks' ); - # If article is new, insert it. - $aid = $this->mTitle->getArticleID( Title::GAID_FOR_UPDATE ); - $new = ( $aid == 0 ); + // Use SELECT FOR UPDATE here to avoid transaction collision in + // WikiPage::updateRevisionOn() and ending in the self::AS_END case. + $this->mArticle->loadPageData( 'forupdate' ); + $new = !$this->mArticle->exists(); - if ( $new ) { - // Late check for create permission, just in case *PARANOIA* - if ( !$this->mTitle->userCan( 'create' ) ) { - $status->fatal( 'nocreatetext' ); - $status->value = self::AS_NO_CREATE_PERMISSION; - wfDebug( __METHOD__ . ": no create permission\n" ); - wfProfileOut( __METHOD__ ); - return $status; - } + try { + if ( $new ) { + // Late check for create permission, just in case *PARANOIA* + if ( !$this->mTitle->userCan( 'create' ) ) { + $status->fatal( 'nocreatetext' ); + $status->value = self::AS_NO_CREATE_PERMISSION; + wfDebug( __METHOD__ . ": no create permission\n" ); + wfProfileOut( __METHOD__ ); + return $status; + } - # Don't save a new article if it's blank. - if ( $this->textbox1 == '' ) { - $status->setResult( false, self::AS_BLANK_ARTICLE ); - wfProfileOut( __METHOD__ ); - return $status; - } + # Don't save a new article if it's blank. + if ( $this->textbox1 == '' ) { + $status->setResult( false, self::AS_BLANK_ARTICLE ); + wfProfileOut( __METHOD__ ); + return $status; + } - // Run post-section-merge edit filter - if ( !wfRunHooks( 'EditFilterMerged', array( $this, $this->textbox1, &$this->hookError, $this->summary ) ) ) { - # Error messages etc. could be handled within the hook... - $status->fatal( 'hookaborted' ); - $status->value = self::AS_HOOK_ERROR; - wfProfileOut( __METHOD__ ); - return $status; - } elseif ( $this->hookError != '' ) { - # ...or the hook could be expecting us to produce an error - $status->fatal( 'hookaborted' ); - $status->value = self::AS_HOOK_ERROR_EXPECTED; - wfProfileOut( __METHOD__ ); - return $status; - } + // Run post-section-merge edit filter + if ( !wfRunHooks( 'EditFilterMerged', array( $this, $this->textbox1, &$this->hookError, $this->summary ) ) ) { + # Error messages etc. could be handled within the hook... + $status->fatal( 'hookaborted' ); + $status->value = self::AS_HOOK_ERROR; + wfProfileOut( __METHOD__ ); + return $status; + } elseif ( $this->hookError != '' ) { + # ...or the hook could be expecting us to produce an error + $status->fatal( 'hookaborted' ); + $status->value = self::AS_HOOK_ERROR_EXPECTED; + wfProfileOut( __METHOD__ ); + return $status; + } - $text = $this->textbox1; - $result['sectionanchor'] = ''; - if ( $this->section == 'new' ) { - if ( $this->sectiontitle !== '' ) { - // Insert the section title above the content. - $text = wfMsgForContent( 'newsectionheaderdefaultlevel', $this->sectiontitle ) . "\n\n" . $text; - - // Jump to the new section - $result['sectionanchor'] = $wgParser->guessLegacySectionNameFromWikiText( $this->sectiontitle ); - - // If no edit summary was specified, create one automatically from the section - // title and have it link to the new section. Otherwise, respect the summary as - // passed. - if ( $this->summary === '' ) { - $cleanSectionTitle = $wgParser->stripSectionName( $this->sectiontitle ); - $this->summary = wfMsgForContent( 'newsectionsummary', $cleanSectionTitle ); - } - } elseif ( $this->summary !== '' ) { - // Insert the section title above the content. - $text = wfMsgForContent( 'newsectionheaderdefaultlevel', $this->summary ) . "\n\n" . $text; + $content = ContentHandler::makeContent( $this->textbox1, $this->getTitle(), $this->content_model, $this->content_format ); - // Jump to the new section - $result['sectionanchor'] = $wgParser->guessLegacySectionNameFromWikiText( $this->summary ); + $result['sectionanchor'] = ''; + if ( $this->section == 'new' ) { + if ( $this->sectiontitle !== '' ) { + // Insert the section title above the content. + $content = $content->addSectionHeader( $this->sectiontitle ); + + // Jump to the new section + $result['sectionanchor'] = $wgParser->guessLegacySectionNameFromWikiText( $this->sectiontitle ); + + // If no edit summary was specified, create one automatically from the section + // title and have it link to the new section. Otherwise, respect the summary as + // passed. + if ( $this->summary === '' ) { + $cleanSectionTitle = $wgParser->stripSectionName( $this->sectiontitle ); + $this->summary = wfMsgForContent( 'newsectionsummary', $cleanSectionTitle ); + } + } elseif ( $this->summary !== '' ) { + // Insert the section title above the content. + $content = $content->addSectionHeader( $this->sectiontitle ); - // Create a link to the new section from the edit summary. - $cleanSummary = $wgParser->stripSectionName( $this->summary ); - $this->summary = wfMsgForContent( 'newsectionsummary', $cleanSummary ); + // Jump to the new section + $result['sectionanchor'] = $wgParser->guessLegacySectionNameFromWikiText( $this->summary ); + + // Create a link to the new section from the edit summary. + $cleanSummary = $wgParser->stripSectionName( $this->summary ); + $this->summary = wfMsgForContent( 'newsectionsummary', $cleanSummary ); + } } - } - $status->value = self::AS_SUCCESS_NEW_ARTICLE; + $status->value = self::AS_SUCCESS_NEW_ARTICLE; - } else { + } else { # not $new - # Article exists. Check for edit conflict. - $timestamp = $this->mArticle->getTimestamp(); - wfDebug( "timestamp: {$timestamp}, edittime: {$this->edittime}\n" ); + # Article exists. Check for edit conflict. - if ( $timestamp != $this->edittime ) { - $this->isConflict = true; - if ( $this->section == 'new' ) { - if ( $this->mArticle->getUserText() == $wgUser->getName() && - $this->mArticle->getComment() == $this->summary ) { - // Probably a duplicate submission of a new comment. - // This can happen when squid resends a request after - // a timeout but the first one actually went through. - wfDebug( __METHOD__ . ": duplicate new section submission; trigger edit conflict!\n" ); - } else { - // New comment; suppress conflict. + $this->mArticle->clear(); # Force reload of dates, etc. + $timestamp = $this->mArticle->getTimestamp(); + + wfDebug( "timestamp: {$timestamp}, edittime: {$this->edittime}\n" ); + + if ( $timestamp != $this->edittime ) { + $this->isConflict = true; + if ( $this->section == 'new' ) { + if ( $this->mArticle->getUserText() == $wgUser->getName() && + $this->mArticle->getComment() == $this->summary ) { + // Probably a duplicate submission of a new comment. + // This can happen when squid resends a request after + // a timeout but the first one actually went through. + wfDebug( __METHOD__ . ": duplicate new section submission; trigger edit conflict!\n" ); + } else { + // New comment; suppress conflict. + $this->isConflict = false; + wfDebug( __METHOD__ . ": conflict suppressed; new section\n" ); + } + } elseif ( $this->section == '' && $this->userWasLastToEdit( $wgUser->getId(), $this->edittime ) ) { + # Suppress edit conflict with self, except for section edits where merging is required. + wfDebug( __METHOD__ . ": Suppressing edit conflict, same user.\n" ); $this->isConflict = false; - wfDebug( __METHOD__ . ": conflict suppressed; new section\n" ); } - } elseif ( $this->section == '' && $this->userWasLastToEdit( $wgUser->getId(), $this->edittime ) ) { - # Suppress edit conflict with self, except for section edits where merging is required. - wfDebug( __METHOD__ . ": Suppressing edit conflict, same user.\n" ); - $this->isConflict = false; } - } - // If sectiontitle is set, use it, otherwise use the summary as the section title (for - // backwards compatibility with old forms/bots). - if ( $this->sectiontitle !== '' ) { - $sectionTitle = $this->sectiontitle; - } else { - $sectionTitle = $this->summary; - } - - if ( $this->isConflict ) { - wfDebug( __METHOD__ . ": conflict! getting section '$this->section' for time '$this->edittime' (article time '{$timestamp}')\n" ); - $text = $this->mArticle->replaceSection( $this->section, $this->textbox1, $sectionTitle, $this->edittime ); - } else { - wfDebug( __METHOD__ . ": getting section '$this->section'\n" ); - $text = $this->mArticle->replaceSection( $this->section, $this->textbox1, $sectionTitle ); - } - if ( is_null( $text ) ) { - wfDebug( __METHOD__ . ": activating conflict; section replace failed.\n" ); - $this->isConflict = true; - $text = $this->textbox1; // do not try to merge here! - } elseif ( $this->isConflict ) { - # Attempt merge - if ( $this->mergeChangesInto( $text ) ) { - // Successful merge! Maybe we should tell the user the good news? - $this->isConflict = false; - wfDebug( __METHOD__ . ": Suppressing edit conflict, successful merge.\n" ); + // If sectiontitle is set, use it, otherwise use the summary as the section title (for + // backwards compatibility with old forms/bots). + if ( $this->sectiontitle !== '' ) { + $sectionTitle = $this->sectiontitle; } else { - $this->section = ''; - $this->textbox1 = $text; - wfDebug( __METHOD__ . ": Keeping edit conflict, failed merge.\n" ); + $sectionTitle = $this->summary; } - } - if ( $this->isConflict ) { - $status->setResult( false, self::AS_CONFLICT_DETECTED ); - wfProfileOut( __METHOD__ ); - return $status; - } + $textbox_content = ContentHandler::makeContent( $this->textbox1, $this->getTitle(), $this->content_model, $this->content_format ); + $content = null; - // Run post-section-merge edit filter - if ( !wfRunHooks( 'EditFilterMerged', array( $this, $text, &$this->hookError, $this->summary ) ) ) { - # Error messages etc. could be handled within the hook... - $status->fatal( 'hookaborted' ); - $status->value = self::AS_HOOK_ERROR; - wfProfileOut( __METHOD__ ); - return $status; - } elseif ( $this->hookError != '' ) { - # ...or the hook could be expecting us to produce an error - $status->fatal( 'hookaborted' ); - $status->value = self::AS_HOOK_ERROR_EXPECTED; - wfProfileOut( __METHOD__ ); - return $status; - } + if ( $this->isConflict ) { + wfDebug( __METHOD__ . ": conflict! getting section '$this->section' for time '$this->edittime' (article time '{$timestamp}')\n" ); + $content = $this->mArticle->replaceSectionContent( $this->section, $textbox_content, $sectionTitle, $this->edittime ); + } else { + wfDebug( __METHOD__ . ": getting section '$this->section'\n" ); + $content = $this->mArticle->replaceSectionContent( $this->section, $textbox_content, $sectionTitle ); + } - # Handle the user preference to force summaries here, but not for null edits - if ( $this->section != 'new' && !$this->allowBlankSummary - && $this->getOriginalContent() != $text - && !Title::newFromRedirect( $text ) ) # check if it's not a redirect - { - if ( md5( $this->summary ) == $this->autoSumm ) { - $this->missingSummary = true; - $status->fatal( 'missingsummary' ); - $status->value = self::AS_SUMMARY_NEEDED; - wfProfileOut( __METHOD__ ); - return $status; + if ( is_null( $content ) ) { + wfDebug( __METHOD__ . ": activating conflict; section replace failed.\n" ); + $this->isConflict = true; + $content = $textbox_content; // do not try to merge here! + } elseif ( $this->isConflict ) { + # Attempt merge + if ( $this->mergeChangesIntoContent( $textbox_content ) ) { + // Successful merge! Maybe we should tell the user the good news? + $this->isConflict = false; + $content = $textbox_content; + wfDebug( __METHOD__ . ": Suppressing edit conflict, successful merge.\n" ); + } else { + $this->section = ''; + #$this->textbox1 = $text; #redundant, nothing to do here? + wfDebug( __METHOD__ . ": Keeping edit conflict, failed merge.\n" ); + } } - } - # And a similar thing for new sections - if ( $this->section == 'new' && !$this->allowBlankSummary ) { - if ( trim( $this->summary ) == '' ) { - $this->missingSummary = true; - $status->fatal( 'missingsummary' ); // or 'missingcommentheader' if $section == 'new'. Blegh - $status->value = self::AS_SUMMARY_NEEDED; + if ( $this->isConflict ) { + $status->setResult( false, self::AS_CONFLICT_DETECTED ); wfProfileOut( __METHOD__ ); return $status; } diff --cc includes/Export.php index e3909ceed0,ea6fd9421b..5ecf4af05c --- a/includes/Export.php +++ b/includes/Export.php @@@ -591,19 -641,9 +641,19 @@@ class XmlDumpWriter if ( $row->rev_deleted & Revision::DELETED_COMMENT ) { $out .= " " . Xml::element( 'comment', array( 'deleted' => 'deleted' ) ) . "\n"; } elseif ( $row->rev_comment != '' ) { - $out .= " " . Xml::elementClean( 'comment', null, strval( $row->rev_comment ) ) . "\n"; + $out .= " " . Xml::elementClean( 'comment', array(), strval( $row->rev_comment ) ) . "\n"; } + if ( isset( $row->rev_content_model ) && !is_null( $row->rev_content_model ) ) { + $name = ContentHandler::getContentModelName( $row->rev_content_model ); + $out .= " " . Xml::element('model', array( 'name' => $name ), strval( $row->rev_content_model ) ) . "\n"; + } + + if ( isset( $row->rev_content_format ) && !is_null( $row->rev_content_format ) ) { + $mime = ContentHandler::getContentFormatMimeType( $row->rev_content_format ); + $out .= " " . Xml::element('format', array( 'mime' => $mime ), strval( $row->rev_content_format ) ) . "\n"; + } + $text = ''; if ( $row->rev_deleted & Revision::DELETED_TEXT ) { $out .= " " . Xml::element( 'text', array( 'deleted' => 'deleted' ) ) . "\n"; diff --cc includes/ImagePage.php index ad90f1453e,fc3bdff5e8..43eca70301 --- a/includes/ImagePage.php +++ b/includes/ImagePage.php @@@ -132,17 -154,15 +154,17 @@@ class ImagePage extends Article # NS_FILE is in the user language, but this section (the actual wikitext) # should be in page content language $pageLang = $this->getTitle()->getPageLanguage(); - $wgOut->addHTML( Xml::openElement( 'div', array( 'id' => 'mw-imagepage-content', + $out->addHTML( Xml::openElement( 'div', array( 'id' => 'mw-imagepage-content', 'lang' => $pageLang->getCode(), 'dir' => $pageLang->getDir(), 'class' => 'mw-content-'.$pageLang->getDir() ) ) ); - parent::view(); - $out->addHTML( Xml::closeElement( 'div' ) ); + + parent::view(); #FIXME: use ContentHandler::makeArticle() !! + + $wgOut->addHTML( Xml::closeElement( 'div' ) ); } else { # Just need to set the right headers - $wgOut->setArticleFlag( true ); - $wgOut->setPageTitle( $this->getTitle()->getPrefixedText() ); + $out->setArticleFlag( true ); + $out->setPageTitle( $this->getTitle()->getPrefixedText() ); $this->mPage->doViewUpdates( $this->getContext()->getUser() ); } @@@ -248,28 -268,32 +270,32 @@@ return $r; } - /** - * Overloading Article's getContent method. - * - * Omit noarticletext if sharedupload; text will be fetched from the - * shared upload server if possible. - * @return string - */ - public function getContent() { - $this->loadFile(); - if ( $this->mPage->getFile() && !$this->mPage->getFile()->isLocal() && 0 == $this->getID() ) { - return ''; - } - return parent::getContent(); - } + /** + * Overloading Article's getContentObject method. + * + * Omit noarticletext if sharedupload; text will be fetched from the + * shared upload server if possible. + * @return string + */ + public function getContentObject() { + $this->loadFile(); + if ( $this->mPage->getFile() && !$this->mPage->getFile()->isLocal() && 0 == $this->getID() ) { + return null; + } + return parent::getContentObject(); + } protected function openShowImage() { - global $wgOut, $wgUser, $wgImageLimits, $wgRequest, - $wgLang, $wgEnableUploads, $wgSend404Code; + global $wgImageLimits, $wgEnableUploads, $wgSend404Code; $this->loadFile(); + $out = $this->getContext()->getOutput(); + $user = $this->getContext()->getUser(); + $lang = $this->getContext()->getLanguage(); + $dirmark = $lang->getDirMarkEntity(); + $request = $this->getContext()->getRequest(); - $sizeSel = intval( $wgUser->getOption( 'imagesize' ) ); + $sizeSel = intval( $user->getOption( 'imagesize' ) ); if ( !isset( $wgImageLimits[$sizeSel] ) ) { $sizeSel = User::getDefaultOption( 'imagesize' ); diff --cc includes/LinksUpdate.php index 4717d83ff5,4518421475..d6fd4fb969 --- a/includes/LinksUpdate.php +++ b/includes/LinksUpdate.php @@@ -17,9 -17,15 +17,15 @@@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * + * @file + */ + + /** + * See docs/deferred.txt + * * @todo document (e.g. one-sentence top-level class description). */ -class LinksUpdate { +class LinksUpdate extends SecondaryDBDataUpdate { /**@{{ * @private @@@ -802,78 -855,3 +808,78 @@@ } } } + +/** + * Update object handling the cleanup of links tables after a page was deleted. + **/ +class LinksDeletionUpdate extends SecondaryDBDataUpdate { + + /**@{{ + * @private + */ + var $mWikiPage; //!< WikiPage the wikipage that was deleted + /**@}}*/ + + /** + * Constructor + * + * @param $title Title of the page we're updating + * @param $parserOutput ParserOutput: output from a full parse of this page + * @param $recursive Boolean: queue jobs for recursive updates? + */ + function __construct( WikiPage $page ) { + parent::__construct( ); + + $this->mPage = $page; + } + + /** + * Do some database updates after deletion + */ + public function doUpdate() { + $title = $this->mPage->getTitle(); + $id = $this->mPage->getId(); + + # Delete restrictions for it + $this->mDb->delete( 'page_restrictions', array ( 'pr_page' => $id ), __METHOD__ ); + + # Fix category table counts + $cats = array(); + $res = $this->mDb->select( 'categorylinks', 'cl_to', array( 'cl_from' => $id ), __METHOD__ ); + + foreach ( $res as $row ) { + $cats [] = $row->cl_to; + } + + $this->mPage->updateCategoryCounts( array(), $cats ); + + # If using cascading deletes, we can skip some explicit deletes + if ( !$this->mDb->cascadingDeletes() ) { + $this->mDb->delete( 'revision', array( 'rev_page' => $id ), __METHOD__ ); + + # Delete outgoing links + $this->mDb->delete( 'pagelinks', array( 'pl_from' => $id ), __METHOD__ ); + $this->mDb->delete( 'imagelinks', array( 'il_from' => $id ), __METHOD__ ); + $this->mDb->delete( 'categorylinks', array( 'cl_from' => $id ), __METHOD__ ); + $this->mDb->delete( 'templatelinks', array( 'tl_from' => $id ), __METHOD__ ); + $this->mDb->delete( 'externallinks', array( 'el_from' => $id ), __METHOD__ ); + $this->mDb->delete( 'langlinks', array( 'll_from' => $id ), __METHOD__ ); + $this->mDb->delete( 'iwlinks', array( 'iwl_from' => $id ), __METHOD__ ); + $this->mDb->delete( 'redirect', array( 'rd_from' => $id ), __METHOD__ ); + $this->mDb->delete( 'page_props', array( 'pp_page' => $id ), __METHOD__ ); + } + + # If using cleanup triggers, we can skip some manual deletes + if ( !$this->mDb->cleanupTriggers() ) { + # Clean up recentchanges entries... + $this->mDb->delete( 'recentchanges', + array( 'rc_type != ' . RC_LOG, + 'rc_namespace' => $title->getNamespace(), + 'rc_title' => $title->getDBkey() ), + __METHOD__ ); + $this->mDb->delete( 'recentchanges', + array( 'rc_type != ' . RC_LOG, 'rc_cur_id' => $id ), + __METHOD__ ); + } + } - } ++} diff --cc includes/WikiPage.php index 1c28d11b12,8e9b0a0462..6063a51366 --- a/includes/WikiPage.php +++ b/includes/WikiPage.php @@@ -2225,8 -2147,7 +2389,7 @@@ class WikiPage extends Page public function doDeleteArticleReal( $reason, $suppress = false, $id = 0, $commit = true, &$error = '', User $user = null ) { - global $wgUser; + global $wgUser, $wgContentHandlerUseDB; - $user = is_null( $user ) ? $wgUser : $user; wfDebug( __METHOD__ . "\n" ); diff --cc includes/api/ApiPurge.php index 0f7315f6b3,b803fd4c7a..c01af27601 --- a/includes/api/ApiPurge.php +++ b/includes/api/ApiPurge.php @@@ -89,12 -89,13 +89,13 @@@ class ApiPurge extends ApiBase global $wgParser, $wgEnableParserCache; $popts = ParserOptions::newFromContext( $this->getContext() ); + $popts->setTidy( true ); $p_result = $wgParser->parse( $page->getRawText(), $title, $popts, - true, true, $page->getLatest() ); + true, true, $page->getLatest() ); #FIXME: content! # Update the links tables - $u = new LinksUpdate( $title, $p_result ); - $u->doUpdate(); + $updates = $p_result->getSecondaryDataUpdates( $title ); + SecondaryDataUpdate::runUpdates( $updates ); $r['linkupdate'] = ''; diff --cc includes/installer/MysqlUpdater.php index c9ac28b535,e453b0113a..e22f1d14a8 --- a/includes/installer/MysqlUpdater.php +++ b/includes/installer/MysqlUpdater.php @@@ -197,13 -211,8 +212,15 @@@ class MysqlUpdater extends DatabaseUpda // 1.20 array( 'addTable', 'config', 'patch-config.sql' ), array( 'addIndex', 'revision', 'page_user_timestamp', 'patch-revision-user-page-index.sql' ), + array( 'addField', 'ipblocks', 'ipb_parent_block_id', 'patch-ipb-parent-block-id.sql' ), + array( 'addIndex', 'ipblocks', 'ipb_parent_block_id', 'patch-ipb-parent-block-id-index.sql' ), + + // 1.WD + array( 'addField', 'revision', 'rev_content_format', 'patch-revision-rev_content_format.sql' ), + array( 'addField', 'revision', 'rev_content_model', 'patch-revision-rev_content_model.sql' ), + array( 'addField', 'archive', 'ar_content_format', 'patch-archive-ar_content_format.sql' ), + array( 'addField', 'archive', 'ar_content_model', 'patch-archive-ar_content_model.sql' ), + array( 'addField', 'page', 'page_content_model', 'patch-page-page_content_model.sql' ), ); } diff --cc includes/installer/SqliteUpdater.php index 967f1c8b3f,8146274025..f936fb6c78 --- a/includes/installer/SqliteUpdater.php +++ b/includes/installer/SqliteUpdater.php @@@ -76,13 -90,8 +91,15 @@@ class SqliteUpdater extends DatabaseUpd // 1.20 array( 'addTable', 'config', 'patch-config.sql' ), array( 'addIndex', 'revision', 'page_user_timestamp', 'patch-revision-user-page-index.sql' ), + array( 'addField', 'ipblocks', 'ipb_parent_block_id', 'patch-ipb-parent-block-id.sql' ), + array( 'addIndex', 'ipblocks', 'ipb_parent_block_id', 'patch-ipb-parent-block-id-index.sql' ), + + // 1.WD + array( 'addField', 'revision', 'rev_content_format', 'patch-revision-rev_content_format.sql' ), + array( 'addField', 'revision', 'rev_content_model', 'patch-revision-rev_content_model.sql' ), + array( 'addField', 'archive', 'ar_content_format', 'patch-archive-ar_content_format.sql' ), + array( 'addField', 'archive', 'ar_content_model', 'patch-archive-ar_content_model.sql' ), + array( 'addField', 'page', 'page_content_model', 'patch-page-page_content_model.sql' ), ); } diff --cc tests/phpunit/includes/RevisionStorageTest.php index e1527781cb,20199b203b..8b77746183 --- a/tests/phpunit/includes/RevisionStorageTest.php +++ b/tests/phpunit/includes/RevisionStorageTest.php @@@ -11,18 -10,28 +11,39 @@@ class RevisionStorageTest extends Media var $the_page; + function __construct( $name = null, array $data = array(), $dataName = '' ) { + parent::__construct( $name, $data, $dataName ); + + $this->tablesUsed = array_merge( $this->tablesUsed, + array( 'page', + 'revision', + 'text', + + 'recentchanges', + 'logging', + + 'page_props', + 'pagelinks', + 'categorylinks', + 'langlinks', + 'externallinks', + 'imagelinks', + 'templatelinks', + 'iwlinks' ) ); + } + public function setUp() { + global $wgExtraNamespaces, $wgNamespaceContentModels, $wgContentHandlers, $wgContLang; + + $wgExtraNamespaces[ 12312 ] = 'Dummy'; + $wgExtraNamespaces[ 12313 ] = 'Dummy_talk'; + + $wgNamespaceContentModels[ 12312 ] = 'DUMMY'; + $wgContentHandlers[ 'DUMMY' ] = 'DummyContentHandlerForTesting'; + + MWNamespace::getCanonicalNamespaces( true ); # reset namespace cache + $wgContLang->resetNamespaces(); # reset namespace cache + if ( !$this->the_page ) { $this->the_page = $this->createPage( 'RevisionStorageTest_the_page', "just a dummy page" ); } @@@ -347,7 -303,6 +368,6 @@@ $this->assertNotEquals( $orig->getId(), $rev->getId(), 'new null revision shold have a different id from the original revision' ); $this->assertEquals( $orig->getTextId(), $rev->getTextId(), 'new null revision shold have the same text id as the original revision' ); - $this->assertEquals( 'some testing text', $rev->getText() ); + $this->assertEquals( 'some testing text', $rev->getContent()->getNativeData() ); } } - ?> diff --cc tests/phpunit/includes/TitleMethodsTest.php index 4edff7285a,aed658baa3..de8f18fb5b --- a/tests/phpunit/includes/TitleMethodsTest.php +++ b/tests/phpunit/includes/TitleMethodsTest.php @@@ -102,48 -75,6 +102,47 @@@ class TitleMethodsTest extends MediaWik $this->assertEquals( $expectedBool, $title->hasSubjectNamespace( $ns ) ); } - + public function dataGetContentModel() { + return array( + array( 'Foo', CONTENT_MODEL_WIKITEXT ), + array( 'Foo.js', CONTENT_MODEL_WIKITEXT ), + array( 'Foo/bar.js', CONTENT_MODEL_WIKITEXT ), + array( 'User:Foo', CONTENT_MODEL_WIKITEXT ), + array( 'User:Foo.js', CONTENT_MODEL_WIKITEXT ), + array( 'User:Foo/bar.js', CONTENT_MODEL_JAVASCRIPT ), + array( 'User:Foo/bar.css', CONTENT_MODEL_CSS ), + array( 'User talk:Foo/bar.css', CONTENT_MODEL_WIKITEXT ), + array( 'User:Foo/bar.js.xxx', CONTENT_MODEL_WIKITEXT ), + array( 'User:Foo/bar.xxx', CONTENT_MODEL_WIKITEXT ), + array( 'MediaWiki:Foo.js', CONTENT_MODEL_JAVASCRIPT ), + array( 'MediaWiki:Foo.css', CONTENT_MODEL_CSS ), + array( 'MediaWiki:Foo/bar.css', CONTENT_MODEL_CSS ), + array( 'MediaWiki:Foo.JS', CONTENT_MODEL_WIKITEXT ), + array( 'MediaWiki:Foo.CSS', CONTENT_MODEL_WIKITEXT ), + array( 'MediaWiki:Foo.css.xxx', CONTENT_MODEL_WIKITEXT ), + array( 'TEST-JS:Foo', CONTENT_MODEL_JAVASCRIPT ), + array( 'TEST-JS:Foo.js', CONTENT_MODEL_JAVASCRIPT ), + array( 'TEST-JS:Foo/bar.js', CONTENT_MODEL_JAVASCRIPT ), + array( 'TEST-JS_TALK:Foo.js', CONTENT_MODEL_WIKITEXT ), + ); + } + + /** + * @dataProvider dataGetContentModel + */ + public function testGetContentModel( $title, $expectedModelId ) { + $title = Title::newFromText( $title ); + $this->assertEquals( $expectedModelId, $title->getContentModel() ); + } + + /** + * @dataProvider dataGetContentModel + */ + public function testHasContentModel( $title, $expectedModelId ) { + $title = Title::newFromText( $title ); + $this->assertTrue( $title->hasContentModel( $expectedModelId ) ); + } + public function dataIsCssOrJsPage() { return array( array( 'Foo', false ), diff --cc tests/phpunit/includes/WikiPageTest.php index 3f3ba5aac6,88c26ef7e9..2540603880 --- a/tests/phpunit/includes/WikiPageTest.php +++ b/tests/phpunit/includes/WikiPageTest.php @@@ -9,6 -8,27 +9,27 @@@ class WikiPageTest extends MediaWikiTes var $pages_to_delete; + function __construct( $name = null, array $data = array(), $dataName = '' ) { + parent::__construct( $name, $data, $dataName ); + + $this->tablesUsed = array_merge ( $this->tablesUsed, + array( 'page', + 'revision', + 'text', + + 'recentchanges', + 'logging', + + 'page_props', + 'pagelinks', + 'categorylinks', + 'langlinks', + 'externallinks', + 'imagelinks', + 'templatelinks', + 'iwlinks' ) ); + } - ++ public function setUp() { $this->pages_to_delete = array(); } @@@ -47,50 -66,6 +68,50 @@@ return $page; } + public function testDoEditContent() { + $title = Title::newFromText( "WikiPageTest_testDoEditContent" ); + + $page = $this->newPage( $title ); + + $content = ContentHandler::makeContent( "[[Lorem ipsum]] dolor sit amet, consetetur sadipscing elitr, sed diam " + . " nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat.", + $title ); + + $page->doEditContent( $content, "testing 1" ); + + $this->assertTrue( $title->exists(), "Title object should indicate that the page now exists" ); + $this->assertTrue( $page->exists(), "WikiPage object should indicate that the page now exists" ); + + $id = $page->getId(); + + # ------------------------ + $page = new WikiPage( $title ); + + $retrieved = $page->getContent(); + $this->assertTrue( $content->equals( $retrieved ), 'retrieved content doesn\'t equal original' ); + + # ------------------------ + $content = ContentHandler::makeContent( "At vero eos et accusam et justo duo [[dolores]] et ea rebum. " + . "Stet clita kasd [[gubergren]], no sea takimata sanctus est.", + $title ); + + $page->doEditContent( $content, "testing 2" ); + + # ------------------------ + $page = new WikiPage( $title ); + + $retrieved = $page->getContent(); + $this->assertTrue( $content->equals( $retrieved ), 'retrieved content doesn\'t equal original' ); + + # ------------------------ + $dbr = wfGetDB( DB_SLAVE ); + $res = $dbr->select( 'pagelinks', '*', array( 'pl_from' => $id ) ); + $n = $res->numRows(); + $res->free(); + + $this->assertEquals( 2, $n, 'pagelinks should contain two links from the page' ); + } - ++ public function testDoEdit() { $title = Title::newFromText( "WikiPageTest_testDoEdit" ); @@@ -146,19 -121,6 +167,19 @@@ $this->assertEquals( $text, $page->getText() ); } + public function testDoQuickEditContent() { + global $wgUser; + + $page = $this->createPage( "WikiPageTest_testDoQuickEditContent", "original text" ); + + $content = ContentHandler::makeContent( "quick text", $page->getTitle() ); + $page->doQuickEditContent( $content, $wgUser, "testing q" ); + + # --------------------- + $page = new WikiPage( $page->getTitle() ); + $this->assertTrue( $content->equals( $page->getContent() ) ); + } - ++ public function testDoDeleteArticle() { $page = $this->createPage( "WikiPageTest_testDoDeleteArticle", "[[original text]] foo" ); $id = $page->getId(); @@@ -166,8 -128,6 +187,7 @@@ $page->doDeleteArticle( "testing deletion" ); $this->assertFalse( $page->exists(), "WikiPage::exists should return false after page was deleted" ); - + $this->assertNull( $page->getContent(), "WikiPage::getContent should return null after page was deleted" ); $this->assertFalse( $page->getText(), "WikiPage::getText should return false after page was deleted" ); $t = Title::newFromText( $page->getTitle()->getPrefixedText() ); @@@ -308,10 -242,6 +328,10 @@@ public function testGetRedirectTarget( $title, $text, $target ) { $page = $this->createPage( $title, $text ); + # sanity check, because this test seems to fail for no reason for some people. + $c = $page->getContent(); + $this->assertEquals( 'WikitextContent', get_class( $c ) ); - ++ # now, test the actual redirect $t = $page->getRedirectTarget(); $this->assertEquals( $target, is_null( $t ) ? null : $t->getPrefixedText() ); @@@ -531,18 -461,6 +551,18 @@@ more stuf $this->assertEquals( $expected, $text ); } + /** + * @dataProvider dataReplaceSection + */ + public function testReplaceSectionContent( $title, $text, $section, $with, $sectionTitle, $expected ) { + $page = $this->createPage( $title, $text ); + + $content = ContentHandler::makeContent( $with, $page->getTitle(), $page->getContentModel() ); + $c = $page->replaceSectionContent( $section, $content, $sectionTitle ); + + $this->assertEquals( $expected, is_null( $c ) ? null : trim( $c->getNativeData() ) ); + } - ++ /* @FIXME: fix this! public function testGetUndoText() { global $wgDiff3; @@@ -645,9 -561,41 +663,41 @@@ $page = new WikiPage( $page->getTitle() ); $this->assertEquals( $rev2->getSha1(), $page->getRevision()->getSha1(), "rollback did not revert to the correct revision" ); - $this->assertEquals( "one\n\ntwo", $page->getText() ); + $this->assertEquals( "one\n\ntwo", $page->getContent()->getNativeData() ); } + /** + * @todo FIXME: the above rollback test is better, but it keeps failing in jenkins for some reason. + */ + public function testDoRollback() { + $admin = new User(); + $admin->setName("Admin"); + + $text = "one"; + $page = $this->newPage( "WikiPageTest_testDoRollback" ); - $page->doEdit( $text, "section one", EDIT_NEW, false, $admin ); ++ $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ), "section one", EDIT_NEW, false, $admin ); + $rev1 = $page->getRevision(); + + $user1 = new User(); + $user1->setName( "127.0.1.11" ); + $text .= "\n\ntwo"; + $page = new WikiPage( $page->getTitle() ); - $page->doEdit( $text, "adding section two", 0, false, $user1 ); ++ $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ), "adding section two", 0, false, $user1 ); + + # now, try the rollback + $admin->addGroup( "sysop" ); #XXX: make the test user a sysop... + $token = $admin->getEditToken( array( $page->getTitle()->getPrefixedText(), $user1->getName() ), null ); + $errors = $page->doRollback( $user1->getName(), "testing revert", $token, false, $details, $admin ); + + if ( $errors ) { + $this->fail( "Rollback failed:\n" . print_r( $errors, true ) . ";\n" . print_r( $details, true ) ); + } + + $page = new WikiPage( $page->getTitle() ); + $this->assertEquals( $rev1->getSha1(), $page->getRevision()->getSha1(), "rollback did not revert to the correct revision" ); - $this->assertEquals( "one", $page->getText() ); ++ $this->assertEquals( "one", $page->getContent()->getNativeData() ); + } + public function dataGetAutosummary( ) { return array( array( diff --cc tests/phpunit/includes/filerepo/FileBackendTest.php index af88bc8ced,8da54e2f23..ec2fbacdb1 --- a/tests/phpunit/includes/filerepo/FileBackendTest.php +++ b/tests/phpunit/includes/filerepo/FileBackendTest.php @@@ -1,6 -1,6 +1,9 @@@ skipWhitespace(); $this->assertTextNode( "comment", $summary ); ++ $this->skipWhitespace(); ++ ++ if ( $this->xml->name == "model" ) { // model tag is optional ++ $this->assertTextNode( "model", CONTENT_MODEL_WIKITEXT ); //@todo: make this a test parameter ++ $this->skipWhitespace(); ++ } ++ ++ ++ if ( $this->xml->name == "format" ) { // format tag is optional ++ $this->assertTextNode( "format", CONTENT_FORMAT_WIKITEXT ); //@todo: make this a test parameter ++ $this->skipWhitespace(); ++ } $this->assertNodeStart( "text", false ); if ( $text_bytes !== false ) { diff --cc tests/phpunit/phpunit.php index e3f780bfdd,61a20f1b1f..61a20f1b1f mode 100644,100755..100644 --- a/tests/phpunit/phpunit.php +++ b/tests/phpunit/phpunit.php