From 8c8ef55be55641d8bad4149b7f2aa46ac1277bb0 Mon Sep 17 00:00:00 2001 From: Tim Starling Date: Sun, 17 Apr 2005 08:30:15 +0000 Subject: [PATCH] * When an image is changed, invalidate pages that use it. * Introduced image "broken links" allowing the user to quickly upload an image with that name * "Upload a new version of this image" link from the image description page --- includes/Article.php | 5 +-- includes/Image.php | 58 +++++++++++++++++++++++--- includes/ImagePage.php | 89 ++++++++++++++++++++++++++-------------- includes/Linker.php | 44 ++++++++++++++++++-- includes/SquidUpdate.php | 7 ++++ languages/Language.php | 8 ++++ 6 files changed, 168 insertions(+), 43 deletions(-) diff --git a/includes/Article.php b/includes/Article.php index 4b8b92d440..ff221b2d62 100644 --- a/includes/Article.php +++ b/includes/Article.php @@ -1713,11 +1713,8 @@ class Article { $this->mTitle->getInternalURL(), $this->mTitle->getInternalURL( 'history' ) ); - foreach ( $linksTo as $linkTo ) { - $urls[] = $linkTo->getInternalURL(); - } - $u = new SquidUpdate( $urls ); + $u = SquidUpdate::newFromTitles( $linksTo, $urls ); array_push( $wgPostCommitUpdateList, $u ); } diff --git a/includes/Image.php b/includes/Image.php index e081739236..57d81b815d 100644 --- a/includes/Image.php +++ b/includes/Image.php @@ -944,7 +944,7 @@ class Image */ function recordUpload( $oldver, $desc, $copyStatus = '', $source = '' ) { global $wgUser, $wgLang, $wgTitle, $wgOut, $wgDeferredUpdateList; - global $wgUseCopyrightUpload; + global $wgUseCopyrightUpload, $wgUseSquid, $wgPostCommitUpdateList; $fname = 'Image::recordUpload'; $dbw =& wfGetDB( DB_MASTER ); @@ -954,7 +954,7 @@ class Image wfDebugDieBacktrace( 'Database schema not up to date, please run maintenance/archives/patch-image_name_unique.sql' ); } - // Delete thumbnails and refresh the cache + // Delete thumbnails and refresh the metadata cache $this->purgeCache(); // Fail now if the image isn't there @@ -970,7 +970,7 @@ class Image $textdesc = $desc; } - $now = wfTimestampNow(); + $now = $dbw->timestamp(); # Test to see if the row exists using INSERT IGNORE # This avoids race conditions by locking the row until the commit, and also @@ -983,13 +983,14 @@ class Image 'img_height' => $this->height, 'img_bits' => $this->bits, 'img_type' => $this->type, - 'img_timestamp' => $dbw->timestamp($now), + 'img_timestamp' => $now, 'img_description' => $desc, 'img_user' => $wgUser->getID(), 'img_user_text' => $wgUser->getName(), ), $fname, 'IGNORE' ); $descTitle = $this->getTitle(); + $purgeURLs = array(); if ( $dbw->affectedRows() ) { # Successfully inserted, this is a new image @@ -1026,7 +1027,7 @@ class Image 'img_height' => $this->height, 'img_bits' => $this->bits, 'img_type' => $this->type, - 'img_timestamp' => $dbw->timestamp(), + 'img_timestamp' => $now, 'img_user' => $wgUser->getID(), 'img_user_text' => $wgUser->getName(), 'img_description' => $desc, @@ -1037,14 +1038,59 @@ class Image # Invalidate the cache for the description page $descTitle->invalidateCache(); + $purgeURLs[] = $descTitle->getInternalURL(); } + # Invalidate cache for all pages using this image + $linksTo = $this->getLinksTo(); + + if ( $wgUseSquid ) { + $u = SquidUpdate::newFromTitles( $linksTo, $purgeURLs ); + array_push( $wgPostCommitUpdateList, $u ); + } + Title::touchArray( $linksTo ); + $log = new LogPage( 'upload' ); $log->addEntry( 'upload', $descTitle, $desc ); return true; } - + + /** + * Get an array of Title objects which are articles which use this image + * Also adds their IDs to the link cache + * + * This is mostly copied from Title::getLinksTo() + */ + function getLinksTo( $options = '' ) { + global $wgLinkCache; + $fname = 'Image::getLinksTo'; + wfProfileIn( $fname ); + + if ( $options ) { + $db =& wfGetDB( DB_MASTER ); + } else { + $db =& wfGetDB( DB_SLAVE ); + } + + extract( $db->tableNames( 'page', 'imagelinks' ) ); + $encName = $db->addQuotes( $this->name ); + $sql = "SELECT page_namespace,page_title,page_id FROM $page,$imagelinks WHERE page_id=il_from AND il_to=$encName $options"; + $res = $db->query( $sql, $fname ); + + $retVal = array(); + if ( $db->numRows( $res ) ) { + while ( $row = $db->fetchObject( $res ) ) { + if ( $titleObj = Title::makeTitle( $row->page_namespace, $row->page_title ) ) { + $wgLinkCache->addGoodLink( $row->page_id, $titleObj->getPrefixedDBkey() ); + $retVal[] = $titleObj; + } + } + } + $db->freeResult( $res ); + return $retVal; + } + } //class diff --git a/includes/ImagePage.php b/includes/ImagePage.php index c6eed1eee8..ee93cfbd91 100644 --- a/includes/ImagePage.php +++ b/includes/ImagePage.php @@ -17,28 +17,39 @@ require_once( 'Image.php' ); */ class ImagePage extends Article { - /* private */ var $img; // Image object this page is shown for. Initialized in openShowImage, not - // available in doDelete etc. - + /* private */ var $img; // Image object this page is shown for + function view() { - global $wgUseExternalEditor; - if( $this->mTitle->getNamespace() == NS_IMAGE ) { - $this->openShowImage(); - } + global $wgUseExternalEditor, $wgOut; - Article::view(); - if($wgUseExternalEditor) { - $this->externalEditorLink(); - } - - # If the article we've just shown is in the "Image" namespace, - # follow it with the history list and link list for the image - # it describes. + $this->img = new Image( $this->mTitle ); - if( $this->mTitle->getNamespace() == NS_IMAGE ) { + if( $this->mTitle->getNamespace() == NS_IMAGE ) { + $this->openShowImage(); + + # No need to display noarticletext, we use our own message, output in openShowImage() + if ( $this->getID() ) { + Article::view(); + } else { + # Just need to set the right headers + $wgOut->setArticleFlag( true ); + $wgOut->setRobotpolicy( 'index,follow' ); + $wgOut->setPageTitle( $this->mTitle->getPrefixedText() ); + $wgOut->addMetaTags(); + $this->viewUpdates(); + } + + if ( $this->img->exists() ) { + $this->uploadNewVersionLink(); + if ( $wgUseExternalEditor && $this->img->exists() ) { + $this->externalEditorLink(); + } + } $this->closeShowImage(); $this->imageHistory(); $this->imageLinks(); + } else { + Article::view(); } } @@ -46,8 +57,7 @@ class ImagePage extends Article { { global $wgOut, $wgUser, $wgImageLimits, $wgRequest, $wgUseImageResize, $wgRepositoryBaseUrl, - $wgUseExternalEditor; - $this->img = new Image( $this->mTitle ); + $wgUseExternalEditor, $wgServer; $full_url = $this->img->getViewURL(); $anchoropen = ''; $anchorclose = ''; @@ -63,12 +73,9 @@ class ImagePage extends Article { $max = $wgImageLimits[$sizeSel]; $maxWidth = $max[0]; $maxHeight = $max[1]; - + $sk = $wgUser->getSkin(); if ( $this->img->exists() ) { - - $sk = $wgUser->getSkin(); - if ( $this->img->getType() != '' ) { # image $width = $this->img->getWidth(); @@ -112,9 +119,24 @@ class ImagePage extends Article { $wgOut->addWikiText($sharedtext); } + } else { + # Image does not exist + $wgOut->addWikiText( wfMsg( 'noimage', $this->getUploadUrl() ) ); } } + function getUploadUrl() { + global $wgServer; + $uploadTitle = Title::makeTitle( NS_SPECIAL, 'Upload' ); + return $wgServer . $uploadTitle->getLocalUrl( 'wpDestFile=' . urlencode( $this->img->getName() ) ); + } + + + function uploadNewVersionLink() { + global $wgOut; + $wgOut->addWikiText( wfMsg( 'uploadnewversion', $this->getUploadUrl() ) ); + } + function externalEditorLink() { global $wgUser,$wgOut; @@ -220,6 +242,8 @@ class ImagePage extends Article { return; } + $this->img = new Image( $this->mTitle ); + # Deleting old images doesn't require confirmation if ( !is_null( $oldimage ) || $confirm ) { if( $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ), $oldimage ) ) { @@ -243,7 +267,7 @@ class ImagePage extends Article { function doDelete() { global $wgOut, $wgUser, $wgContLang, $wgRequest; - global $wgUseSquid, $wgInternalServer, $wgDeferredUpdateList; + global $wgUseSquid, $wgInternalServer, $wgPostCommitUpdateList; $fname = 'ImagePage::doDelete'; $reason = $wgRequest->getVal( 'wpReason' ); @@ -291,17 +315,18 @@ class ImagePage extends Article { $dbw->delete( 'image', array( 'img_name' => $image ) ); $res = $dbw->select( 'oldimage', array( 'oi_archive_name' ), array( 'oi_name' => $image ) ); + # Purge archive URLs from the squid $urlArr = Array(); while ( $s = $dbw->fetchObject( $res ) ) { $this->doDeleteOldImage( $s->oi_archive_name ); $urlArr[] = $wgInternalServer.wfImageArchiveUrl( $s->oi_archive_name ); } - - # Defer purging of archived URLs + + # And also the HTML of all pages using this image + $linksTo = $this->img->getLinksTo(); if ( $wgUseSquid ) { - /* this needs to be done after LinksUpdate */ - $u = new SquidUpdate( $urlArr ); - array_push( $wgDeferredUpdateList, $u ); + $u = SquidUpdate::newFromTitles( $linksTo, $urlArr ); + array_push( $wgPostCommitUpdateList, $u ); } $dbw->delete( 'oldimage', array( 'oi_name' => $image ) ); @@ -312,9 +337,13 @@ class ImagePage extends Article { $article = new Article( $this->mTitle ); $article->doDeleteArticle( $reason ); # ignore errors + # Invalidate parser cache and client cache for pages using this image + # This is left until relatively late to reduce lock time + Title::touchArray( $linksTo ); + /* Delete thumbnails and refresh image metadata cache */ - $imgObj = new Image( $this->mTitle ); - $imgObj->purgeCache(); + $this->img->purgeCache(); + $deleted = $image; } diff --git a/includes/Linker.php b/includes/Linker.php index a5c7a2b7b3..f89a92020b 100644 --- a/includes/Linker.php +++ b/includes/Linker.php @@ -537,8 +537,8 @@ class Linker { $u = $nt->escapeLocalURL(); if ( $url == '' ) { - $s = wfMsg( 'missingimage', $img->getName() ); - $s .= "
{$alt}
{$url}
\n"; + $s = $this->makeBrokenImageLinkObj( $img->getTitle() ); + //$s .= "
{$alt}
{$url}
\n"; } else { $s = '' . ''.$alt.''; @@ -617,7 +617,7 @@ class Linker { $s = "
"; if ( $thumbUrl == '' ) { - $s .= wfMsg( 'missingimage', $img->getName() ); + $s .= $this->makeBrokenImageLinkObj( $img->getTitle ); $zoomicon = ''; } else { $s .= ''. @@ -636,7 +636,45 @@ class Linker { $s .= '
'.$zoomicon.$label."
"; return str_replace("\n", ' ', $s); } + + /** + * Pass a title object, not a title string + */ + function makeBrokenImageLinkObj( &$nt, $text = '', $query = '', $trail = '', $prefix = '' ) { + # Fail gracefully + if ( ! isset($nt) ) { + # wfDebugDieBacktrace(); + return "{$prefix}{$text}{$trail}"; + } + + $fname = 'Skin::makeBrokenImageLinkObj'; + wfProfileIn( $fname ); + + $q = 'wpDestFile=' . urlencode( $nt->getDBkey() ); + if ( '' != $query ) { + $q .= "&$query"; + } + $uploadTitle = Title::makeTitle( NS_SPECIAL, 'Upload' ); + $url = $uploadTitle->escapeLocalURL( $q ); + + if ( '' == $text ) { + $text = htmlspecialchars( $nt->getPrefixedText() ); + } + $style = $this->getInternalLinkAttributesObj( $nt, $text, "yes" ); + $inside = ''; + if ( '' != $trail ) { + if ( preg_match( $this->linktrail, $trail, $m ) ) { + $inside = $m[1]; + $trail = $m[2]; + } + } + $s = "{$prefix}{$text}{$inside}{$trail}"; + + wfProfileOut( $fname ); + return $s; + } + /** @todo document */ function makeMediaLink( $name, $url, $alt = '' ) { $nt = Title::makeTitleSafe( NS_IMAGE, $name ); diff --git a/includes/SquidUpdate.php b/includes/SquidUpdate.php index 0bcb61faaf..484f26dec4 100644 --- a/includes/SquidUpdate.php +++ b/includes/SquidUpdate.php @@ -76,6 +76,13 @@ class SquidUpdate { return new SquidUpdate( $blurlArr ); } + /* static */ function newFromTitles( &$titles, $urlArr = array() ) { + foreach ( $titles as $title ) { + $urlArr[] = $title->getInternalURL(); + } + return new SquidUpdate( $urlArr ); + } + /* static */ function newSimplePurge( &$title ) { $urlArr = $title->getSquidURLs(); return new SquidUpdate( $blurlArr ); diff --git a/languages/Language.php b/languages/Language.php index 190a14e3c2..dca90b4915 100644 --- a/languages/Language.php +++ b/languages/Language.php @@ -1056,6 +1056,14 @@ this old version, (rev) = revert to this old version. 'nolinkstoimage' => 'There are no pages that link to this image.', 'sharedupload' => 'This file is a shared upload and may be used by other projects.', 'shareduploadwiki' => 'Please see the [$1 image description page] for further information.', +'noimage' => "No image by this name exists. + +
+'''[$1 Upload this image]''' +

", +'uploadnewversion' => "
+[$1 Upload a new version of this image] +

", # Statistics # -- 2.20.1