Exemplo n.º 1
0
 /**
  * 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;
 }
Exemplo n.º 2
0
 /**
  * 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;
 }
Exemplo n.º 3
0
 /**
  * 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;
 }
Exemplo n.º 4
0
 /**
  * This loads/parses the sub-contents for the in-page header TOC display.
  * This display shows the H2s and H3s for the current topic.
  *
  * @return array
  */
 public function getSubContents()
 {
     $sections = array();
     if (preg_match('/__NOTOC__/', $this->pArticle->getContent())) {
         return $sections;
     }
     $matches = $this->pArticle->getParserOutput()->getSections();
     $h2 = FALSE;
     $headReference = array();
     $headCount = 0;
     foreach ($matches as $match) {
         $level = $match['level'];
         if (!isset($headReference[$match['line']])) {
             $headReference[$match['line']] = 1;
         } else {
             $headReference[$match['line']]++;
         }
         // We don't want to include any H3s that don't have an H2 parent
         if ($level == 2 || $level == 3 && $h2) {
             if ($level == 2) {
                 $h2 = TRUE;
             }
             $headCount = $headReference[$match['line']];
             if ($headCount > 1) {
                 $link = '#' . Sanitizer::escapeId(PonyDocsTOC::normalizeSection($match['line']), 'noninitial') . '_' . $headCount;
             } else {
                 $link = '#' . Sanitizer::escapeId(PonyDocsTOC::normalizeSection($match['line']), 'noninitial');
             }
             $sections[] = array('level' => $level, 'link' => $link, 'text' => $match['line'], 'class' => 'toclevel-' . round($level - 1, 0));
         }
     }
     return $sections;
 }
Exemplo n.º 5
0
    /**
     * Generates an HTML string which represents the entire manual for a given product and version.
     *
     * @param $product PonyDocsProduct
     * @param $manual  PonyDocsProductManual
     * @param $version PonyDocsProductVersion
     *
     * @return string HTML String representation of manual contents
     */
    public function getManualHTML($product, $manual, $version)
    {
        global $wgOut, $wgUser, $wgTitle, $wgParser, $wgRequest;
        global $wgServer, $wgArticlePath, $wgScriptPath, $wgUploadPath, $wgUploadDirectory, $wgScript, $wgStylePath;
        // 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();
        $toc = new PonyDocsTOC($manual, $version, $product);
        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']);
            }
        }
        // Format the article(s) as a single HTML document with absolute URL's
        $html = <<<EOT
<!doctype html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:og="http://ogp.me/ns#" xmlns:fb="http://ogp.me/ns/fb#" charset="utf-8">
<head>
<meta charset="UTF-8">
<title></title>
<style>
html,body {
margin: 0px;
padding: 0px;
width: 210mm;
max-width: 210mm;
overflow-x: hidden;
}
pre {
\twidth: 100%;
\toverflow-x: hidden;
}
</style>
</head>

