Welterkj has uploaded a new change for review. ( 
https://gerrit.wikimedia.org/r/344483 )

Change subject: Redesign to eliminate creation of file pages for graph images.
......................................................................

Redesign to eliminate creation of file pages for graph images.

Bug: T100795
Change-Id: I3aa5957899aad85a9a443a07aed369c60aeb7a49
---
M GraphViz.php
M GraphViz_body.php
M RELEASE-NOTES.md
M UploadLocalFile.php
4 files changed, 347 insertions(+), 683 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/GraphViz 
refs/changes/83/344483/1

diff --git a/GraphViz.php b/GraphViz.php
index 1739841..7299154 100644
--- a/GraphViz.php
+++ b/GraphViz.php
@@ -33,7 +33,7 @@
 
 if ( !defined( 'MEDIAWIKI' ) ) die();
 
-define( 'GraphViz_VERSION', '1.6.1' );
+define( 'GraphViz_VERSION', '2.0.0' );
 
 /**
  * The GraphViz settings class.
@@ -106,12 +106,10 @@
        $GLOBALS['wgAutoloadClasses']['UploadLocalFile'] = $dir . 
"UploadLocalFile.php";
        $GLOBALS['wgAutoloadClasses']['UploadFromLocalFile'] = $dir . 
"UploadLocalFile.php";
        $GLOBALS['wgHooks']['ParserFirstCallInit'][] = 'GraphViz::onParserInit';
-       $GLOBALS['wgHooks']['OutputPageParserOutput'][] = 
'GraphViz::onOutputPageParserOutput';
        $GLOBALS['wgHooks']['ArticleDeleteComplete'][] = 
'GraphViz::onArticleDeleteComplete';
        $GLOBALS['wgHooks']['UnitTestsList'][] = 'GraphViz::onUnitTestsList';
        $GLOBALS['wgHooks']['PageContentSave'][] = 
'GraphViz::onPageContentSave';
-       $GLOBALS['wgHooks']['PageContentSaveComplete'][] = 
'GraphViz::onPageContentSaveComplete';
-       $GLOBALS['wgHooks']['EditPageGetPreviewContent'][] = 
'GraphViz::onEditPageGetPreviewContent';
+       $GLOBALS['wgHooks']['RejectParserCacheValue'][] = 
'GraphViz::onRejectParserCacheValue';
        $GLOBALS['wgExtensionCredits']['parserhook'][] = array(
                'name' => 'Graphviz',
                'path' => __FILE__,
diff --git a/GraphViz_body.php b/GraphViz_body.php
index 43d6d45..3e233f1 100644
--- a/GraphViz_body.php
+++ b/GraphViz_body.php
@@ -99,14 +99,6 @@
        const IMAGE_SUBDIR = "images/";
 
        /**
-        * The basename to use for dummy graph image files.
-        * @see GraphViz::createDummyImageFilePage
-        *
-        * @var string IMAGE_DUMMY
-        */
-       const IMAGE_DUMMY = "File_graph_GraphVizExtensionDummy_dot";
-
-       /**
         * Used as an array key in GraphViz::$graphTypes and other arrays.
         * It must be a unique value in GraphViz::$graphTypes.
         *
@@ -121,13 +113,6 @@
         * @var integer MSCGEN
         */
        const MSCGEN = 1;
-
-       /**
-        * The name of the root category containing pages created by this 
extension.
-        *
-        * @var integer ROOT_CATEGORY
-        */
-       const ROOT_CATEGORY = "GraphViz";
 
        /**
         * A list of dot attributes that are forbidden.
@@ -179,63 +164,6 @@
        );
 
        /**
-        * A two dimensional array of graph related state.
-        * The array keys of the first dimension are the list of article titles 
being saved.
-        * The array keys of the second dimension are the list of active graph 
files for a given title being saved.
-        * @var array $titlesBeingSaved
-        */
-       private static $titlesBeingSaved = array();
-
-       /**
-        * A variable for temporarily holding a copy of GLOBALS['wgHooks'].
-        * @var $disabledHooks
-        */
-       private static $disabledHooks = null;
-
-       /**
-        * Disable all hook functions (GLOBALS['wgHooks']).
-        * @author Keith Welter
-        * @return true upon success, false upon failure.
-        */
-       protected static function disableHooks() {
-               if ( isset( $GLOBALS['wgHooks'] ) ) {
-                       if ( isset( self::$disabledHooks ) ) {
-                               wfDebug( __METHOD__ . ": hooks already 
disabled\n" );
-                       } else {
-                               self::$disabledHooks = $GLOBALS['wgHooks'];
-                               $GLOBALS['wgHooks'] = null;
-                               wfDebug( __METHOD__ . ": hooks disabled\n" );
-                               return true;
-                       }
-               } else {
-                       wfDebug( __METHOD__ . ": hooks not set\n" );
-               }
-               return false;
-       }
-
-       /**
-        * Re-enable all hook functions (GLOBALS['wgHooks']).
-        * Must be called after GraphViz::disableHooks.
-        * @author Keith Welter
-        * @return true upon success, false upon failure.
-        */
-       protected static function enableHooks() {
-               if ( isset( self::$disabledHooks ) ) {
-                       if ( isset( $GLOBALS['wgHooks'] ) ) {
-                               wfDebug( __METHOD__ . ": hooks are already set 
- aborting\n" );
-                       } else {
-                               $GLOBALS['wgHooks'] = self::$disabledHooks;
-                               self::$disabledHooks = null;
-                               wfDebug( __METHOD__ . ": hooks enabled\n" );
-                               return true;
-                       }
-               } else {
-                       wfDebug( __METHOD__ . ": hooks not disabled\n" );
-               }
-               return false;
-       }
-
-       /**
         * @return string regular expression for matching an image attribute in 
the DOT language.
         *
         * @see http://www.graphviz.org/content/attrs#dimage
@@ -256,39 +184,6 @@
        }
 
        /**
-        * Create dummy file pages for each image type supported by this 
extension.
-        * @see GraphViz::createDummyImageFilePage
-        * @author Keith Welter
-        */
-       public static function initDummyFilePages() {
-               global $wgParser;
-
-               if ( $wgParser instanceof StubObject ) {
-                       $wgParser->_unstub();
-               }
-
-               foreach ( GraphRenderParms::$supportedDotImageTypes as 
$imageType ) {
-                       if ( !self::imageTypeAllowed( $imageType ) ) {
-                               wfDebug( __METHOD__ . ": skipping $imageType\n" 
);
-                               continue;
-                       }
-
-                       $imageFileName = self::IMAGE_DUMMY . '.' . $imageType;
-                       $imageTitle = Title::newFromText( $imageFileName, 
NS_FILE );
-
-                       if ( !$imageTitle->exists() ) {
-                               wfDebug( __METHOD__ . ": file page for 
$imageFileName does not exist\n" );
-                               self::createDummyImageFilePage( $wgParser, 
$imageType );
-                       } else if ( self::titleHasMultipleRevisions( 
$imageTitle ) ) {
-                               wfDebug( __METHOD__ . ": file page for 
$imageFileName has multiple revisions\n" );
-                               self::deleteFilePage( $imageTitle );
-
-                               self::createDummyImageFilePage( $wgParser, 
$imageType );
-                       }
-               }
-       }
-
-       /**
         * Check if a given image type is probably allowed to be uploaded
         * (does not consult any file extension blacklists).
         * @param[in] string $imageType is the type of image (e.g. png) to 
check.
@@ -305,100 +200,6 @@
        }
 
        /**
-        * Delete a file page with the given title.
-        * This is used to delete dummy file pages with multiple revisions
-        * so that they don't bloat the database.
-        * @see GraphViz::createDummyImageFilePage
-        * @param[in] Title $title
-        * @author Keith Welter
-        */
-       public static function deleteFilePage( $title ) {
-               $file = wfLocalFile( $title );
-               $oldimage = null;
-               $reason = '';
-               $suppress = false;
-               FileDeleteForm::doDelete( $title, $file, $oldimage, $reason, 
$suppress );
-       }
-
-       /**
-        * Optionally create a category page for GraphViz::ROOT_CATEGORY and 
the given subcategory.
-        * @param[in] string $subCategory is the same as the graph renderer.
-        * @author Keith Welter
-        */
-       public static function optionallyCreateCategoryPage( $subCategory ) {
-               global $wgGraphVizSettings;
-
-               if ( strcasecmp( $wgGraphVizSettings->createCategoryPages, 
'yes' ) == 0 ) {
-                       $rootCategoryName = self::ROOT_CATEGORY;
-                       $rootCategoryDesc = wfMessage( 
'graphviz-category-desc', "[[:Category:$rootCategoryName]]" )->text();
-                       self::createCategoryPage( $rootCategoryName, 
$rootCategoryDesc, "" );
-
-                       $subCategoryName = $rootCategoryName . ' ' . 
$subCategory;
-                       $subCategoryDesc = wfMessage( 
'graphviz-subcategory-desc', "[[:Category:$subCategoryName]]", $subCategory 
)->text();
-                       $subCategoryDesc .= "[[Category:$rootCategoryName]]";
-                       self::createCategoryPage( $subCategoryName, 
$subCategoryDesc, "" );
-               }
-       }
-
-       public static function getCategoryTags( $renderer ) {
-               $rootCategoryName = self::ROOT_CATEGORY;
-               return 
"[[Category:$rootCategoryName]][[Category:$rootCategoryName $renderer]]";
-       }
-
-       /**
-        * Create a category page with the given name if it does not already 
exist.
-        * @param[in] string $name is the name to use for the new category page.
-        * @param[in] string $pageText is the page text to supply.
-        * @param[in] string $comment is the comment to supply for the edit.
-        * @author Keith Welter
-        */
-       public static function createCategoryPage( $name, $pageText, $comment ) 
{
-               $title = Title::newFromText( "Category:" . $name );
-
-               if ( !$title->exists() ) {
-                       $wikiPage = new WikiPage( $title );
-                       $flags = EDIT_NEW;
-                       $status = self::doEditContent( $wikiPage, $title, 
$pageText, $comment, $flags, null );
-                       if ( $status->isOK() ) {
-                               wfDebug( __METHOD__ . ": created category 
'$name'\n" );
-                       } else {
-                               wfDebug( __METHOD__ . ": create failed for 
category '$name'\n" );
-                       }
-               }
-       }
-
-       /**
-        * Create a dummy file page with a trivial graph image of the given 
image type.
-        * New graph images of the same type may be uploaded on top of this 
dummy without
-        * triggering any parser code.  This makes it possible to do graph 
image file
-        * uploads in the context of a tag function like GraphViz::render.
-        * @param[in] Parser $parser
-        * @param[in] string $imageType is the type of image (e.g. png) to 
create a dummy file page for.
-        * @author Keith Welter
-        */
-       public static function createDummyImageFilePage( Parser &$parser, 
$imageType ) {
-               $input = "graph GraphVizExtensionDummy { GraphViz }";
-               $args['format'] = $imageType;
-               $frame = false;
-               $isDummy = true;
-               self::render( $input, $args, $parser, $frame, $isDummy );
-       }
-
-       /**
-        * Determine if the given title has multiple revisions.
-        * @param[in] Title $title is the title of the wiki page to check for 
multiple revisions.
-        * @author Keith Welter
-        * @return true if the given title has multiple revisions, otherwise 
false.
-        */
-       public static function titleHasMultipleRevisions( $title ) {
-               $firstRev = $title->getFirstRevision();
-               if ( $firstRev ) {
-                       return $title->getLatestRevID() != $firstRev->getId();
-               }
-               return false;
-       }
-
-       /**
         * Set parser hook functions for supported graph types.
         * @author Keith Welter
         * @return true
@@ -411,177 +212,13 @@
        }
 
        /**
-        * Hook function front-end to GraphViz::initDummyFilePages for edit 
preview.
-        * initDummyFilePages() must be called before parsing for edit previews.
-        * @author Keith Welter
-        */
-       public static function onEditPageGetPreviewContent( $editPage, 
&$content ) {
-               self::initDummyFilePages();
-       }
-
-       /**
-        * When parsing is complete for a page, check for any graph image files 
associated with the page and
-        * upload them.  The uploading must be deferred until this point 
because the upload code path
-        * invokes parsing logic which throws an exception if called from a 
parser hook function.
-        *
-        * @return true
-        * @author Keith Welter
-        */
-       public static function onOutputPageParserOutput( OutputPage &$out, 
ParserOutput $parserOutput ) {
-               global $wgUser;
-
-               $title = $out->getTitle();
-
-               self::uploadImagesForTitle( $title, $wgUser );
-
-               return true;
-       }
-
-       /**
-        * Check for any graph image files associated with the title and upload 
them.
-        *
-        * @return int number of uploaded images
-        * @author Keith Welter
-        */
-       public static function uploadImagesForTitle( $title, $user ) {
-               // find any stored images for the page
-               $graphName = self::getGraphFileBaseNameFromTitle( $title );
-               $imageDir = self::getImageDir();
-               $globPattern = $imageDir . $graphName . "*.*";
-               $imageFilePaths = glob( $globPattern );
-               $uploaded = 0;
-
-               // if any were found, upload them now
-               if ( !empty( $imageFilePaths ) ) {
-                       $errorText = "";
-
-                       $uploaded = self::uploadImages( $title->getFulltext(), 
$imageFilePaths, $user, $errorText );
-
-                       if ( $errorText != "" ) {
-                               $errorHTML = self::multilineErrorHTML( 
$errorText );
-                               $out->addHTML( $errorHTML );
-                       }
-
-                       // purge the page if any uploads were successful so 
that active images may be displayed
-                       if ( $uploaded > 0 ) {
-                               $wikiPage = WikiPage::factory( $title );
-                               $wikiPage->doPurge();
-                       }
-               }
-
-               return $uploaded;
-       }
-
-       /**
-        * Convenience function for creating or editing page text.
-        *
-        * @param[in] WikiPage $wikiPage is the wiki page to create or edit.
-        * @param[in] string $title is the title of the wiki page to create or 
edit.
-        * @param[in] string $pageText is the page text to supply.
-        * @param[in] string $comment is the comment to supply for the edit.
-        * @param[in] integer $flags see the WikiPage::doEditContent flags 
documentation
-        * @param[in] User $user is the user on behalf of whom the edit is 
recorded.
-        *
-        * @return Status.
-        *
-        * @author Keith Welter
-        */
-       public static function doEditContent( $wikiPage, $title, $pageText, 
$comment, $flags, $user ) {
-               $status = Status::newGood();
-
-               $oldVersion = version_compare( $GLOBALS['wgVersion'], '1.21', 
'<' );
-               if ( $oldVersion ) {
-                       // Do stuff for MediaWiki 1.20 and older
-                       $status = $wikiPage->doEdit( $pageText, $comment, 
$flags, false, $user );
-               } else {
-                       // Do stuff for MediaWiki 1.21 and newer
-                       $content = ContentHandler::makeContent( $pageText, 
$title );
-                       $status = $wikiPage->doEditContent( $content, $comment, 
$flags, false, $user );
-               }
-
-               return $status;
-       }
-
-       public static function getRendererFromImageFileName( $imageFileName ) {
-               $renderer = strrev( $imageFileName );
-               $renderer = substr( $renderer, 0, strpos( $renderer, '_' ) );
-               $renderer = strrev( $renderer );
-               wfDebug( __METHOD__ . ": imageFileName: $imageFileName 
renderer: $renderer\n" );
-               return $renderer;
-       }
-
-       /**
-        * Upload a list of graph images for a wiki page with the given title 
text.
-        *
-        * @param[in] string $titleText is the title text of the wiki page.
-        * @param[in] string $imageFilePaths is an array of graph image file 
paths.
-        * @param[in] User $user is the user on behalf of whom the upload is to 
be done.
-        * @param[out] string $errorText is used for returning an HTML error 
message if any errors occur.
-        *
-        * @return integer number of uploaded files.
-        *
-        * @author Keith Welter
-        */
-       public static function uploadImages( $titleText, $imageFilePaths, 
$user, &$errorText ) {
-               $comment = wfMessage( 'graphviz-upload-comment', $titleText 
)->text();
-               $watch = false;
-               $removeTempFile = true;
-               $uploaded = 0;
-
-               foreach ( $imageFilePaths as $imageFilePath ) {
-                       wfDebug( __METHOD__ . ": uploading $imageFilePath\n" );
-                       $imageFileName = basename( $imageFilePath );
-                       $renderer = self::getRendererFromImageFileName( 
pathinfo ( $imageFilePath, PATHINFO_FILENAME ) );
-                       $pageText = self::getMapHowToText( $imageFileName ) . 
self::getCategoryTags( $renderer );
-
-                       $imageTitle = Title::newFromText( $imageFileName, 
NS_FILE );
-                       if ( !$imageTitle->exists() ) {
-                               if ( !UploadLocalFile::upload( $imageFileName, 
$imageFilePath, $user, $comment, $pageText, $watch, $removeTempFile ) ) {
-                                       wfDebug( __METHOD__ . ": upload failed 
for $imageFileName\n" );
-                                       if ( file_exists( $imageFilePath ) ) {
-                                               wfDebug( __METHOD__ . ": 
unlinking $imageFilePath\n" );
-                                               unlink( $imageFilePath );
-                                       }
-                                       $graphName = pathinfo( $imageFilePath, 
PATHINFO_FILENAME );
-                                       self::deleteGraphFiles( $graphName, 
self::getSourceAndMapDir() );
-                                       $errorText .= wfMessage( 
'graphviz-uploaderror', $imageFileName )->text() . "\n";
-                               } else {
-                                       wfDebug( __METHOD__ . ": uploaded 
$imageFilePath\n" );
-                                       $uploaded++;
-
-                                       self::optionallyCreateCategoryPage( 
$renderer );
-                               }
-                       } else {
-                               // The upload for this title has already 
occured in GraphViz::render
-                               // but the page text and comment have not been 
updated yet so do it now.
-                               unlink( $imageFilePath );
-
-                               $wikiPage = new WikiFilePage( $imageTitle );
-                               $flags = EDIT_UPDATE | EDIT_SUPPRESS_RC;
-                               self::doEditContent( $wikiPage, $imageTitle, 
$pageText, $comment, $flags, $user );
-                               wfDebug( __METHOD__ . ": updated file page for 
$imageFilePath\n" );
-
-                               // Go ahead and count this as an upload since 
it has been done.
-                               $uploaded++;
-
-                               self::optionallyCreateCategoryPage( $renderer );
-                       }
-               }
-               wfDebug( __METHOD__ . ": uploaded $uploaded files for article: 
$titleText\n" );
-
-               return $uploaded;
-       }
-
-       /**
-        * When an article is deleted, delete all the associated graph files
-        * (except the uploaded ones).
-        * @return true
+        * When an article is deleted, delete all the associated graph files.
         * @author Keith Welter
         */
        public static function onArticleDeleteComplete( &$article, User &$user, 
$reason, $id ) {
+               self::deleteArticleUploadedFiles( $article, self::getImageDir() 
);
                self::deleteArticleFiles( $article, self::getSourceAndMapDir() 
);
                self::deleteArticleFiles( $article, self::getImageDir() );
-               return true;
        }
 
        /**
@@ -589,13 +226,7 @@
         * @author Keith Welter
         */
        public static function getGraphFileBaseNameFromTitle( $title ) {
-               if ( $title->inNamespace( NS_FILE ) ) {
-                       $baseName = $title->getText();
-                       // remove the dummy file extension
-                       $baseName = substr( $baseName, 0, strrpos( $baseName, 
'.' ) );
-               } else {
-                       $baseName = $title->getFulltext();
-               }
+               $baseName = $title->getFulltext();
                $baseName = self::makeFriendlyGraphName( $baseName );
                return $baseName;
        }
@@ -613,6 +244,42 @@
        }
 
        /**
+        * Detect if the given title has associated graph files at the given 
path.
+        * @return True if the title has associated graph files.  Otherwise 
false.
+        * @author Keith Welter
+        */
+       public static function titleHasGraphFiles( $title, $path ) {
+               $globPattern = self::getGraphFileBaseNameFromTitle( $title );
+               $globPattern = $path . $globPattern . "*.*";
+               if ( empty ( glob( $globPattern ) ) ) {
+                       $result = false;
+               }
+               else {
+                       $result = true;
+               }
+               wfDebug( __METHOD__ . ": result: $result\n" );
+               return $result;
+       }
+
+       /**
+        * Delete all uploaded files associated with the given article and path.
+        * @author Keith Welter
+        */
+       public static function deleteArticleUploadedFiles( $article, $path ) {
+               wfDebug( __METHOD__ . ": entering\n" );
+               $title = $article->getTitle();
+               $globPattern = self::getGraphFileBaseNameFromTitle( $title );
+               $globPattern = $path . $globPattern . "*.*";
+               foreach ( glob( $globPattern ) as $file ) {
+                       $uploadedFile = UploadLocalFile::getUploadedFile( $file 
);
+                       if ( $uploadedFile ) {
+                               wfDebug( __METHOD__ . ": deleting uploaded 
file: $file\n" );
+                               $uploadedFile->delete( wfMessage( 
'graphviz-delete-reason' )->text() );
+                       }
+               }
+       }
+
+       /**
         * Delete all the graph files associated with the graph name and path.
         * @author Keith Welter
         */
@@ -623,105 +290,42 @@
        }
 
        /**
-        * Hook function front-end to GraphViz::onTitleSave.
+        * Reject the parser cache value for a page if it has recently uploaded 
graph images.
+        * This is necessary when a user edits a graph but then cancels the 
edit.  In this case,
+        * the parser cache reflects the canceled edit rather than the saved 
graph so we must
+        * reject it.
+        * @author Keith Welter
+        */
+       public static function onRejectParserCacheValue( $parserOutput, 
$wikiPage, $parserOptions ) {
+               $title = $wikiPage->getTitle();
+
+               $result = true;
+               if ( self::titleHasGraphFiles( $title, self::getImageDir() ) ) {
+                       self::deleteArticleFiles( $wikiPage, 
self::getImageDir() );
+                       $result = false;
+               }
+
+               wfDebug( __METHOD__ . ": result $result\n" );
+               return $result;
+       }
+
+       /**
+        * Deletes all graph files for the page.
+        * The reason for this is to ensure clean-up of orphaned graph images
+        * (graph images for which the source wiki text has been deleted).
+        * Graph images for extant wiki source will be regenerated when parsed.
         * @author Keith Welter
         */
        public static function onPageContentSave( $wikiPage, $user, $content, 
$summary, $isMinor, $isWatch, $section, $flags, $status ) {
+               global $wgParser;
+               wfDebug( __METHOD__ . ": entering\n" );
+
                $title = $wikiPage->getTitle();
-               return self::onTitleSave( $title );
-       }
 
-       /**
-        * Function to record when a page with the given title is being saved.
-        * Also, ensure initDummyFilePages() is called before parsing the page
-        * (except for file pages).
-        * @author Keith Welter
-        */
-       public static function onTitleSave( $title ) {
-               $titleText = $title->getFulltext();
-               if ( !$title->inNamespace( NS_FILE ) ) {
-                       self::initDummyFilePages();
-               }
-               self::$titlesBeingSaved[$titleText] = '';
-               wfDebug( __METHOD__ . ": saving: $titleText\n" );
+               self::deleteArticleUploadedFiles( $wikiPage, 
self::getImageDir() );
+               self::deleteArticleFiles( $wikiPage, self::getSourceAndMapDir() 
);
+
                return true;
-       }
-
-       /**
-        * Check if the given title text corresponds to a page that is being 
saved.
-        * @author Keith Welter
-        */
-       protected static function saving( $titleText ) {
-               return array_key_exists( $titleText, self::$titlesBeingSaved );
-       }
-
-       /**
-        * Hook function front-end to GraphViz::onTitleSaveComplete.
-        * @author Keith Welter
-        */
-       public static function onPageContentSaveComplete( $wikiPage, $user, 
$content, $summary, $isMinor, $isWatch, $section, $flags, $revision, $status, 
$baseRevId ) {
-               $title = $wikiPage->getTitle();
-               return self::onTitleSaveComplete( $title );
-       }
-
-       /**
-        * Function to clean-up when a page with the given title is done being 
saved.
-        * Firstly, this function invokes uploadImagesForTitle() to upload any 
graph
-        * images newly created for the given title.
-        * Secondly, this function invokes deleteInactiveFiles() to delete 
inactive
-        * graph files associated with a page when it is done being saved.  
Lastly,
-        * this function removes the record that the page is being saved as 
well as
-        * the list of active files for the page.
-        * @author Keith Welter
-        */
-       public static function onTitleSaveComplete( $title ) {
-               global $wgUser;
-               $titleText = $title->getFulltext();
-               self::uploadImagesForTitle( $title, $wgUser );
-               self::deleteInactiveFiles( $titleText );
-               wfDebug( __METHOD__ . ": done saving: $titleText\n" );
-               unset( self::$titlesBeingSaved[$titleText] );
-               return true;
-       }
-
-       /**
-        * Record an active graph file for the given title text.
-        * An active graph file is one that is referenced by wiki text that is 
being saved.
-        * @param[in] string $titleText is the title of the page being saved.
-        * @param[in] string $file is the path of the active file to record.
-        * @author Keith Welter
-        */
-       protected static function recordActiveFile( $titleText, $file ) {
-               self::$titlesBeingSaved[$titleText][$file] = '';
-       }
-
-       /**
-        * For a given title, check if the given file is active.
-        * An active graph file is one that is referenced by wiki text that is 
being saved.
-        * @param[in] string $titleText is the title of the page to be checked.
-        * @param[in] string $file is the path of the file to be checked.
-        * @author Keith Welter
-        */
-       protected static function isActiveFile( $titleText, $file ) {
-               return array_key_exists( $file, 
self::$titlesBeingSaved[$titleText] );
-       }
-
-       /**
-        * Delete inactive graph files associated with the given title text.
-        * @see GraphViz::isActiveFile
-        * @param[in] string $titleText is the title of the page for which 
inactive files should be deleted.
-        * @author Keith Welter
-        */
-       public static function deleteInactiveFiles( $titleText ) {
-               $globPattern = self::makeFriendlyGraphName( $titleText );
-               $globPattern = self::getSourceAndMapDir() . $globPattern . 
"*.*";
-
-               foreach ( glob( $globPattern ) as $file ) {
-                       if ( !self::isActiveFile( $titleText, $file ) ) {
-                               wfDebug( __METHOD__ . ": deleting inactive 
file: $file\n" );
-                               unlink( $file );
-                       }
-               }
        }
 
        /**
@@ -780,19 +384,13 @@
         * A graph description is the content of a graph tag supported by this 
extension.
         *
         * Before a graph description can be rendered as HTML, a graph renderer 
must be invoked to render
-        * the graph description as an image which is stored on the file system 
and later uploaded to the wiki.
-        *
-        * Normal upload restrictions apply. See:
-        * - UploadLocalFile::isUploadAllowedForUser
-        * - UploadLocalFile::isUploadAllowedForTitle
+        * the graph description as an image which is stored on the file system 
and later stored in
+        * the wiki file repository.
         *
         * @section Maps
         * If the graph description contains links then a map file is populated 
with the coordinates of
         * the shape corresponding to each link.  The map file is also stored 
on the file system but it
-        * is not uploaded.  Rather, the map data is included on the 
description of the uploaded graph
-        * image in the format used by the ImageMap extension.  This allows the 
graph image and map data
-        * to be included in wiki pages without the graph description itself by 
copying the ImageMap text
-        * from the graph image description page and pasting it into the 
desired wiki page.
+        * is not uploaded.
         *
         * @section Regeneration
         * The graph description is stored in a source file on the file system 
(independently of the
@@ -813,11 +411,8 @@
         * -# graph type from the graph description (e.g. graph, digraph, msc)
         * -# graph name (if any) from the graph description
         * -# an optional "uniquifier" (see $args)
-        * -# the user name (for edit previews only)
         *
-        * Basenames are sanitized by makeFriendlyGraphName().  The user name 
is included for
-        * edit previews so that files generated for edits (which might be 
cancelled) don't
-        * collide with files corresponding to the saved version of the wiki 
page.
+        * Basenames are sanitized by makeFriendlyGraphName().
         *
         * The source and map file types are determined by GraphRenderParms.  
See:
         * - GraphRenderParms::getSourceFileName
@@ -883,14 +478,12 @@
         * among those supported for the graph language.  @ref Files.
         *
         * @param[in] Parser $parser
-        * @param[in] boolean $isDummy indicates whether the input represents 
the source for
-        * a dummy graph image (see GraphViz::createDummyImageFilePage).
         *
         * @return string HTML of a graph image and optional map or an HTML 
error message.
         *
         * @author Keith Welter et al.
         */
-       protected static function render( $input, $args, $parser, $frame, 
$isDummy = false )
+       protected static function render( $input, $args, $parser, $frame )
        {
                global
                $wgUser,
@@ -909,13 +502,7 @@
 
                // get title text
                $title = $parser->getTitle();
-               if ( $isDummy ) {
-                       $titleText = "File";
-               } else if ( $title ) {
-                       $titleText = $title->getFulltext();
-               } else {
-                       $titleText = "";
-               }
+               $titleText = $title->getFulltext();
 
                // begin the graphName with the article title text...
                $graphName = $titleText;
@@ -968,40 +555,30 @@
                $graphParms = new GraphRenderParms( $renderer, $graphName, 
$userName, $imageType, $sourceAndMapDir, $imageDir );
 
                // initialize context variables
-               $saving = false;
                $isPreview = false;
+               $userSpecific = false;
                $parserOptions = $parser->getOptions();
 
                if ( $parserOptions ) {
                        $isPreview = $parserOptions->getIsPreview();
                }
-
-               if ( !$isPreview ) {
-                       $saving = self::saving( $titleText );
-                       if ( $saving ) {
-                               // delete the preview version of the files (if 
any)
-                               self::deleteFiles( $graphParms, true, true );
-                       }
-               }
-               wfDebug( __METHOD__ . ": isPreview: $isPreview saving: $saving 
isDummy: $isDummy\n" );
+               wfDebug( __METHOD__ . ": isPreview: $isPreview\n" );
 
                // determine whether or not to call recursiveTagParse
                $doRecursiveTagParse = false;
                $preParseType = "none";
 
-               if ( !$isDummy ) {
-                       if ( isset( $args['preparse'] ) ) {
-                               $preParseType = $args['preparse'];
-                               if ( $preParseType == "dynamic" ) {
+               if ( isset( $args['preparse'] ) ) {
+                       $preParseType = $args['preparse'];
+                       if ( $preParseType == "dynamic" ) {
+                               $doRecursiveTagParse = true;
+                               $parser->disableCache();
+                       } else if ( $preParseType == "static" ) {
+                               if ( $isPreview ) {
                                        $doRecursiveTagParse = true;
-                                       $parser->disableCache();
-                               } else if ( $preParseType == "static" ) {
-                                       if ( $saving || $isPreview ) {
-                                               $doRecursiveTagParse = true;
-                                       }
-                               } else {
-                                       return self::i18nErrorMessageHTML( 
'graphviz-unrecognized-preparse-value', $preParseType );
                                }
+                       } else {
+                               return self::i18nErrorMessageHTML( 
'graphviz-unrecognized-preparse-value', $preParseType );
                        }
                }
                wfDebug( __METHOD__ . ": preParseType: $preParseType 
doRecursiveTagParse: $doRecursiveTagParse\n" );
@@ -1020,10 +597,10 @@
                }
 
                // determine if the image to render exists
-               $imageExists = UploadLocalFile::getUploadedFile( 
$graphParms->getImageFileName( $isPreview ) ) ? true : false;
+               $imageExists = UploadLocalFile::getUploadedFile( 
$graphParms->getImageFileName( $userSpecific ) ) ? true : false;
 
                // get the path of the map to render
-               $mapPath = $graphParms->getMapPath( $isPreview );
+               $mapPath = $graphParms->getMapPath( $userSpecific );
 
                // determine if the map to render exists
                $mapExists = false;
@@ -1032,45 +609,37 @@
                }
                wfDebug( __METHOD__ . ": imageExists: $imageExists mapExists: 
$mapExists\n" );
 
-               // check if the graph source changed only if:
-               // - the wiki text is being saved or
-               // - an edit of the wiki text is being previewed or
-               // - the graph image or map file does not exist
-               // - doing recursiveTagParse
                $sourceChanged = false;
-               if ( $saving || $isPreview || !$imageExists || !$mapExists || 
$doRecursiveTagParse ) {
-                       if ( !self::isSourceChanged( 
$graphParms->getSourcePath( $isPreview ), $input, $sourceChanged, $errorText ) 
) {
-                               return self::errorHTML( $errorText );
-                       }
+               if ( !self::isSourceChanged( $graphParms->getSourcePath( 
$userSpecific ), $input, $sourceChanged, $errorText ) ) {
+                       return self::errorHTML( $errorText );
                }
                wfDebug( __METHOD__ . ": sourceChanged: $sourceChanged\n" );
 
-               $imageFileName = $graphParms->getImageFileName( $isPreview );
-               $imageFilePath = $graphParms->getImagePath( $isPreview );
+               $imageFileName = $graphParms->getImageFileName( $userSpecific );
+               $imageFilePath = $graphParms->getImagePath( $userSpecific );
                $uploaded = false;
-               $usedDummy = false;
 
                // generate image and map files only if the graph source 
changed or the image or map files do not exist
-               if ( $isDummy || $sourceChanged || !$imageExists || !$mapExists 
) {
+               if ( $sourceChanged || !$imageExists || !$mapExists ) {
                        // first, check if the user is allowed to upload the 
image
-                       if ( !UploadLocalFile::isUploadAllowedForUser( $user, 
$errorText ) ) {
+                   if ( !UploadLocalFile::isUploadAllowedForUser( $user, 
$errorText ) ) {
                                wfDebug( __METHOD__ . ": $errorText\n" );
                                return self::errorHTML( $errorText );
-                       }
+                   }
 
                        // if the source changed, update it on disk
                        if ( $sourceChanged ) {
-                               if ( !self::updateSource( 
$graphParms->getSourcePath( $isPreview ), $input, $errorText ) ) {
+                               if ( !self::updateSource( 
$graphParms->getSourcePath( $userSpecific ), $input, $errorText ) ) {
                                        wfDebug( __METHOD__ . ": $errorText\n" 
);
-                                       self::deleteFiles( $graphParms, 
$isPreview, false );
+                                       self::deleteFiles( $graphParms, 
$userSpecific, false );
                                        return self::errorHTML( $errorText );
                                }
                        }
 
                        // execute the image creation command
-                       if ( !self::executeCommand( 
$graphParms->getImageCommand( $isPreview ), $errorText ) )
+                       if ( !self::executeCommand( 
$graphParms->getImageCommand( $userSpecific ), $errorText ) )
                        {
-                               self::deleteFiles( $graphParms, $isPreview, 
false );
+                               self::deleteFiles( $graphParms, $userSpecific, 
false );
 
                                // remove path info from the errorText
                                $errorText = str_replace( $imageDir, "", 
$errorText );
@@ -1078,24 +647,27 @@
                                return self::multilineErrorHTML( $errorText );
                        }
 
-                       // check if the upload is allowed for the intended 
title (the image file must exist prior to this check)
-                       if ( !UploadLocalFile::isUploadAllowedForTitle(
+                       $upload = new UploadFromLocalFile;
+
+               // check if the upload is allowed for the intended title (the 
image file must exist prior to this check)
+               if ( !UploadLocalFile::isUploadAllowedForTitle(
+                               $upload,
                                $user,
                                $imageFileName,
                                $imageFilePath,
                                false,
                                wfGetLangObj(),
                                $errorText ) )
-                       {
-                               wfDebug( __METHOD__ . ": $errorText\n" );
-                               self::deleteFiles( $graphParms, $isPreview, 
false );
-                               return self::errorHTML( $errorText );
+               {
+                       wfDebug( __METHOD__ . ": $errorText\n" );
+                       self::deleteFiles( $graphParms, $userSpecific, false );
+                       return self::errorHTML( $errorText );
                        }
 
                        // execute the map creation command
-                       if ( !self::executeCommand( $graphParms->getMapCommand( 
$isPreview ), $errorText ) )
+                       if ( !self::executeCommand( $graphParms->getMapCommand( 
$userSpecific ), $errorText ) )
                        {
-                               self::deleteFiles( $graphParms, $isPreview, 
false );
+                               self::deleteFiles( $graphParms, $userSpecific, 
false );
 
                                // remove path info from the errorText (file 
base names are allowed to pass)
                                $errorText = str_replace( $imageDir, "", 
$errorText );
@@ -1104,84 +676,18 @@
                        }
 
                        // normalize the map file contents
-                       if ( !self::normalizeMapFileContents( 
$graphParms->getMapPath( $isPreview ), $graphParms->getRenderer(),
+                       if ( !self::normalizeMapFileContents( 
$graphParms->getMapPath( $userSpecific ), $graphParms->getRenderer(),
                                $titleText, $errorText ) ) {
                                wfDebug( __METHOD__ . ": $errorText\n" );
-                               self::deleteFiles( $graphParms, $isPreview, 
false );
+                               self::deleteFiles( $graphParms, $userSpecific, 
false );
                                return self::errorHTML( $errorText );
                        }
 
-                       if ( $saving ) {
-                               self::recordActiveFile( $titleText, 
$graphParms->getSourcePath( $isPreview ) );
-                               self::recordActiveFile( $titleText, 
$graphParms->getMapPath( $isPreview ) );
-                       }
-
-                       $imageTitle = Title::newFromText( $imageFileName, 
NS_FILE );
-                       $imageTitleText = $imageTitle->getText();
-                       $dummyFilePath = self::getImageDir() . 
self::IMAGE_DUMMY . '.' . $imageType;
-
-                       // decide whether to upload the graph image on top of a 
dummy or
-                       // on top of an existing file page for the graph (or 
neither)
-                       if ( !$imageTitle->exists() ) {
-                               // there is no image page for the graph yet so 
try to use a dummy
-                               wfDebug( __METHOD__ . ": $imageTitleText does 
not exist\n" );
-
-                               if ( $dummyFilePath != $imageFilePath) {
-                                       $dummyFileName = basename( 
$dummyFilePath );
-                                       $dummyTitle = Title::newFromText( 
$dummyFileName, NS_FILE );
-
-                                       if ( $dummyTitle->exists() ) {
-                                               if ( 
self::titleHasMultipleRevisions( $dummyTitle ) ) {
-                                                       // the dummy file page 
exists but has already been used so we must bail out
-                                                       $dummyTitleText = 
$dummyTitle->getText();
-                                                       wfDebug( __METHOD__ . 
": $dummyTitleText has multiple revisions\n" );
-                                                       return wfMessage( 
'graphviz-reload' )->escaped();
-                                               } else {
-                                                       // the dummy file page 
exists and has not been copied over yet... use it now!
-                                                       
GraphRenderParms::unlinkIfFileExists( $dummyFilePath );
-                                                       wfDebug( __METHOD__ . 
": copying $imageFilePath to $dummyFilePath\n" );
-                                                       copy( $imageFilePath, 
$dummyFilePath );
-                                                       $imageFilePath = 
$dummyFilePath;
-                                                       $imageFileName = 
basename( $imageFilePath );
-                                                       $usedDummy = true;
-                                               }
-                                       } else {
-                                               // the dummy file page does not 
exist (unexpected) so we must bail out
-                                               $dummyTitleText = 
$dummyTitle->getText();
-                                               wfDebug( __METHOD__ . ": 
$dummyTitleText does not exist\n" );
-                                               return wfMessage( 
'graphviz-reload' )->escaped();
-                                       }
-                               }
-                       } else {
-                               wfDebug( __METHOD__ . ": $imageTitleText 
exists\n" );
-                               if ( $dummyFilePath == $imageFilePath) { //SNO
-                                       return;
-                               }
-                       }
-
                        // prepare to upload
-                       $pageText = "";
-
-                       // don't bother tagging dummies
-                       if ( !$isDummy ) {
-                               $pageText = self::getCategoryTags( $renderer );
-                       }
-                       $comment = wfMessage( 'graphviz-upload-comment', 
$titleText )->text();
-                       $watch = false;
                        $removeTempFile = true;
 
-                       // prevent recusive call to Parser::parse (see bug 
73073).
-                       if ( !self::disableHooks() ) {
-                               return wfMessage( 'graphviz-reload' 
)->escaped();
-                       }
-
-                       // Upload the graph image.
-                       // We can only do this here when a file page already 
exists for the given image.
-                       // Otherwise, file page creation triggers parsing of 
the page text and comment
-                       // resulting in an illegal recursive parse and an 
exception (StripState invalid marker).
-                       // The exception is not even thrown on a path that this 
extension can wrap try/catch logic around.
-                       // So we must really jump through hoops to accomplish 
file uploads in this context.
-                       if ( !UploadLocalFile::upload( $imageFileName, 
$imageFilePath, $user, $comment, $pageText, $watch, $removeTempFile ) ) {
+                       // Store the graph image in the wiki file repository.
+                       if ( !UploadLocalFile::uploadWithoutFilePage( $upload, 
$imageFileName, $imageFilePath, $removeTempFile ) ) {
                                wfDebug( __METHOD__ . ": upload failed for 
$imageFileName\n" );
                                if ( file_exists( $imageFilePath ) ) {
                                        wfDebug( __METHOD__ . ": unlinking 
$imageFilePath\n" );
@@ -1193,55 +699,28 @@
                        } else {
                                wfDebug( __METHOD__ . ": uploaded 
$imageFilePath\n" );
                                $uploaded = true;
-                               // File page content cannot be created/modified 
here so we leave
-                               // a record to do it later using the touch 
below.
-                               // The image file is touched here to trigger
-                               // additional processing later in 
GraphViz::uploadImages.
-                               // Specifically, see the path containing the 
call to doEditContent.
-                               // If a dummy file page was used, we do not 
touch the image file
-                               // because it was the target of a copy and the 
source still exists
-                               // to trigger the additional processing.  We 
want the additional
-                               // processing done on the source, not the 
target of the copy.  If
-                               // we did touch the target, it would be a 
leaked file.
-                               if ( !$usedDummy ) {
-                                       touch( $imageFilePath );
-                               }
-                       }
 
-                       if ( !self::enableHooks() ) {
-                               throw new MWException( "failed to re-enable 
hooks" );
+                               // create an empty file to serve as a marker of 
a recently uploaded file
+                               touch( $imageFilePath );
                        }
-               }
-
-               // If rendering a dummy graph image just return without 
producing HTML.
-               if ( $isDummy ) {
-                       return;
                }
 
                // get the map file contents
-               $mapContents = self::getMapContents( $graphParms->getMapPath( 
$isPreview ) );
-
-               // If an upload was done using a dummy then set the ImageMap 
desc parameter to none.
-               // Otherwise, the image would contain a link to the dummy graph 
image file page.
-               if ( $uploaded && $usedDummy ) {
-                       $args['desc'] = "none";
-               }
+               $mapContents = self::getMapContents( $graphParms->getMapPath( 
$userSpecific ) );
 
                // generate the input for the ImageMap renderer
+               $args['desc'] = "none";
                $imageMapInput = self::generateImageMapInput( $args, 
$imageFileName, $mapContents );
 
                // render the image map (image must be uploaded first)
                $imageMapOutput = ImageMap::render( $imageMapInput, null, 
$parser );
 
-               if ( $saving ) {
-                       self::recordActiveFile( $titleText, 
$graphParms->getSourcePath( $isPreview ) );
-                       self::recordActiveFile( $titleText, 
$graphParms->getMapPath( $isPreview ) );
-               }
-
-               // purge the page if an upload was successful so that active 
images may be displayed
-               if ( $title && $uploaded ) {
-                       $wikiPage = WikiPage::factory( $title );
-                       $wikiPage->doPurge();
+               // if no upload occured, clear the recently uploaded file 
marker (if any)
+               if ( !$uploaded ) {
+                       if ( file_exists( $imageFilePath ) ) {
+                               wfDebug( __METHOD__ . ": unlinking 
$imageFilePath\n" );
+                               unlink( $imageFilePath );
+                       }
                }
 
                // return the rendered HTML
@@ -1376,44 +855,22 @@
        }
 
        /**
-        * @param[in] string $imagePath is the file system path of the graph 
map file.
-        * @return string description with example of how to include ImageMap 
tags for the given image file in a wiki page.
-        * @author Keith Welter
-        */
-       protected static function getMapHowToText( $imagePath ) {
-               $imageExtension = pathinfo( $imagePath, PATHINFO_EXTENSION );
-               $graphName = substr( $imagePath, 0, strlen( $imagePath ) - 
strlen( $imageExtension ) );
-               $mapPath = self::getSourceAndMapDir() . $graphName . 'map';
-               $mapContents = self::getMapContents( $mapPath );
-               $mapHowToText = "";
-               if ( $mapContents != "" ) {
-                       $mapHowToText = wfMessage( 'graphviz-map-desc', 
$imagePath, $mapContents )->text();
-               }
-               return $mapHowToText;
-       }
-
-       /**
         * Delete the given graph source, image and map files.
         *
         * @param[in] GraphRenderParms $graphParms contains the names of the 
graph source, image and map files to delete.
-        * @param[in] boolean $isPreview indicates whether or not the files to 
be deleted were for an edit preview.
+        * @param[in] boolean $userSpecific indicates whether or not the files 
to be deleted are user specific.
         * @param[in] boolean $deleteUploads indicates whether or not to delete 
the uploaded image file.
         *
         * @author Keith Welter
         */
-       protected static function deleteFiles( $graphParms, $isPreview, 
$deleteUploads ) {
-               $graphParms->deleteFiles( $isPreview );
+       protected static function deleteFiles( $graphParms, $userSpecific, 
$deleteUploads ) {
+               $graphParms->deleteFiles( $userSpecific );
 
                if ( $deleteUploads ) {
-                       $imageFileName = $graphParms->getImageFileName( 
$isPreview );
+                       $imageFileName = $graphParms->getImageFileName( 
$userSpecific );
                        $imageFile = UploadLocalFile::getUploadedFile( 
$imageFileName );
                        if ( $imageFile ) {
                                $imageFile->delete( wfMessage( 
'graphviz-delete-reason' )->text() );
-
-                               $imageTitle = Title::newFromText( 
$imageFileName, NS_FILE );
-                               if ( $imageTitle->exists() ) {
-                                       self::deleteFilePage( $imageTitle );
-                               }
                        }
                }
        }
diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
index c971727..cc0221a 100644
--- a/RELEASE-NOTES.md
+++ b/RELEASE-NOTES.md
@@ -1,5 +1,10 @@
 These are the release notes for the [MediaWiki][mediawiki] [GraphViz 
extension][gv_ext].
 
+## GraphViz 2.0.0 ## (2017-3-23)
+* Redesign to eliminate creation of file pages for uploaded graph images.
+* Fix for [bug T100795](https://phabricator.wikimedia.org/T100795).
+* Fix for [bug T151294](https://phabricator.wikimedia.org/T151294).
+
 ## GraphViz 1.6.1 ## (2015-6-16)
 * Fix for [bug T89325](https://phabricator.wikimedia.org/T89325).
 * Fix for [bug T97596](https://phabricator.wikimedia.org/T97596).
diff --git a/UploadLocalFile.php b/UploadLocalFile.php
index ce50153..034d1ac 100644
--- a/UploadLocalFile.php
+++ b/UploadLocalFile.php
@@ -78,6 +78,7 @@
         * Check if the upload is allowed for the given user and destination 
name.
         * Based on SpecialUpload::processUpload.
         *
+        * @param[in] UploadFromLocalFile $upload
         * @param[in] User $user is the user to check.
         * @param[in] string $desiredDestName the desired destination name of 
the file to be uploaded.
         * @param[in] string $localPath the local path of the file to be 
uploaded.
@@ -86,10 +87,9 @@
         * @param[out] string $errorText is populated with an error message if 
the user is not allowed to upload.
         * @return boolean true if the user is allowed to upload, false if not.
         */
-       static function isUploadAllowedForTitle( $user, $desiredDestName, 
$localPath, $removeLocalFile, $language, &$errorText ) {
+       static function isUploadAllowedForTitle( $upload, $user, 
$desiredDestName, $localPath, $removeLocalFile, $language, &$errorText ) {
                // Initialize path info
                $fileSize = filesize( $localPath );
-               $upload = new UploadFromLocalFile;
                $upload->initializePathInfo( $desiredDestName, $localPath, 
$fileSize, $removeLocalFile );
 
                // Upload verification
@@ -214,26 +214,39 @@
        }
 
        /**
+        * Create a LocalFile from a title
+        *
+        * @param Title $title
+        * @param FileRepo $repo
+        *
+        * @return UploadLocalFile
+        */
+       static function newFromTitle( $title, $repo, $unused = NULL ) {
+               return new self( $title, $repo );
+       }
+
+       /**
         * Upload a file from the given local path to the given destination 
name.
         * Based on SpecialUpload::processUpload
         *
+     * @param[in] UploadFromLocalFile $upload
         * @param[in] string $desiredDestName the desired destination name of 
the file to be uploaded.
         * @param[in] string $localPath the local path of the file to be 
uploaded.
-        * @param[in] User $user is the user performing the upload.
-        * @param[in] string $comment is the upload description.
-        * @param[in] string|null $pageText text to use for the description 
page or null to keep the text of an existing page.
-        * @param[in] bool $watch indicates whether or not to make the user 
watch the new page.
         * @param[in] bool $removeLocalFile remove the local file?
         *
         * @return bool true if the upload succeeds, false if it fails.
         */
-       static function upload( $desiredDestName, $localPath, $user, $comment, 
$pageText, $watch, $removeLocalFile ) {
+       static function uploadWithoutFilePage( $upload, $desiredDestName, 
$localPath, $removeLocalFile ) {
                // Initialize path info
                $fileSize = filesize( $localPath );
-               $upload = new UploadFromLocalFile;
                $upload->initializePathInfo( $desiredDestName, $localPath, 
$fileSize, $removeLocalFile );
 
                $title = $upload->getTitle();
+
+               $comment = '';
+               $pageText = '';
+               $watch = false;
+               $user = null;
 
                $status = $upload->performUpload( $comment, $pageText, $watch, 
$user );
                if ( !$status->isGood() ) {
@@ -241,6 +254,182 @@
                }
 
                RepoGroup::singleton()->clearCache( $title );
+
+               return true;
+       }
+
+       /**
+        * Record a file upload in the image table only
+        * @param string $oldver
+        * @param string $comment
+        * @param string $pageText
+        * @param bool|array $props
+        * @param string|bool $timestamp
+        * @param null|User $user
+        * @return bool
+        */
+       function recordUpload2( $oldver, $comment, $pageText, $props = false, 
$timestamp = false, $user = null )
+       {
+               if ( is_null( $user ) ) {
+                       global $wgUser;
+                       $user = $wgUser;
+               }
+
+               $dbw = $this->repo->getMasterDB();
+               $dbw->begin( __METHOD__ );
+
+               if ( !$props ) {
+                       $props = $this->repo->getFileProps( 
$this->getVirtualUrl() );
+               }
+
+               # Imports or such might force a certain timestamp; otherwise we 
generate
+               # it and can fudge it slightly to keep (name,timestamp) unique 
on re-upload.
+               if ( $timestamp === false ) {
+                       $timestamp = $dbw->timestamp();
+                       $allowTimeKludge = true;
+               } else {
+                       $allowTimeKludge = false;
+               }
+
+               $props['description'] = $comment;
+               $props['user'] = $user->getId();
+               $props['user_text'] = $user->getName();
+               $props['timestamp'] = wfTimestamp( TS_MW, $timestamp ); // DB 
-> TS_MW
+               $this->setProps( $props );
+
+               # major_mime and minor_mime are private in the parent so use 
local variables instead
+               list( $major_mime, $minor_mime ) = self::splitMime( $this->mime 
);
+
+               # Fail now if the file isn't there
+               if ( !$this->fileExists ) {
+                       wfDebug( __METHOD__ . ": File " . $this->getRel() . " 
went missing!\n" );
+                       $dbw->rollback( __METHOD__ );
+
+                       return false;
+               }
+
+               $reupload = false;
+
+               # Test to see if the row exists using INSERT IGNORE
+               # This avoids race conditions by locking the row until the 
commit, and also
+               # doesn't deadlock. SELECT FOR UPDATE causes a deadlock for 
every race condition.
+               $dbw->insert( 'image',
+                       array(
+                               'img_name' => $this->getName(),
+                               'img_size' => $this->size,
+                               'img_width' => intval( $this->width ),
+                               'img_height' => intval( $this->height ),
+                               'img_bits' => $this->bits,
+                               'img_media_type' => $this->media_type,
+                               'img_major_mime' => $major_mime,
+                               'img_minor_mime' => $minor_mime,
+                               'img_timestamp' => $timestamp,
+                               'img_description' => $comment,
+                               'img_user' => $user->getId(),
+                               'img_user_text' => $user->getName(),
+                               'img_metadata' => $dbw->encodeBlob( 
$this->metadata ),
+                               'img_sha1' => $this->sha1
+                       ),
+                       __METHOD__,
+                       'IGNORE'
+               );
+               if ( $dbw->affectedRows() == 0 ) {
+                       if ( $allowTimeKludge ) {
+                               # Use LOCK IN SHARE MODE to ignore any 
transaction snapshotting
+                               $ltimestamp = $dbw->selectField( 'image', 
'img_timestamp',
+                                       array( 'img_name' => $this->getName() ),
+                                       __METHOD__,
+                                       array( 'LOCK IN SHARE MODE' ) );
+                               $lUnixtime = $ltimestamp ? wfTimestamp( 
TS_UNIX, $ltimestamp ) : false;
+                               # Avoid a timestamp that is not newer than the 
last version
+                               # TODO: the image/oldimage tables should be 
like page/revision with an ID field
+                               if ( $lUnixtime && wfTimestamp( TS_UNIX, 
$timestamp ) <= $lUnixtime ) {
+                                       sleep( 1 ); // fast enough re-uploads 
would go far in the future otherwise
+                                       $timestamp = $dbw->timestamp( 
$lUnixtime + 1 );
+                                       $props['timestamp'] = wfTimestamp( 
TS_MW, $timestamp ); // DB -> TS_MW
+                                       #timestamp is private in the parent so 
use setProps to update it
+                                       $this->setProps( $props );
+                               }
+                       }
+
+                       # (bug 34993) Note: $oldver can be empty here, if the 
previous
+                       # version of the file was broken. Allow registration of 
the new
+                       # version to continue anyway, because that's better 
than having
+                       # an image that's not fixable by user operations.
+
+                       $reupload = true;
+                       # Collision, this is an update of a file
+                       # Insert previous contents into oldimage
+                       $dbw->insertSelect( 'oldimage', 'image',
+                               array(
+                                       'oi_name' => 'img_name',
+                                       'oi_archive_name' => $dbw->addQuotes( 
$oldver ),
+                                       'oi_size' => 'img_size',
+                                       'oi_width' => 'img_width',
+                                       'oi_height' => 'img_height',
+                                       'oi_bits' => 'img_bits',
+                                       'oi_timestamp' => 'img_timestamp',
+                                       'oi_description' => 'img_description',
+                                       'oi_user' => 'img_user',
+                                       'oi_user_text' => 'img_user_text',
+                                       'oi_metadata' => 'img_metadata',
+                                       'oi_media_type' => 'img_media_type',
+                                       'oi_major_mime' => 'img_major_mime',
+                                       'oi_minor_mime' => 'img_minor_mime',
+                                       'oi_sha1' => 'img_sha1'
+                               ),
+                               array( 'img_name' => $this->getName() ),
+                               __METHOD__
+                       );
+
+                       # Update the current image row
+                       $dbw->update( 'image',
+                               array( /* SET */
+                                       'img_size' => $this->size,
+                                       'img_width' => intval( $this->width ),
+                                       'img_height' => intval( $this->height ),
+                                       'img_bits' => $this->bits,
+                                       'img_media_type' => $this->media_type,
+                                       'img_major_mime' => $major_mime,
+                                       'img_minor_mime' => $minor_mime,
+                                       'img_timestamp' => $timestamp,
+                                       'img_description' => $comment,
+                                       'img_user' => $user->getId(),
+                                       'img_user_text' => $user->getName(),
+                                       'img_metadata' => $dbw->encodeBlob( 
$this->metadata ),
+                                       'img_sha1' => $this->sha1
+                               ),
+                               array( 'img_name' => $this->getName() ),
+                               __METHOD__
+                       );
+               } else {
+                       # This is a new file, so update the image count
+                       DeferredUpdates::addUpdate( SiteStatsUpdate::factory( 
array( 'images' => 1 ) ) );
+               }
+
+               # Commit the transaction now, in case something goes wrong later
+               # The most important thing is that files don't get lost, 
especially archives
+               # NOTE: once we have support for nested transactions, the 
commit may be moved
+               #       to after $wikiPage->doEdit has been called.
+               $dbw->commit( __METHOD__ );
+
+               # Update memcache after the commit
+               $this->invalidateCache();
+
+               if ( $reupload ) {
+                       # Delete old thumbnails
+                       $this->purgeThumbnails();
+
+                       # Remove the old file from the squid cache
+                       SquidUpdate::purge( array( $this->getURL() ) );
+               }
+
+               # Invalidate cache for all pages using this file
+               $update = new HTMLCacheUpdate( $this->getTitle(), 'imagelinks' 
);
+               $update->doUpdate();
+               if ( !$reupload ) {
+                       LinksUpdate::queueRecursiveJobsForTable( 
$this->getTitle(), 'imagelinks' );
+               }
 
                return true;
        }
@@ -267,4 +456,19 @@
        public function getSourceType() {
                return 'file';
        }
+
+       /**
+        * Return the local file and initializes if necessary.
+        *
+        * @return UploadLocalFile|null
+        */
+       public function getLocalFile() {
+               if ( is_null( $this->mLocalFile ) ) {
+                       $nt = $this->getTitle();
+                       $repo = RepoGroup::singleton()->getLocalRepo();
+                       $this->mLocalFile = is_null( $nt ) ? null : 
UploadLocalFile::newFromTitle( $nt, $repo );
+               }
+
+               return $this->mLocalFile;
+       }
 }

-- 
To view, visit https://gerrit.wikimedia.org/r/344483
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I3aa5957899aad85a9a443a07aed369c60aeb7a49
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/GraphViz
Gerrit-Branch: master
Gerrit-Owner: Welterkj <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to