Exemple #1
0
 /**
  * This is called upon loading the special page.  It should write output to the page with $wgOut.
  * @param string $par the URL path following the special page name
  */
 public function execute($par)
 {
     global $wgOut, $wgArticlePath;
     $dbr = wfGetDB(DB_SLAVE);
     $this->setHeaders();
     /**
      * We need to select ALL pages of the form:
      * 	Documentation:<productShortName>:<manualShortName>TOC*
      * We should group these by manual and then by descending version order.  The simplest way is by assuming that every TOC
      * page is linked to at least one version (category) and thus has an entry in the categorylinks table.  So to do this we
      * must run this query for each manual type, which involes getting the list of manuals defined.
      */
     $out = array();
     // Added for WEB-10802, looking for product name passed in
     // e.g. /Special:TOCList/Splunk
     $parts = explode('/', $par);
     if (isset($parts[0]) && is_string($parts[0]) and $parts[0] != '') {
         $productName = $parts[0];
     } else {
         $productName = PonyDocsProduct::GetSelectedProduct();
     }
     $manuals = PonyDocsProductManual::GetDefinedManuals($productName);
     $allowed_versions = array();
     $product = PonyDocsProduct::GetProductByShortName($productName);
     $wgOut->setPagetitle('Table of Contents Management');
     if ($product) {
         $wgOut->addHTML('<h2>Table of Contents Management Pages for ' . $product->getLongName() . '</h2>');
         foreach (PonyDocsProductVersion::GetVersions($productName) as $v) {
             $allowed_versions[] = $v->getVersionName();
         }
         foreach ($manuals as $pMan) {
             $res = $dbr->select(array('categorylinks', 'page'), array('page_title', 'GROUP_CONCAT( cl_to separator "|") categories'), array('cl_from = page_id', 'page_namespace = "' . NS_PONYDOCS . '"', "page_title LIKE '" . $dbr->strencode("{$productName}:" . $pMan->getShortName()) . "TOC%'", 'cl_to LIKE "V:%:%"', 'cl_type = "page"'), __METHOD__, array('GROUP BY' => 'page_title'));
             while ($row = $dbr->fetchObject($res)) {
                 $versions = array();
                 $categories = explode('|', $row->categories);
                 foreach ($categories as $category) {
                     $categoryParts = explode(':', $category);
                     if (in_array($categoryParts[2], $allowed_versions)) {
                         $versions[] = $categoryParts[2];
                     }
                 }
                 if (sizeof($versions)) {
                     $wgOut->addHTML('<a href="' . str_replace('$1', PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ":{$row->page_title}", $wgArticlePath) . '">' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ":{$row->page_title}" . '</a> - Versions: ' . implode(' | ', $versions) . '<br />');
                 }
             }
         }
         $html = '<h2>Other Useful Management Pages</h2>' . '<a href="' . str_replace('$1', PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':' . $productName . PONYDOCS_PRODUCTVERSION_SUFFIX, $wgArticlePath) . '">Version Management</a> - Define and update available ' . $productName . ' versions.<br />' . '<a href="' . str_replace('$1', PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':' . $productName . PONYDOCS_PRODUCTMANUAL_SUFFIX, $wgArticlePath) . '">Manuals Management</a> - Define the list of available manuals for the Documentation namespace.' . '<br/><br/>';
         $wgOut->addHTML($html);
     } else {
         $wgOut->addHTML("<h2>Table of Contents Management Page</h2>Error: Product {$productName} does not exist.");
     }
 }
 /**
  * This is called upon loading the special page.  It should write output to the page with $wgOut.
  * @param string $par the URL path following the special page name
  */
 public function execute($par)
 {
     global $wgOut, $wgUser;
     $this->setHeaders();
     $parts = explode('/', $par);
     $productName = isset($parts[0]) ? $parts[0] : PonyDocsProduct::GetSelectedProduct();
     $manualName = isset($parts[1]) ? $parts[1] : NULL;
     // Security Check
     $authProductGroup = PonyDocsExtension::getDerivedGroup(PonyDocsExtension::ACCESS_GROUP_PRODUCT, $productName);
     $groups = $wgUser->getGroups();
     if (!in_array($authProductGroup, $groups)) {
         $wgOut->addHTML('<p>Sorry, but you do not have permission to access this Special page.</p>');
         return;
     }
     $product = PonyDocsProduct::GetProductByShortName($productName);
     $productLongName = $product->getLongName();
     if (!is_null($manualName)) {
         $manual = PonyDocsProductManual::GetManualByShortName($productName, $manualName);
         $manualLongName = $manual->getLongName();
     } else {
         $manual = NULL;
     }
     $wgOut->setPagetitle('Static Documentation Import Tool');
     $h2 = "Static Documentation Import for {$productLongName}";
     if (!is_null($manualName)) {
         $h2 .= ", {$manualLongName}";
     }
     $wgOut->addHTML("<h2>{$h2}</h2>");
     if ($this->validateProductAndManualAreStatic($product, $manual)) {
         if (isset($_POST['action'])) {
             $this->processImportForm($_POST['action'], $product, $manual);
         } else {
             $this->showImportForm($product, $manual);
         }
     }
     $this->showExistingVersions($product, $manual);
     $this->showHelpfulLinks($productName);
 }
 /**
  * When an article is fully saved, we want to update the doclinks for that 
  * article in our doclinks table.  Only if it's in the documentation 
  * namepsace, however.
  * 
  * @param WikiPage $article
  * @param User $user
  * @param string $text
  * @param string $summary
  * @param boolean $minor
  * @param boolean $watch
  * @param $sectionanchor
  * @param integer $flags
  * @param Revision $revision
  * @param Status $status
  * @param integer $baseRevId
  * 
  * @deprecated Replace with PageContentSaveComplete hook
  *
  */
 public static function onArticleSaveComplete(&$article, &$user, $text, $summary, $minoredit, $watchthis, $sectionanchor, &$flags, $revision, &$status, $baseRevId)
 {
     $title = $article->getTitle();
     $realArticle = Article::newFromWikiPage($article, RequestContext::getMain());
     // Update doc links
     PonyDocsExtension::updateOrDeleteDocLinks("update", $realArticle, $text);
     if (!preg_match('/^' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':/i', $title->__toString(), $matches)) {
         return TRUE;
     }
     // Okay, article is in doc namespace
     // Now we need to remove any pdf books for this topic.
     // Since the person is editing the article, it's safe to say that the
     // version and manual can be fetched from the classes and not do any
     // manipulation on the article itself.
     $productName = PonyDocsProduct::GetSelectedProduct();
     $product = PonyDocsProduct::GetProductByShortName($productName);
     $version = PonyDocsProductVersion::GetSelectedVersion($productName);
     $manual = PonyDocsProductManual::GetCurrentManual($productName, $title);
     if ($manual != null) {
         // Then we are in the documentation namespace, but we're not part of
         // manual.
         // Clear any PDF for this manual
         PonyDocsPdfBook::removeCachedFile($productName, $manual->getShortName(), $version);
     }
     // Clear all TOC cache entries for each version.
     // Dangerous.  Only set the flag if you know that you should be skipping this processing.
     // Currently used for branch/inherit.
     if ($manual && !PonyDocsExtension::isSpeedProcessingEnabled()) {
         // Clear any TOC cache entries this article may be related to.
         $topic = new PonyDocsTopic($realArticle);
         $manVersionList = $topic->getProductVersions();
         foreach ($manVersionList as $version) {
             PonyDocsTOC::clearTOCCache($manual, $version, $product);
             PonyDocsProductVersion::clearNAVCache($version);
         }
     }
     PonyDocsExtension::clearArticleCategoryCache($realArticle);
     // if this is product versions or manuals page, clear navigation cache
     if (preg_match(PONYDOCS_PRODUCTVERSION_TITLE_REGEX, $title->__toString(), $matches) || preg_match(PONYDOCS_PRODUCTMANUAL_TITLE_REGEX, $title->__toString(), $matches)) {
         // reload to get updated version list
         PonyDocsProductVersion::LoadVersionsForProduct($productName, true);
         $prodVersionList = PonyDocsProductVersion::GetVersions($productName);
         foreach ($prodVersionList as $version) {
             PonyDocsProductVersion::clearNAVCache($version);
         }
     }
     return true;
 }
 /**
  * Called when an unknown action occurs on url.  We are only interested in zipmanual action.
  */
 function onUnknownAction($action, $article)
 {
     global $wgOut, $wgUser, $wgTitle, $wgParser, $wgRequest;
     global $wgServer, $wgArticlePath, $wgScriptPath, $wgUploadPath, $wgUploadDirectory, $wgScript, $wgStylePath;
     // We don't do any processing unless it's zipmanual
     if ($action != 'zipmanual') {
         return true;
     }
     $zipAllowed = false;
     PonyDocsExtension::onUserCan($wgTitle, $wgUser, 'zipmanual', &$zipAllowed);
     if (!$zipAllowed) {
         error_log("WARNING [" . __METHOD__ . "] User attempted to perform a ZIP Export without permission.");
         $defaultRedirect = PonyDocsExtension::getDefaultUrl();
         header("Location: " . $defaultRedirect);
         exit;
     }
     // Get the title and make sure we're in Documentation namespace
     $title = $article->getTitle();
     if ($title->getNamespace() != NS_PONYDOCS) {
         return true;
     }
     // Grab parser options for the logged in user.
     $opt = ParserOptions::newFromUser($wgUser);
     // Any potential titles to exclude
     $exclude = array();
     // Determine articles to gather
     $articles = array();
     $pieces = explode(":", $wgTitle->__toString());
     // Try and get rid of the TOC portion of the title
     if (strpos($pieces[2], "TOC") && count($pieces) == 3) {
         $pieces[2] = substr($pieces[2], 0, strpos($pieces[2], "TOC"));
     } else {
         if (count($pieces) != 5) {
             // something is wrong, let's get out of here
             $defaultRedirect = PonyDocsExtension::getDefaultUrl();
             if (PONYDOCS_DEBUG) {
                 error_log("DEBUG [" . __METHOD__ . ":" . __LINE__ . "] redirecting to {$defaultRedirect}");
             }
             header("Location: " . $defaultRedirect);
             exit;
         }
     }
     $productName = $pieces[1];
     $ponydocs = PonyDocsWiki::getInstance($productName);
     $pProduct = PonyDocsProduct::GetProductByShortName($productName);
     if ($pProduct === NULL) {
         // product wasn't valid
         wfProfileOut(__METHOD__);
         $wgOut->setStatusCode(404);
         return FALSE;
     }
     $productLongName = $pProduct->getLongName();
     if (PonyDocsProductManual::isManual($productName, $pieces[2])) {
         $pManual = PonyDocsProductManual::GetManualByShortName($productName, $pieces[2]);
     }
     $versionText = PonyDocsProductVersion::GetSelectedVersion($productName);
     if (!empty($pManual)) {
         // We should always have a pManual, if we're printing
         // from a TOC
         $v = PonyDocsProductVersion::GetVersionByName($productName, $versionText);
         $toc = new PonyDocsTOC($pManual, $v, $pProduct);
         list($manualtoc, $tocprev, $tocnext, $tocstart) = $toc->loadContent();
         // We successfully got our table of contents.  It's
         // stored in $manualtoc
         foreach ($manualtoc as $tocEntry) {
             if ($tocEntry['level'] > 0 && strlen($tocEntry['title']) > 0) {
                 $title = Title::newFromText($tocEntry['title']);
                 $articles[$tocEntry['section']][] = array('title' => $title, 'text' => $tocEntry['text']);
             }
         }
     } else {
         error_log("WARNING [" . __METHOD__ . "] " . php_uname('n') . ": User attempted to export ZIP from a non TOC page with path:" . $wgTitle->__toString());
     }
     $html = self::getManualHTML($pProduct, $pManual, $v);
     $coverPageHTML = self::getCoverPageHTML($pProduct, $pManual, $v, false);
     // Make a temporary directory to store our archive contents.
     $tempDirPath = sys_get_temp_dir() . '/ponydocs-zip-export-' . time();
     $success = @mkdir($tempDirPath);
     if (!$success) {
         error_log("FATAL [" . __METHOD__ . "] Failed to create temporary directory " . $tempDirPath . " for Zip Export.");
         throw new Exception('Failed to create temporary directory for Zip Export.');
     }
     // Now, let's fetch all the img elements for both and grab them all in
     // parallel.
     $imgData = array();
     // Initialize our RollingCurl instance
     $rollingCurl = new \RollingCurl\RollingCurl();
     $mh = curl_multi_init();
     $manualDoc = new DOMDocument();
     @$manualDoc->loadHTML($html);
     $coverPageDoc = new DOMDocument();
     @$coverPageDoc->loadHTML($coverPageHTML);
     self::prepareImageRequests($manualDoc, $rollingCurl, $tempDirPath, &$imgData);
     self::prepareImageRequests($coverPageDoc, $rollingCurl, $tempDirPath, &$imgData);
     // Execute the RollingCurl requests
     $rollingCurl->execute();
     // Now update all our image elements in our appropriate DOMDocs.
     foreach ($imgData as $img) {
         // Put the data into it.
         file_put_contents($img['local_path'], $img['request']->getResponseText());
         // Modify element
         $img['element']->setAttribute('src', $img['new_path']);
         // Do curl cleanup
     }
     $html = $manualDoc->saveHTML();
     $coverPageHTML = $coverPageDoc->saveHTML();
     // Write the HTML to a tmp file
     $file = tempnam($tempDirPath, "zipexport-");
     $fh = fopen($file, 'w+');
     fwrite($fh, $html);
     fclose($fh);
     // Okay, write the title page
     $titlepagefile = tempnam($tempDirPath, "zipexport-");
     $fh = fopen($titlepagefile, 'w+');
     fwrite($fh, $coverPageHTML);
     fclose($fh);
     // Disable output of our standard mediawiki output.  We will be outputting a zip file instead.
     $wgOut->disable();
     // Create ZIP Archive which contains a cover and manual html
     $zip = new ZipArchive();
     $tempZipFilePath = tempnam($tempDirPath, "zipexport-");
     $zipFileName = $productName . '-' . $versionText . '-' . $pManual->getShortName() . '.zip';
     $zip->open($tempZipFilePath, ZipArchive::OVERWRITE);
     $zip->addFile($titlepagefile, 'cover.html');
     $zip->addFile($file, 'manual.html');
     // Iterate through all the images
     foreach ($imgData as $img) {
         $zip->addFile($img['local_path'], $img['new_path']);
     }
     $zip->close();
     header("Content-Type: application/zip");
     header("Content-Length: " . filesize($tempZipFilePath));
     header("Content-Disposition: attachment; filename=\"" . $zipFileName . "\"");
     readfile($tempZipFilePath);
     // Now remove all temp files
     self::rrmdir($tempDirPath);
     // Okay, let's add an entry to the error log to dictate someone requested a pdf
     error_log("INFO [" . __METHOD__ . "] " . php_uname('n') . ": zip export serve username=\"" . $wgUser->getName() . "\" version=\"{$versionText}\" " . " manual=\"" . $pManual->getShortName() . "\"");
     // No more processing
     return false;
 }
 /**
  * Branches a topic from a source title to a target title.
  *
  * @param $topicTitle string The name of the internal topic.
  * @param $version PonyDocsVersion The target Version
  * @param $tocSection The TOC section this title resides in.
  * @param $tocTitle The toc title that references this topic.
  * @param $deleteExisting boolean Should we purge any existing conflicts?
  * @param $split Should we create a new page?
  * @returns boolean
  */
 static function branchTopic($topicTitle, $version, $tocSection, $tocTitle, $deleteExisting, $split)
 {
     // Clear any hooks so no weirdness gets called after we create the
     // branch
     $wgHooks['ArticleSave'] = array();
     if (!preg_match('/^' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':([^:]*):([^:]*):(.*):([^:]*)$/', $topicTitle, $match)) {
         throw new Exception("Invalid Title to Branch From");
     }
     $productName = $match[1];
     $manualName = $match[2];
     $title = $match[3];
     // Get the PonyDocsProduct and PonyDocsProductManual
     $product = PonyDocsProduct::GetProductByShortName($productName);
     $manual = PonyDocsProductManual::GetManualByShortName($productName, $manualName);
     // Get conflicts.
     $conflicts = self::getConflicts($product, $topicTitle, $version);
     if (!empty($conflicts)) {
         if ($deleteExisting && !$split) {
             // We want to purge each conflicting title completely.
             foreach ($conflicts as $conflict) {
                 $article = new Article(Title::newFromText($conflict));
                 if (!$article->exists()) {
                     // Article doesn't exist. Should never occur, but if it doesn't, no big deal since it was a conflict.
                     continue;
                 }
                 if ($conflict == $topicTitle) {
                     // Then the conflict is same as source material, do nothing.
                     continue;
                 } else {
                     // Do actual delete.
                     $article->doDelete("Requested purge of conficting article when branching topic " . $topicTitle . " with version: " . $version->getVersionName(), false);
                 }
             }
         } elseif (!$split) {
             // Ruh oh, there's conflicts and we didn't want to purge or split. Cancel out.
             throw new Exception("When calling branchTitle, there were conflicts and purge was not requested and we're not splitting.");
         }
     }
     // Load existing article to branch from
     $existingArticle = PonyDocsArticleFactory::getArticleByTitle($topicTitle);
     if (!$existingArticle->exists()) {
         // No such title exists in the system
         throw new Exception("Invalid Title to Branch From. Target Article does not exist:" . $topicTitle);
     }
     $title = PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':' . $product->getShortName() . ':' . $manual->getShortName() . ':' . $title . ':' . $version->getVersionName();
     $newArticle = PonyDocsArticleFactory::getArticleByTitle($title);
     if ($newArticle->exists()) {
         throw new Exception("Article already exists:" . $title);
     }
     // Copy content
     $existingContent = $existingArticle->getContent();
     $newContent = $existingContent;
     // Build the versions which will go into the new array.
     $newVersions = array();
     // Text representation of the versions
     $newVersions[] = $version->getVersionName();
     if ($split) {
         // We need to get all versions from PonyDocsVersion
         $rawVersions = PonyDocsProductVersion::GetVersions($productName);
         $existingVersions = array();
         foreach ($rawVersions as $rawVersion) {
             $existingVersions[] = $rawVersion->getVersionName();
         }
         // $existingVersions is now an array of version names in incremental order
         $versionIndex = array_search($version->getVersionName(), $existingVersions);
         // versionIndex is the index where our target version is
         // we will use this to determine what versions need to be brought over.
         preg_match_all("/\\[\\[Category:V:([^\\]]*):([^\\]]*)\\]\\]/", $existingContent, $matches);
         foreach ($matches[2] as $match) {
             $index = array_search($match, $existingVersions);
             if ($index > $versionIndex) {
                 $newVersions[] = $match;
             }
         }
     }
     // $newVersions contains all the versions that need to be pulled from the existing Content and put into the new content.
     // So let's now remove it form the original content
     foreach ($newVersions as $tempVersion) {
         $existingContent = preg_replace("/\\[\\[Category:V:" . $productName . ":" . $tempVersion . "\\]\\]/", "", $existingContent);
     }
     // Now let's do the edit on the original content.
     // Set version and manual
     $existingArticle->doEdit($existingContent, "Removed versions from existing article when branching Topic " . $topicTitle, EDIT_UPDATE);
     // Clear categories tags from new article content
     $newContent = preg_replace("/\\[\\[Category:V:([^\\]]*)]]/", "", $newContent);
     // add new category tags to new content
     foreach ($newVersions as $version) {
         $newContent .= "[[Category:V:" . $productName . ":" . $version . "]]";
     }
     $newContent .= "\n";
     // doEdit on new article
     $newArticle->doEdit($newContent, "Created new topic from branched topic " . $topicTitle, EDIT_NEW);
     return $title;
 }