<body>
EOT;
        $wgArticlePath = $wgServer . $wgArticlePath;
        $wgScriptPath = $wgServer . $wgScriptPath;
        $wgUploadPath = $wgServer . $wgUploadPath;
        $wgScript = $wgServer . $wgScript;
        $currentSection = '';
        foreach ($articles as $section => $subarticles) {
            foreach ($subarticles as $article) {
                $title = $article['title'];
                $ttext = $title->getPrefixedText();
                if (!in_array($ttext, $exclude)) {
                    if ($currentSection != $section) {
                        $html .= '<h1>' . $section . '</h1>';
                        $currentSection = $section;
                    }
                    $article = new Article($title, 0);
                    $text = $article->fetchContent();
                    $text .= '__NOTOC__';
                    $opt->setEditSection(false);
                    // remove section-edit links
                    $wgOut->setHTMLTitle($ttext);
                    // use this so DISPLAYTITLE magic works
                    $out = $wgParser->parse($text, $title, $opt, true, true);
                    $ttext = $wgOut->getHTMLTitle();
                    $text = $out->getText();
                    // parse article title string and add topic name anchor tag for intramanual linking
                    $articleMeta = PonyDocsArticleFactory::getArticleMetadataFromTitle($title);
                    $text = '<a name="' . $articleMeta['topic'] . '"></a>' . $text;
                    // prepare for replacing pre tags with code tags WEB-5926 derived from
                    // http://stackoverflow.com/questions/1517102/replace-newlines-with-br-tags-but-only-inside-pre-tags
                    // only inside pre tag:
                    //   replace space with &nbsp; only when positive lookbehind is a whitespace character
                    //   replace \n -> <br/>
                    //   replace \t -> 8 * &nbsp;
                    /* split on <pre ... /pre>, basically.  probably good enough */
                    $str = " " . $text;
                    // guarantee split will be in even positions
                    $parts = preg_split("/(< \\s* pre .* \\/ \\s* pre \\s* >)/Umsxu", $str, -1, PREG_SPLIT_DELIM_CAPTURE);
                    foreach ($parts as $idx => $part) {
                        if ($idx % 2) {
                            $parts[$idx] = preg_replace(array("/(?<=\\s) /", "/\n/", "/\t/"), array("&nbsp;", "<br/>", "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"), $part);
                        }
                    }
                    $str = implode('', $parts);
                    /* chop off the first space, that we had added */
                    $text = substr($str, 1);
                    // String search and replace
                    $str_search = array('<h5>', '</h5>', '<h4>', '</h4>', '<h3>', '</h3>', '<h2>', '</h2>', '<h1>', '</h1>', '<code>', '</code>', '<pre>', '</pre>');
                    $str_replace = array('<h6>', '</h6>', '<h5>', '</h5>', '<h4><font size="3"><b><i>', '</i></b></font></h4>', '<h3>', '</h3>', '<h2>', '</h2>', '<code><font size="2">', '</font></code>', '<code><font size="2">', '</font></code>');
                    $text = str_replace($str_search, $str_replace, $text);
                    /*
                     * HTML regex tweaking prior to sending to PDF library
                     *
                     * 1 - replace intramanual links with just the anchor hash of topic name (e.g. href="#topicname")
                     * 2 - remove all non-intramanual links - strip anchor tags with href attribute whose href value doesn't start
                     *     with #
                     * 3 - wrap all span tags having id attribute with <a name="[topicname]_[span_id_attr_value]"> ... </a>
                     * 4 - all anchor links' href values that contain two # characters, replace the second with _
                     * 5 - make images have absolute URLs
                     * 6 - non-printable areas
                     * 7 - comment
                     * 8 - cell padding
                     * 9 - th bgcolor
                     * 10 - td valign, align and font size
                     * 
                     */
                    $regex_search = array('|<a([^\\>]+)href="(' . str_replace('/', '\\/', $wgServer) . ')+\\/' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . '\\/' . $product->getShortName() . '\\/' . $version->getVersionName() . '\\/' . $manual->getShortName() . '\\/([^"]*)"([^\\<]*)>|', '|<a[^\\>]+href="(?!#)[^"]*"[^>]*>(.*?)</a>|', '|<span[^\\>]+id="([^"]*)"[^>]*>(.*?)</span>|', '|<a([^\\>]+)href="#([^"]*)#([^"]*)"([^>]*)>(.*?)</a>|', '|(<img[^>]+?src=")(/.*>)|', '|<div\\s*class=[\'"]?noprint["\']?>.+?</div>|s', '|@{4}([^@]+?)@{4}|s', '/(<table[^>]*)/', '/(<th[^>]*)/', '/(<td[^>]*)>([^<]*)/');
                    // Table vars
                    $table_extra = ' cellpadding="6"';
                    $th_extra = ' bgcolor="#C0C0C0"';
                    $td_extra = ' valign="center" align="left"';
                    $regex_replace = array('<a${1}href="#${3}"${4}>', '${1}', '<a name="' . $articleMeta['topic'] . '_${1}">${0}</a>', '<a${1}href="#${2}_${3}"${4}>${5}</a>', "\$1{$wgServer}\$2", '', '<!--$1-->', "\$1{$table_extra}", "\$1{$th_extra}", "\$1{$td_extra}>\$2");
                    $text = preg_replace($regex_search, $regex_replace, $text);
                    // Make all anchor tags uniformly lower case (wkhtmltopdf is case sensitive for internal links)
                    $text = preg_replace_callback('|<a([^\\>])+href="([^"]*)"([^\\<]*)>|', function ($matches) {
                        return '<a' . $matches[1] . 'href="' . strtolower($matches[2]) . '"' . $matches[3] . '>';
                    }, $text);
                    $text = preg_replace_callback('|<a([^\\>])+name="([^"]*)"([^>]*)>|', function ($matches) {
                        return '<a' . $matches[1] . 'name="' . strtolower($matches[2]) . '"' . $matches[3] . '>';
                    }, $text);
                    $ttext = basename($ttext);
                    $html .= $text . "\n";
                }
            }
        }
        $html .= "</body></html>";
        return $html;
    }
Exemplo n.º 6
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;
     }
 }
Exemplo n.º 7
0
 /**
  * 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;
 }