/**
 * This expects to find:
 * 	{{#topic:Text Name}}
 *
 * @param Parser $parser
 * @param string $param1 Full text name of topic, must be converted to wiki topic name.
 * @return array
 * 
 * TODO: Much of this function duplicates code above in efGetTitleFromMarkup(), can we DRY?
 *       There really shouldn't be any real code in this file, just calls to class methods...
 */
function efTopicParserFunction_Render(&$parser, $param1 = '')
{
    global $wgArticlePath, $wgTitle, $action;
    if (PonyDocsExtension::isSpeedProcessingEnabled()) {
        return TRUE;
    }
    /**
     * We ignore this parser function if not in a TOC management page.
     */
    if (!preg_match('/' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':(.*):(.*)TOC(.*)/i', $wgTitle->__toString(), $matches)) {
        return FALSE;
    }
    $manualShortName = $matches[2];
    $productShortName = $matches[1];
    PonyDocsWiki::getInstance($productShortName);
    /**
     * Get the earliest tagged version of this TOC page and append it to the wiki page?
     * Ensure the manual is valid then use PonyDocsManual::getManualByShortName().
     * Next attempt to get the version tags for this page -- which may be NONE -- 
     * and from this determine the "earliest" version to which this page applies.
     * 
     * TODO: This comment is duplicated above in efGetTitleFromMarkup, can we DRY?
     */
    if (!PonyDocsProductManual::IsManual($productShortName, $manualShortName)) {
        return FALSE;
    }
    $pManual = PonyDocsProductManual::GetManualByShortName($productShortName, $manualShortName);
    $pTopic = new PonyDocsTopic(new Article($wgTitle));
    /**
     * @FIXME: If TOC page is NOT tagged with any versions we cannot create the pages/links to the 
     * topics, right?
     */
    $manVersionList = $pTopic->getProductVersions();
    if (!sizeof($manVersionList)) {
        return $parser->insertStripItem($param1, $parser->mStripState);
    }
    $earliestVersion = PonyDocsProductVersion::findEarliest($productShortName, $manVersionList);
    /**
     * Clean up the full text name into a wiki-form. This means remove spaces, #, ?, and a few other
     * characters which are not valid or wanted. It's not important HOW this is done as long as it is
     * consistent.
     */
    $wikiTopic = preg_replace('/([^' . str_replace(' ', '', Title::legalChars()) . '])/', '', $param1);
    $wikiPath = PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':' . $productShortName . ':' . $manualShortName . ':' . $wikiTopic;
    $dbr = wfGetDB(DB_SLAVE);
    /**
     * Now look in the database for any instance of this topic name PLUS :<version>.
     * We need to look in categorylinks for it to find a record with a cl_to (version tag)
     * which is equal to the set of the versions for this TOC page.
     * For instance, if the TOC page was for versions 1.0 and 1.1 and our topic was 'How To Foo'
     * we need to find any cl_sortkey which is 'HowToFoo:%' and has a cl_to equal to 1.0 or 1.1.
     * There should only be 0 or 1, so we ignore anything beyond 1.
     * If found, we use THAT cl_sortkey as the link;
     * if NOT found we create a new topic, the name being the compressed topic name plus the earliest TOC version
     * ($earliestVersion->getName()).
     * We then need to ACTUALLY create it in the database, tag it with all the versions the TOC mgmt page is tagged with,
     * and set the H1 to the text inside the parser function.
     * 
     * @fixme: Can we test if $action=save here so we don't do this on every page view? 
     */
    $versionIn = array();
    foreach ($manVersionList as $pV) {
        $versionIn[] = $productShortName . ':' . $pV->getVersionName();
    }
    $res = $dbr->select(array('categorylinks', 'page'), 'page_title', array('cl_from = page_id', 'page_namespace = "' . NS_PONYDOCS . '"', "cl_to IN ('V:" . implode("','V:", $versionIn) . "')", 'cl_type = "page"', "cl_sortkey LIKE '" . $dbr->strencode(strtoupper($productShortName . ':' . $manualShortName . ':' . $wikiTopic)) . ":%'"), __METHOD__);
    $topicName = '';
    if (!$res->numRows()) {
        /**
         * No match -- so this is a "new" topic. Set name.
         */
        $topicName = PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':' . $productShortName . ':' . $manualShortName . ':' . $wikiTopic . ':' . $earliestVersion->getVersionName();
    } else {
        $row = $dbr->fetchObject($res);
        $topicName = PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ":{$row->page_title}";
    }
    $output = '<a href="' . wfUrlencode(str_replace('$1', $topicName, $wgArticlePath)) . '">' . $param1 . '</a>';
    return $parser->insertStripItem($output, $parser->mStripState);
}
 /**
  * Processes a rename version request for a single manual
  *
  * @param $jobID The unique id for this job (see ajaxFetchJobID)
  * @param $productName string product short name
  * @param $manualName string manual short name
  * @param $sourceVersionName string String representation of the source version
  * @param $targetVersionName string String representaiton of the target version
  * @return string Full job log of the process by printing to stdout.
  */
 public static function ajaxProcessManual($jobID, $productName, $manualName, $sourceVersionName, $targetVersionName)
 {
     global $wgScriptPath;
     ob_start();
     list($msec, $sec) = explode(' ', microtime());
     $startTime = (double) $msec + (double) $sec;
     $logFields = "action=start status=success product={$productName} manual={$manualName} " . "sourceVersion={$sourceVersionName} targetVersion={$targetVersionName}";
     error_log('INFO [' . __METHOD__ . "] [RenameVersion] {$logFields}");
     // Validate product and versions
     $product = PonyDocsProduct::GetProductByShortName($productName);
     $manual = PonyDocsProductManual::getManualByShortName($productName, $manualName);
     $sourceVersion = PonyDocsProductVersion::GetVersionByName($productName, $sourceVersionName);
     $targetVersion = PonyDocsProductVersion::GetVersionByName($productName, $targetVersionName);
     if (!($product && $manual && $sourceVersion && $targetVersion)) {
         $result = array('success', false);
         $result = json_encode($result);
         return $result;
     }
     // TODO: Is this necessary? Haven't we done this already?
     PonyDocsProductVersion::SetSelectedVersion($productName, $sourceVersionName);
     print "Beginning process job for manual: {$manualName}<br />";
     print "Source version is {$productName}:{$sourceVersionName}<br />";
     print "Target version is {$targetVersionName}<br />";
     // Get topics
     // Update log file
     // TODO: Pull this out into a separate method somewhere
     $path = PonyDocsExtension::getTempDir() . $jobID;
     $fp = fopen($path, "w+");
     fputs($fp, "Getting Topics for {$manualName}");
     fclose($fp);
     // TODO: This is copied form SpecialBranchInherit::ajaxFetchTopics() and should get DRYed out
     $manualTopics = array();
     $TOC = new PonyDocsTOC($manual, $sourceVersion, $product);
     list($toc, $prev, $next, $start) = $TOC->loadContent();
     // Time to iterate through all the items.
     $section = '';
     foreach ($toc as $tocItem) {
         if ($tocItem['level'] == 0) {
             $section = $tocItem['text'];
             $manualTopics[$manualName]['sections'][$section] = array();
             $manualTopics[$manualName]['sections'][$section]['meta'] = array();
             $manualTopics[$manualName]['sections'][$section]['topics'] = array();
         }
         // actual topic
         if ($tocItem['level'] == 1) {
             $tempEntry = array('title' => $tocItem['title'], 'text' => $tocItem['text'], 'toctitle' => $tocItem['toctitle'], 'conflicts' => PonyDocsBranchInheritEngine::getConflicts($product, $tocItem['title'], $targetVersion));
             $manualTopics[$manualName]['sections'][$section]['topics'][] = $tempEntry;
         }
     }
     foreach ($manualTopics as $manualName => $manual) {
         foreach ($manual['sections'] as $sectionIndex => $section) {
             if (count($section['topics']) == 0) {
                 unset($manualTopics[$manualName]['sections'][$sectionIndex]);
             }
         }
     }
     $logFields = "action=topics status=success product={$productName} manual={$manualName} " . "sourceVersion={$sourceVersionName} targetVersion={$targetVersionName}";
     error_log('INFO [' . __METHOD__ . "] [RenameVersion] {$logFields}");
     // Enable speed processing to avoid any unnecessary processing on topics modified by this tool.
     // TODO: I'm not 100% sure this is necessary or proper here -RU
     PonyDocsExtension::setSpeedProcessing(TRUE);
     // Determine how many topics there are to process so that we can keep track of progress
     $numOfTopics = 0;
     $numOfTopicsCompleted = 0;
     foreach ($manualTopics as $manualName => $manualData) {
         foreach ($manualData['sections'] as $sectionName => $section) {
             // The following is a goofy fix for some browsers.
             // Sometimes the JSON comes along with null values for the first element.
             // It's just an additional element, so we can drop it.
             // TODO: Since we're no longer getting this from JSON, this is probably removeable
             if (empty($section['topics'][0]['text'])) {
                 array_shift($manualTopics[$manualName]['sections'][$sectionName]['topics']);
             }
             $numOfTopics += count($manualTopics[$manualName]['sections'][$sectionName]['topics']);
         }
     }
     foreach ($manualTopics as $manualName => $manualData) {
         // TODO: We already got the manual above, why make another? We could add the manual to $manualTopics somewhere..
         $manual = PonyDocsProductManual::GetManualByShortName($productName, $manualName);
         // First update all the topics
         print '<div class="normal">Processing topics</div>';
         foreach ($manualData['sections'] as $sectionName => $section) {
             print "<div class=\"normal\">Processing section {$sectionName}</div>";
             foreach ($section['topics'] as $topic) {
                 // Update log file
                 $fp = fopen($path, "w+");
                 fputs($fp, "Renaming topics in manual {$manualName}<br />" . "Completed {$numOfTopicsCompleted} of {$numOfTopics} Total: " . (int) ($numOfTopicsCompleted / $numOfTopics * 100) . '%');
                 fclose($fp);
                 try {
                     print '<div class="normal">Attempting to update topic ' . $topic['title'] . '...';
                     PonyDocsRenameVersionEngine::changeVersionOnTopic($topic['title'], $sourceVersion, $targetVersion);
                     $logFields = "action=topic status=success product={$productName} manual={$manualName} " . "title={$topic['title']} sourceVersion={$sourceVersionName} targetVersion={$targetVersionName}";
                     error_log('INFO [' . __METHOD__ . "] [RenameVersion] {$logFields}");
                     print 'Complete</div>';
                 } catch (Exception $e) {
                     $logFields = "action=topic status=failure error={$e->getMessage()} product={$productName} manual={$manualName} " . "title={$topic['title']} sourceVersion={$sourceVersionName} targetVersion={$targetVersionName}";
                     error_log('WARNING [' . __METHOD__ . "] [RenameVersion] {$logFields}");
                     print '</div><div class="error">Exception: ' . $e->getMessage() . '</div>';
                 }
                 $numOfTopicsCompleted++;
             }
         }
         // Now we can update the TOC
         // Determine if TOC already exists for target version.
         if (!PonyDocsBranchInheritEngine::TOCExists($product, $manual, $sourceVersion)) {
             print '<div class="normal">TOC Does not exist for Manual ' . $manual->getShortName() . ' with version ' . $targetVersion->getVersionName() . '</div>';
         } else {
             try {
                 print '<div class="normal">Attempting to update TOC...';
                 PonyDocsRenameVersionEngine::changeVersionOnTOC($product, $manual, $sourceVersion, $targetVersion);
                 $logFields = "action=TOC status=success product={$productName} manual={$manualName} " . "sourceVersion={$sourceVersionName} targetVersion={$targetVersionName}";
                 error_log('INFO [' . __METHOD__ . "] [RenameVersion] {$logFields}");
                 print 'Complete</div>';
             } catch (Exception $e) {
                 $logFields = "action=TOC status=failure error={$e->getMessage()} product={$productName} manual={$manualName} " . "sourceVersion={$sourceVersionName} targetVersion={$targetVersionName}";
                 error_log('WARNING [' . __METHOD__ . "] [RenameVersion] {$logFields}");
                 print '</div><div class="error">Exception: ' . $e->getMessage() . '</div>';
             }
         }
     }
     list($msec, $sec) = explode(' ', microtime());
     $endTime = (double) $msec + (double) $sec;
     print "Done with {$manualName}! Execution Time: " . round($endTime - $startTime, 3) . ' seconds<br />';
     //WEB-10792, Clear TOCCACHE for the target version only, each Manual at a time
     PonyDocsTOC::clearTOCCache($manual, $targetVersion, $product);
     //Also clear the NAVCache for the target version
     PonyDocsProductVersion::clearNAVCache($targetVersion);
     unlink($path);
     $buffer = ob_get_clean();
     return $buffer;
 }
Exemple #8
0
 /**
  * This returns the list of available manuals (active ones) in a more useful way for templates.  It is an associative array
  * where the key is the short name of the manual and the value is the display/long name.
  *
  * @return array
  */
 public function getManualsForProduct($product)
 {
     PonyDocsProductVersion::LoadVersionsForProduct($product);
     // Dependency
     PonyDocsProductVersion::getSelectedVersion($product);
     $manuals = PonyDocsProductManual::LoadManualsForProduct($product);
     // Dependency
     $out = array();
     foreach ($manuals as $m) {
         $out[$m->getShortName()] = $m->getLongName();
     }
     return $out;
 }
Exemple #9
0
 public function prepareDocumentation()
 {
     global $wgOut, $wgParser, $wgScriptPath, $wgTitle, $wgUser;
     /**
      * We need a lot of stuff from our PonyDocs extension!
      */
     $ponydocs = PonyDocsWiki::getInstance($this->data['selectedProduct']);
     $this->data['manuals'] = $ponydocs->getManualsForProduct($this->data['selectedProduct']);
     /**
      * Adjust content actions as needed, such as add 'view all' link.
      */
     $this->contentActions();
     $this->navURLS();
     /**
      * Possible topic syntax we must handle:
      * 
      * Documentation:<topic> *Which may include a version tag at the end, we don't care about this.
      * Documentation:<productShortName>:<manualShortName>:<topic>:<version>
      * Documentation:<productShortName>:<manualShortName>
      */
     /**
      * Based on the name; i.e. 'Documentation:Product:Manual:Topic' we need to parse it out and store the manual name and
      * the topic name as parameters. We store manual in 'manualname' and topic in 'topicname'. Special handling
      * needs to be done for versions and TOC?
      *
      * 	0=NS (Documentation)
      *  1=Product (Short name)
      *  2=Manual (Short name)
      *  3=Topic
      *  4=Version
      */
     $pManual = null;
     $pieces = explode(':', $wgTitle->__toString());
     $helpClass = '';
     /**
      * This isn't a specific topic+version -- handle appropriately.
      */
     if (sizeof($pieces) < 4) {
         if (!strcmp(PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':' . $this->data['selectedProduct'] . PONYDOCS_PRODUCTVERSION_SUFFIX, $wgTitle->__toString())) {
             $this->data['titletext'] = 'Versions Management - ' . $this->data['selectedProduct'];
             $wgOut->addHTML('<br><span class="' . $helpClass . '"><i>* Use {{#version:name|status}} to define a new version,' . ' where status is released, unreleased, or preview.' . ' Valid chars in version name are A-Z, 0-9, period, comma, and dash.</i></span>');
             $wgOut->addHTML('<br><span class="' . $helpClass . '"><i>* Use {{#versiongroup:name|message}} to set a banner' . ' message that will appear on every topic in every version following the versiongroup.</i></span>');
         } elseif (!strcmp(PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':' . $this->data['selectedProduct'] . PONYDOCS_PRODUCTMANUAL_SUFFIX, $wgTitle->__toString())) {
             $this->data['titletext'] = 'Manuals Management - ' . $this->data['selectedProduct'];
             $wgOut->addHTML('<br><span class="' . $helpClass . '"><i>' . '* Use {{#manual:manualShortName|displayName|categories}} to define a new manual.');
             $wgOut->addHTML('<br><span class="' . $helpClass . '"><i>' . '* Prepend manual short name with ' . PONYDOCS_PRODUCT_STATIC_PREFIX . ' to define a static manual.' . '</i></span>');
             $wgOut->addHTML('<br><span class="' . $helpClass . '">' . '<i>* If you omit display name, the short name will be used in links.</i></span>');
             $wgOut->addHTML('<br><span class="' . $helpClass . '">' . '<i>* Categories is a comma-separated list of categories</i></span>');
         } elseif (!strcmp(PONYDOCS_DOCUMENTATION_PRODUCTS_TITLE, $wgTitle->__toString())) {
             $this->data['titletext'] = 'Products Management';
             $wgOut->addHTML('<br><span class="' . $helpClass . '"><i>' . '* Use {{#product:productShortName|displayName|description|parent|categories}} to define a new product.' . '</i></span>');
             $wgOut->addHTML('<br><span class="' . $helpClass . '"><i>' . '* Prepend product short name with ' . PONYDOCS_PRODUCT_STATIC_PREFIX . ' to define a static product.' . '</i></span>');
             $wgOut->addHTML('<br><span class="' . $helpClass . '"><i>' . '* displayName, description, parent, and categories can be left empty.</i></span>');
             $wgOut->addHTML('<br><span class="' . $helpClass . '">' . '<i>* If you leave displayName empty, productShortName will be used in links.</i></span>');
             $wgOut->addHTML('<br><span class="' . $helpClass . '">' . '<i>* Categories is a comma-separated list of categories.</i></span>');
             $wgOut->addHTML('<br><span class="' . $helpClass . '">' . '<i>* Each product here <b>MUST</b> also be listed in $ponyDocsProductsList,' . ' usually configured in LocalSettings.php.</i></span>');
         } elseif (preg_match('/(.*)TOC(.*)/', $pieces[2], $matches)) {
             $this->data['titletext'] = $matches[1] . ' Table of Contents Page';
             $wgOut->addHTML('<br><span class="' . $helpClass . '"><i>' . '* Optionally start this page with {{#manualDescription:Manual Description.}}' . ' followed by two line-breaks to set a manual description for the Manual this TOC belongs to.' . '</i></span>');
             $wgOut->addHTML('<br><span class="' . $helpClass . '"><i>' . '* Topics are grouped into sections by section headers.' . ' Any line without markup is considered a section header.' . ' A section header is required before the the first topic tag.</i></span>');
             $wgOut->addHTML('<br><span class="' . $helpClass . '"><i>' . '* Topic tags must be part of an unordered list.' . ' Use {{#topic:Display Name}} after a * (list item markup) to create topics.</i></span>');
         } elseif (sizeof($pieces) >= 2 && PonyDocsProductManual::IsManual($pieces[1], $pieces[2])) {
             $pManual = PonyDocsProductManual::GetManualByShortName($pieces[1], $pieces[2]);
             if ($pManual) {
                 $this->data['manualname'] = $pManual->getLongName();
             } else {
                 $this->data['manualname'] = $pieces[2];
             }
             $this->data['topicname'] = $pieces[3];
             $this->data['titletext'] = $pieces[2];
         } else {
             $this->data['topicname'] = $pieces[2];
         }
     } else {
         $pManual = PonyDocsProductManual::GetManualByShortName($pieces[1], $pieces[2]);
         if ($pManual) {
             $this->data['manualname'] = $pManual->getLongName();
         } else {
             $this->data['manualname'] = $pieces[2];
         }
         $this->data['topicname'] = $pieces[3];
         $h1 = PonyDocsTopic::FindH1ForTitle($wgTitle->__toString());
         if ($h1 !== FALSE) {
             $this->data['titletext'] = $h1;
         }
     }
     /**
      * Get current topic, passing it our global Article object.
      * From this, generate our TOC based on the current topic selected.
      * This generates our left sidebar TOC plus our prev/next/start navigation links.
      * This should ONLY be done if we actually are WITHIN a manual, so special pages like TOC, etc. should not do this!
      */
     if ($pManual) {
         $p = PonyDocsProduct::GetProductByShortName($this->data['selectedProduct']);
         $v = PonyDocsProductVersion::GetVersionByName($this->data['selectedProduct'], $this->data['selectedVersion']);
         $toc = new PonyDocsTOC($pManual, $v, $p);
         list($this->data['manualtoc'], $this->data['tocprev'], $this->data['tocnext'], $this->data['tocstart']) = $toc->loadContent();
         $this->data['toctitle'] = $toc->getTOCPageTitle();
     }
     /**
      * Create a PonyDocsTopic from our article. From this we populate:
      *
      * topicversions:  List of version names topic is tagged with.
      * inlinetoc:  Inline TOC shown above article body.
      * catcode:  Special category code.
      * cattext:  Category description.
      * basetopicname:  Base topic name (w/o :<version> at end).
      * basetopiclink:  Link to special TopicList page to view all same topics.
      */
     $context = $this->skin->getContext();
     $article = Article::newFromTitle($context->getTitle(), $context);
     $topic = new PonyDocsTopic($article);
     if (preg_match('/^' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':(.*):(.*):(.*):(.*)/', $wgTitle->__toString()) || preg_match('/^' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':.*:.*TOC.*/', $wgTitle->__toString())) {
         $this->data['topicversions'] = PonyDocsWiki::getVersionsForTopic($topic);
         $this->data['inlinetoc'] = $topic->getSubContents();
         $this->data['versionclasses'] = $topic->getVersionClasses();
         $this->data['versionGroupMessage'] = $this->data['pVersion']->getVersionGroupMessage();
         /**
          * Sort of a hack -- we only use this right now when loading a TOC page which is new/does not exist.
          * When this happens a hook (AlternateEdit) adds an inline script to define this JS function,
          * which populates the edit box with the proper Category tag based on the currently selected version.
          */
         $this->data['body_onload'] = 'ponyDocsOnLoad();';
         switch ($this->data['catcode']) {
             case 0:
                 $this->data['cattext'] = 'Applies to latest version which is currently unreleased.';
                 break;
             case 1:
                 $this->data['cattext'] = 'Applies to latest version.';
                 break;
             case 2:
                 $this->data['cattext'] = 'Applies to released version(s) but not the latest.';
                 break;
             case 3:
                 $this->data['cattext'] = 'Applies to latest preview version.';
                 break;
             case 4:
                 $this->data['cattext'] = 'Applies to one or more preview version(s) only.';
                 break;
             case 5:
                 $this->data['cattext'] = 'Applies to one or more unreleased version(s) only.';
                 break;
             case -2:
                 /** Means its not a a title name which should be checked. */
                 break;
             default:
                 $this->data['cattext'] = 'Does not apply to any version of PonyDocs.';
                 break;
         }
     }
     $this->data['basetopicname'] = $topic->getBaseTopicName();
     if (strlen($this->data['basetopicname'])) {
         $this->data['basetopiclink'] = '<a href="' . $wgScriptPath . '/index.php?title=Special:TopicList&topic=' . $this->data['basetopicname'] . '">View All</a>';
     }
     $temp = PonyDocsTopic::FindH1ForTitle(PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':' . $topic->getTitle()->getText());
     if ($temp !== false) {
         // We got an H1!
         $this->data['pagetitle'] = $temp;
     }
 }
Exemple #10
0
 /**
  * Called when an unknown action occurs on url.  We are only interested in pdfbook action.
  */
 function onUnknownAction($action, $article)
 {
     global $wgOut, $wgUser, $wgTitle, $wgParser, $wgRequest;
     global $wgServer, $wgArticlePath, $wgScriptPath, $wgUploadPath, $wgUploadDirectory, $wgScript, $wgStylePath;
     // We don't do any processing unless it's pdfbook
     if ($action != 'pdfbook') {
         return true;
     }
     // Get the title and make sure we're in Documentation namespace
     $title = $article->getTitle();
     if ($title->getNamespace() != NS_PONYDOCS) {
         return true;
     }
     // Grab parser options for the logged in user.
     $opt = ParserOptions::newFromUser($wgUser);
     // Log the export
     $msg = $wgUser->getUserPage()->getPrefixedText() . ' exported as a PonyDocs PDF Book';
     $log = new LogPage('ponydocspdfbook', false);
     $log->addEntry('book', $wgTitle, $msg);
     // Initialise PDF variables
     $layout = '--firstpage p1';
     $x_margin = '1.25in';
     $y_margin = '1in';
     $font = 'Arial';
     $size = '12';
     $linkcol = '4d9bb3';
     $levels = '2';
     $exclude = array();
     $width = '1024';
     $width = "--browserwidth 1024";
     // Determine articles to gather
     $articles = array();
     $pieces = explode(":", $wgTitle->__toString());
     // Try and get rid of the TOC portion of the title
     if (strpos($pieces[2], "TOC") && count($pieces) == 3) {
         $pieces[2] = substr($pieces[2], 0, strpos($pieces[2], "TOC"));
     } else {
         if (count($pieces) != 5) {
             // something is wrong, let's get out of here
             $defaultRedirect = PonyDocsExtension::getDefaultUrl();
             if (PONYDOCS_DEBUG) {
                 error_log("DEBUG [" . __METHOD__ . ":" . __LINE__ . "] redirecting to {$defaultRedirect}");
             }
             header("Location: " . $defaultRedirect);
             exit;
         }
     }
     $productName = $pieces[1];
     $ponydocs = PonyDocsWiki::getInstance($productName);
     $pProduct = PonyDocsProduct::GetProductByShortName($productName);
     if ($pProduct === NULL) {
         // product wasn't valid
         wfProfileOut(__METHOD__);
         $wgOut->setStatusCode(404);
         return FALSE;
     }
     $productLongName = $pProduct->getLongName();
     if (PonyDocsProductManual::isManual($productName, $pieces[2])) {
         $pManual = PonyDocsProductManual::GetManualByShortName($productName, $pieces[2]);
     }
     $versionText = PonyDocsProductVersion::GetSelectedVersion($productName);
     if (!empty($pManual)) {
         // We should always have a pManual, if we're printing from a TOC
         $v = PonyDocsProductVersion::GetVersionByName($productName, $versionText);
         // We have our version and our manual Check to see if a file already exists for this combination
         $pdfFileName = "{$wgUploadDirectory}/ponydocspdf-" . $productName . "-" . $versionText . "-" . $pManual->getShortName() . "-book.pdf";
         // Check first to see if this PDF has already been created and is up to date.  If so, serve it to the user and stop
         // execution.
         if (file_exists($pdfFileName)) {
             error_log("INFO [PonyDocsPdfBook::onUnknownAction] " . php_uname('n') . ": cache serve username=\"" . $wgUser->getName() . "\" product=\"" . $productName . "\" version=\"" . $versionText . "\" " . " manual=\"" . $pManual->getShortName() . "\"");
             PonyDocsPdfBook::servePdf($pdfFileName, $productName, $versionText, $pManual->getShortName());
             // No more processing
             return false;
         }
     } else {
         error_log("ERROR [PonyDocsPdfBook::onUnknownAction] " . php_uname('n') . ": User attempted to print a pdfbook from a non TOC page with path:" . $wgTitle->__toString());
     }
     $html = self::getManualHTML($pProduct, $pManual, $v);
     // HTMLDOC does not care for utf8.
     $html = utf8_decode("{$html}\n");
     // Write the HTML to a tmp file
     $file = "{$wgUploadDirectory}/" . uniqid('ponydocs-pdf-book');
     $fh = fopen($file, 'w+');
     fwrite($fh, $html);
     fclose($fh);
     // Okay, create the title page
     $titlepagefile = "{$wgUploadDirectory}/" . uniqid('ponydocs-pdf-book-title');
     $fh = fopen($titlepagefile, 'w+');
     $coverPageHTML = self::getCoverPageHTML($pProduct, $pManual, $v);
     fwrite($fh, $coverPageHTML);
     fclose($fh);
     $format = 'manual';
     /* @todo Modify so single topics can be printed in pdf */
     $footer = $format == 'single' ? '...' : '.1.';
     $toc = $format == 'single' ? '' : " --toclevels {$levels}";
     // Send the file to the client via htmldoc converter
     $wgOut->disable();
     $cmd = " --left {$x_margin} --right {$x_margin} --top {$y_margin} --bottom {$y_margin}";
     $cmd .= " --header ... --footer {$footer} --tocfooter .i. --quiet --jpeg --color";
     $cmd .= " --bodyfont {$font} --fontsize {$size} --linkstyle plain --linkcolor {$linkcol}";
     $cmd .= "{$toc} --format pdf14 {$layout} {$width} --titlefile {$titlepagefile} --size letter";
     $cmd = "htmldoc -t pdf --book --charset iso-8859-1 --no-numbered {$cmd} {$file} > {$pdfFileName}";
     putenv("HTMLDOC_NOCGI=1");
     $output = array();
     $returnVar = 1;
     exec($cmd, $output, $returnVar);
     if ($returnVar != 0) {
         // 0 is success
         error_log("INFO [PonyDocsPdfBook::onUnknownAction] " . php_uname('n') . ": Failed to run htmldoc (" . $returnVar . ") Output is as follows: " . implode("-", $output));
         print "Failed to create PDF.  Our team is looking into it.";
     }
     // Delete the htmlfile and title file from the filesystem.
     @unlink($file);
     if (file_exists($file)) {
         error_log("ERROR [PonyDocsPdfBook::onUnknownAction] " . php_uname('n') . ": Failed to delete temp file {$file}");
     }
     @unlink($titlepagefile);
     if (file_exists($titlepagefile)) {
         error_log("ERROR [PonyDocsPdfBook::onUnknownAction] " . php_uname('n') . ": Failed to delete temp file {$titlepagefile}");
     }
     // Okay, let's add an entry to the error log to dictate someone requested a pdf
     error_log("INFO [PonyDocsPdfBook::onUnknownAction] " . php_uname('n') . ": fresh serve username=\"" . $wgUser->getName() . "\" version=\"{$versionText}\" " . " manual=\"" . $pManual->getLongName() . "\"");
     PonyDocsPdfBook::servePdf($pdfFileName, $productName, $versionText, $pManual->getLongName());
     // No more processing
     return false;
 }
 /**
  * Processes a branch/inherit job request.
  *
  * @param $jobID The unique id for this job (see ajaxFetchJobID)
  * @param $productName string product short name
  * @param $sourceVersion string String representation of the source version
  * @param $targetVersion string String representaiton of the target version
  * @param $topicActions string JSON array representation of all topics and 
  * 								their requested actions.
  * @return string Full job log of the process by printing to stdout.
  */
 public static function ajaxProcessRequest($jobID, $productName, $sourceVersion, $targetVersion, $topicActions)
 {
     global $wgScriptPath;
     ob_start();
     $targetVersionName = $targetVersion;
     $sourceVersionName = $sourceVersion;
     $topicActions = json_decode($topicActions, true);
     list($msec, $sec) = explode(' ', microtime());
     $startTime = (double) $msec + (double) $sec;
     $logFields = "action=\"start\" status=\"success\" product=\"" . addslashes($productName) . "\" sourceVersion=\"" . htmlentities($sourceVersionName) . "\" " . "targetVersion=\"" . addslashes($targetVersionName) . "\"";
     error_log('INFO [' . __METHOD__ . "] [BranchInherit] {$logFields}");
     if ($topicActions == false) {
         print "Failed to read request.";
         return true;
     }
     print "Beginning process job for source version: " . $productName . ':' . $sourceVersion . "<br />";
     print "Target version is: " . $targetVersion . "<br />";
     // Enable speed processing to avoid any unnecessary processing on
     // new topics created by this tool.
     PonyDocsExtension::setSpeedProcessing(true);
     $product = PonyDocsProduct::GetProductByShortName($productName);
     $sourceVersion = PonyDocsProductVersion::GetVersionByName($productName, $sourceVersion);
     $targetVersion = PonyDocsProductVersion::GetVersionByName($productName, $targetVersion);
     // Determine how many topics there are to process.
     $numOfTopics = 0;
     $numOfTopicsCompleted = 0;
     foreach ($topicActions as $manualIndex => $manualData) {
         foreach ($manualData['sections'] as $sectionName => $topics) {
             // The following is a goofy fix for some browsers.  Sometimes
             // the JSON comes along with null values for the first element.
             // IT's just an additional element, so we can drop it.
             if (empty($topics[0]['text'])) {
                 array_shift($topicActions[$manualIndex]['sections'][$sectionName]);
             }
             $numOfTopics += count($topicActions[$manualIndex]['sections'][$sectionName]);
         }
     }
     $logFields = "action=\"topic\" status=\"success\" product=\"" . addslashes($productName) . "\" numOfTopics={$numOfTopics} " . "sourceVersion=\"" . addslashes($sourceVersionName) . "\" targetVersion=\"" . htmlentities($targetVersionName) . "\"";
     error_log('INFO [' . __METHOD__ . "] [BranchInherit] {$logFields}");
     $lastTopicTarget = null;
     foreach ($topicActions as $manualName => $manualData) {
         $manual = PonyDocsProductManual::GetManualByShortName($productName, $manualName);
         // Determine if TOC already exists for target version.
         if (!PonyDocsBranchInheritEngine::TOCExists($product, $manual, $targetVersion)) {
             print "<div class=\"normal\">TOC Does not exist for Manual " . $manual->getShortName() . " for version " . $targetVersion->getVersionName() . "</div>";
             // Crl eate the toc or inherit.
             if ($manualData['tocAction'] != 'default') {
                 // Then they want to force.
                 if ($manualData['tocAction'] == 'forceinherit') {
                     print "<div class=\"normal\">Forcing inheritance of source TOC.</div>";
                     PonyDocsBranchInheritEngine::addVersionToTOC($product, $manual, $sourceVersion, $targetVersion);
                     print "<div class=\"normal\">Complete</div>";
                 } else {
                     if ($manualData['tocAction'] == 'forcebranch') {
                         print "<div class=\"normal\">Forcing branch of source TOC.</div>";
                         PonyDocsBranchInheritEngine::branchTOC($product, $manual, $sourceVersion, $targetVersion);
                         print "<div class=\"normal\">Complete</div>";
                     }
                 }
             } else {
                 if ($manualData['tocInherit']) {
                     // We need to get the TOC for source version/manual and add
                     // target version to the category tags.
                     try {
                         print "<div class=\"normal\">Attempting to add target version to existing source version TOC.</div>";
                         PonyDocsBranchInheritEngine::addVersionToTOC($product, $manual, $sourceVersion, $targetVersion);
                         $logFields = "action=\"TOC\" status=\"success\" product=\"" . addslashes($productName) . "\" manual=\"" . htmlentities($manualName) . "\" " . "sourceVersion=\"" . addslashes($sourceVersionName) . "\" targetVersion=\"" . htmlentities($targetVersionName) . "\"";
                         error_log('INFO [' . __METHOD__ . "] [BranchInherit] {$logFields}");
                         print "<div class=\"normal\">Complete</div>";
                     } catch (Exception $e) {
                         $logFields = "action=\"TOC\" status=\"failure\" product=\"" . addslashes($productName) . "\" manual=\"" . htmlentities($manualName) . "\" " . "error=\"" . addslashes($e->getMessage()) . "\" sourceVersion=\"" . htmlentities($sourceVersionName) . "\" " . "targetVersion=\"" . addslashes($targetVersionName) . "\"";
                         error_log('WARNING [' . __METHOD__ . "] [BranchInherit] {$logFields}");
                         print "<div class=\"error\">Exception: " . $e->getMessage() . "</div>";
                     }
                 } else {
                     try {
                         print "<div class=\"normal\">Attempting to create TOC for target version.</div>";
                         $addData = array();
                         foreach ($manualData['sections'] as $sectionName => $topics) {
                             $addData[$sectionName] = array();
                             foreach ($topics as $topic) {
                                 $addData[$sectionName][] = $topic['toctitle'];
                             }
                         }
                         PonyDocsBranchInheritEngine::createTOC($product, $manual, $targetVersion, $addData);
                         $logFields = "action=\"TOC\" status=\"success\" product=\"" . addslashes($productName) . "\" manual=\"" . htmlentities($manualName) . "\" " . "sourceVersion=\"" . addslashes($sourceVersionName) . "\" targetVersion=\"" . htmlentities($targetVersionName) . "\"";
                         error_log('INFO [' . __METHOD__ . "] [BranchInherit] {$logFields}");
                         print "<div class=\"normal\">Complete</div>";
                     } catch (Exception $e) {
                         $logFields = "action=\"TOC\" status=\"failure\" product=\"" . addslashes($productName) . "\" manual=\"" . htmlentities($manualName) . "\" " . "sourceVersion=\"" . addslashes($sourceVersionName) . "\" error=\"" . htmlentities($e->getMessage()) . "\" " . "targetVersion=\"" . addslashes($targetVersionName) . "\"";
                         error_log('WARNING [' . __METHOD__ . "] [BranchInherit] {$logFields}");
                         print "<div class=\"error\">Exception: " . $e->getMessage() . "</div>";
                     }
                 }
             }
         } else {
             try {
                 print "<div class=\"normal\">Attempting to update TOC for target version.</div>";
                 $addData = array();
                 foreach ($manualData['sections'] as $sectionName => $topics) {
                     $addData[$sectionName] = array();
                     foreach ($topics as $topic) {
                         if (!isset($topic['action']) || isset($topic['action']) && $topic['action'] != 'ignore') {
                             $addData[$sectionName][] = $topic['toctitle'];
                         }
                     }
                 }
                 PonyDocsBranchInheritEngine::addCollectionToTOC($product, $manual, $targetVersion, $addData);
                 $logFields = "action=\"TOC\" status=\"success\" product=\"" . addslashes($productName) . "\" manual=\"" . htmlentities($manualName) . "\" " . "sourceVersion=\"" . addslashes($sourceVersionName) . "\" targetVersion=\"" . htmlentities($targetVersionName) . "\"";
                 error_log('INFO [' . __METHOD__ . "] [BranchInherit] {$logFields}");
                 print "<div class=\"normal\">Complete</div>";
             } catch (Exception $e) {
                 $logFields = "action=\"TOC\" status=\"failure\" product=\"" . addslashes($productName) . "\" manual=\"" . htmlentities($manualName) . "\" " . "sourceVersion=\"" . addslashes($sourceVersionName) . "\" error=\"" . htmlentities($e->getMessage()) . "\" " . "targetVersion=\"" . addslashes($targetVersionName) . "\"";
                 error_log('WARNING [' . __METHOD__ . "] [BranchInherit] {$logFields}");
                 print "<div class=\"error\">Exception: " . $e->getMessage() . "</div>";
             }
         }
         // Okay, now let's go through each of the topics and
         // branch/inherit.
         print "Processing topics.\n";
         $path = PonyDocsExtension::getTempDir() . $jobID;
         foreach ($manualData['sections'] as $sectionName => $topics) {
             print "<div class=\"normal\">Processing section {$sectionName}</div>";
             foreach ($topics as $topic) {
                 // Update log file
                 $fp = fopen($path, "w+");
                 fputs($fp, "Completed " . $numOfTopicsCompleted . " of " . $numOfTopics . " Total: " . (int) ($numOfTopicsCompleted / $numOfTopics * 100) . "%");
                 fclose($fp);
                 if (isset($topic['action']) && $topic['action'] == "ignore") {
                     print "<div class=\"normal\">Ignoring topic: " . $topic['title'] . "</div>";
                     $numOfTopicsCompleted++;
                     continue;
                 } else {
                     if (isset($topic['action']) && $topic['action'] == "branchpurge") {
                         try {
                             print "<div class=\"normal\">Attempting to branch topic " . $topic['title'] . " and remove existing topic.</div>";
                             $lastTopicTarget = PonyDocsBranchInheritEngine::branchTopic($topic['title'], $targetVersion, $sectionName, $topic['text'], TRUE, FALSE);
                             $logFields = "action=\"topic\" status=\"success\" product=\"" . addslashes($productName) . "\" manual=\"" . htmlentities($manualName) . "\" " . "topic=\"" . addslashes($topic['title']) . "\" sourceVersion=\"" . htmlentities($sourceVersionName) . "\" targetVersion=\"" . htmlentities($targetVersionName) . "\"";
                             error_log('INFO [' . __METHOD__ . "] [BranchInherit] {$logFields}");
                             print "<div class=\"normal\">Complete</div>";
                         } catch (Exception $e) {
                             $logFields = "action=\"topic\" status=\"failure\" product=\"" . addslashes($productName) . "\" manual=\"" . htmlentities($manualName) . "\" " . "topic=\"" . addslashes($topic['title']) . "\" sourceVersion=\"" . htmlentities($sourceVersionName) . "\" error=\"" . htmlentities($e->getMessage()) . "\" " . "targetVersion=\"" . addslashes($targetVersionName) . "\"";
                             error_log('WARNING [' . __METHOD__ . "] [BranchInherit] {$logFields}");
                             print "<div class=\"error\">Exception: " . $e->getMessage() . "</div>";
                         }
                     } else {
                         if (isset($topic['action']) && $topic['action'] == "branch") {
                             try {
                                 print "<div class=\"normal\">Attempting to branch topic " . $topic['title'] . "</div>";
                                 $lastTopicTarget = PonyDocsBranchInheritEngine::branchTopic($topic['title'], $targetVersion, $sectionName, $topic['text'], FALSE, TRUE);
                                 $logFields = "action=\"topic\" status=\"success\" product=\"" . addslashes($productName) . "\" manual=\"" . htmlentities($manualName) . "\" " . "topic=\"" . addslashes($topic['title']) . "\" sourceVersion=\"" . htmlentities($sourceVersionName) . "\" targetVersion=\"" . htmlentities($targetVersionName) . "\"";
                                 error_log('INFO [' . __METHOD__ . "] [BranchInherit] {$logFields}");
                                 print "<div class=\"normal\">Complete</div>";
                             } catch (Exception $e) {
                                 $logFields = "action=\"topic\" status=\"failure\" product=\"" . addslashes($productName) . "\" manual=\"" . htmlentities($manualName) . "\" " . "topic=\"" . addslashes($topic['title']) . "\" sourceVersion=\"" . htmlentities($sourceVersionName) . "\" error=\"" . htmlentities($e->getMessage()) . "\" " . "targetVersion=\"" . addslashes($targetVersionName) . "\"";
                                 error_log('WARNING [' . __METHOD__ . "] [BranchInherit] {$logFields}");
                                 print "<div class=\"error\">Exception: " . $e->getMessage() . "</div>";
                             }
                         } else {
                             if (isset($topic['action']) && $topic['action'] == "branchsplit") {
                                 try {
                                     print "<div class=\"normal\">Attempting to branch topic " . $topic['title'] . " and split from existing topic.</div>";
                                     $lastTopicTarget = PonyDocsBranchInheritEngine::branchTopic($topic['title'], $targetVersion, $sectionName, $topic['text'], FALSE, TRUE);
                                     $logFields = "action=\"topic\" status=\"success\" product=\"" . addslashes($productName) . "\" manual=\"" . htmlentities($manualName) . "\"e " . "topic=\"" . addslashes($topic['title']) . "\" sourceVersion=\"" . htmlentities($sourceVersionName) . "\" " . "targetVersion=\"" . addslashes($targetVersionName) . "\"";
                                     error_log('INFO [' . __METHOD__ . "] [BranchInherit] {$logFields}");
                                     print "<div class=\"normal\">Complete</div>";
                                 } catch (Exception $e) {
                                     $logFields = "action=\"topic\" status=\"failure\" product=\"" . addslashes($productName) . "\" manual=\"" . htmlentities($manualName) . "\" " . "topic=\"" . addslashes($topic['title']) . "\" sourceVersion=\"" . htmlentities($sourceVersionName) . "\" error=\"" . htmlentities($e->getMessage()) . "\" " . "targetVersion=\"" . addslashes($targetVersionName) . "\"";
                                     error_log('WARNING [' . __METHOD__ . "] [BranchInherit] {$logFields}");
                                     print "<div class=\"error\">Exception: " . $e->getMessage() . "</div>";
                                 }
                             } else {
                                 if (isset($topic['action']) && $topic['action'] == "inherit") {
                                     try {
                                         print "<div class=\"normal\">Attempting to inherit topic " . $topic['title'] . "</div>";
                                         $lastTopicTarget = PonyDocsBranchInheritEngine::inheritTopic($topic['title'], $targetVersion, $sectionName, $topic['text'], FALSE);
                                         $logFields = "action=\"topic\" status=\"success\" product=\"" . addslashes($productName) . "\" manual=\"" . htmlentities($manualName) . "\" " . "topic=\"" . addslashes($topic['title']) . "\" sourceVersion=\"" . htmlentities($sourceVersionName) . "\" targetVersion=\"" . htmlentities($targetVersionName) . "\"";
                                         error_log('INFO [' . __METHOD__ . "] [BranchInherit] {$logFields}");
                                         print "<div class=\"normal\">Complete</div>";
                                     } catch (Exception $e) {
                                         $logFields = "action=\"topic\" status=\"failure\" product=\"" . addslashes($productName) . "\" manual=\"" . htmlentities($manualName) . "\" " . "topic=\"" . addslashes($topic['title']) . "\" sourceVersion=\"" . htmlentities($sourceVersionName) . "\" error=\"" . htmlentities($e->getMessage()) . "\" " . "targetVersion=\"" . addslashes($targetVersionName) . "\"";
                                         error_log('WARNING [' . __METHOD__ . "] [BranchInherit] {$logFields}");
                                         print "<div class=\"error\">Exception: " . $e->getMessage() . "</div>";
                                     }
                                 } else {
                                     if (isset($topic['action']) && $topic['action'] == "inheritpurge") {
                                         try {
                                             print "<div class=\"normal\">Attempting to inherit topic " . $topic['title'] . " and remove existing topic.</div>";
                                             $lastTopicTarget = PonyDocsBranchInheritEngine::inheritTopic($topic['title'], $targetVersion, $sectionName, $topic['text'], TRUE);
                                             $logFields = "action=\"topic\" status=\"success\" product=\"" . addslashes($productName) . "\" manual=\"" . htmlentities($manualName) . "\" " . "topic=\"" . addslashes($topic['title']) . "\" sourceVersion=\"" . htmlentities($sourceVersionName) . "\" targetVersion=\"" . htmlentities($targetVersionName) . "\"";
                                             error_log('INFO [' . __METHOD__ . "] [BranchInherit] {$logFields}");
                                             print "<div class=\"normal\">Complete</div>";
                                         } catch (Exception $e) {
                                             $logFields = "action=\"topic\" status=\"failure\" product=\"" . addslashes($productName) . "\" manual=\"" . htmlentities($manualName) . "\" " . "topic=\"" . addslashes($topic['title']) . "\" sourceVersion=\"" . htmlentities($sourceVersionName) . "\" error=\"" . htmlentities($e->getMessage()) . "\" " . "targetVersion=\"" . addslashes($targetVersionName) . "\"";
                                             error_log('WARNING [' . __METHOD__ . "] [BranchInherit] {$logFields}");
                                             print "<div class=\"error\">Exception: " . $e->getMessage() . "</div>";
                                         }
                                     }
                                 }
                             }
                         }
                     }
                 }
                 $numOfTopicsCompleted++;
             }
         }
         //WEB-10792, Clear TOCCACHE for the target version only, each Manual at a time
         PonyDocsTOC::clearTOCCache($manual, $targetVersion, $product);
         //Also clear the NAVCache for the target version
         PonyDocsProductVersion::clearNAVCache($targetVersion);
     }
     list($msec, $sec) = explode(' ', microtime());
     $endTime = (double) $msec + (double) $sec;
     print "All done!\n";
     print 'Execution Time: ' . round($endTime - $startTime, 3) . ' seconds';
     if ($numOfTopics == 1 && $lastTopicTarget != null) {
         // We can safely show a link to the topic.
         print "<br />";
         print "Link to new topic: <a href=\"" . $wgScriptPath . "/" . $lastTopicTarget . "\">" . $lastTopicTarget . "</a>";
         print "<br />";
     }
     // Okay, let's start the process!
     unlink($path);
     $buffer = ob_get_clean();
     return $buffer;
 }
 /**
  * This loads the list of manuals BASED ON whether each manual defined has a TOC defined for the
  * currently selected version or not.
  *
  * @param boolean $reload
  * @return array
  */
 public static function LoadManualsForProduct($productName, $reload = false)
 {
     $dbr = wfGetDB(DB_SLAVE);
     /**
      * If we have content in our list, just return that unless $reload is true.
      */
     if (isset(self::$sManualList[$productName]) && sizeof(self::$sManualList[$productName]) && !$reload) {
         return self::$sManualList[$productName];
     }
     self::$sManualList[$productName] = array();
     self::$sCategoryMap[$productName] = array();
     // Use 0 as the last parameter to enforce getting latest revision of this article.
     $article = new Article(Title::newFromText(PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':' . $productName . PONYDOCS_PRODUCTMANUAL_SUFFIX), 0);
     $content = $article->getContent();
     if (!$article->exists()) {
         /**
          * There is no manuals file found -- just return.
          */
         return array();
     }
     /**
      * The content of this topic should be of this form:
      * {{#manual:shortName|longName}}
      * ...
      * 
      * There is a user defined parser hook which converts this into useful output when viewing as well.
      * 
      * Then query categorylinks to only add the manual if it is for the selected product and has a tagged TOC file with the selected version.
      * Otherwise, skip it!
      */
     // Explode on the closing tag to get an array of manual tags
     $tags = explode('}}', $content);
     foreach ($tags as $tag) {
         $tag = trim($tag);
         if (strpos($tag, '{{#manual:') === 0) {
             // Remove the opening tag and prefix
             $manual = str_replace('{{#manual:', '', $tag);
             $parameters = explode('|', $manual);
             $parameters = array_map('trim', $parameters);
             // Pad out array to avoid notices
             $parameters = array_pad($parameters, 3, '');
             // Set static flag if defined as static
             $static = FALSE;
             if (strpos($parameters[0], PONYDOCS_PRODUCT_STATIC_PREFIX) === 0) {
                 $parameters[0] = substr($parameters[0], strlen(PONYDOCS_PRODUCT_STATIC_PREFIX));
                 $static = TRUE;
             }
             $pManual = new PonyDocsProductManual($productName, $parameters[0], $parameters[1], $parameters[2], $static);
             self::$sDefinedManualList[$productName][strtolower($pManual->getShortName())] = $pManual;
             // If the Manual is static or there is a TOC for this Product/Manual/Version, add to sManualList
             if (!$static) {
                 $res = PonyDocsCategoryLinks::getTOCByProductManualVersion($productName, $pManual->getShortName(), PonyDocsProductVersion::GetSelectedVersion($productName));
                 if (!$res->numRows()) {
                     continue;
                 }
             }
             // Handle Manual Categories
             if (isset($parameters[2]) && $parameters[2] != '') {
                 $categories = explode(',', $parameters[2]);
                 foreach ($categories as $category) {
                     self::$sCategoryMap[$productName][$category][$pManual->getShortName()] = $pManual;
                 }
             } else {
                 self::$sCategoryMap[$productName][PONYDOCS_NO_CATEGORY][$pManual->getShortName()] = $pManual;
             }
             self::$sManualList[$productName][strtolower($pManual->getShortName())] = $pManual;
         }
     }
     return self::$sManualList[$productName];
 }