/** * 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; }
/** * Replace a version on an existing Topic * * @param $topicTitle string The internal mediawiki title of the article. * @param $sourceVersion PonyDocsVersion The source Version * @param $targetVersion PonyDocsVersion The target Version * @returns boolean */ public static function changeVersionOnTopic($topicTitle, $sourceVersion, $targetVersion) { global $wgTitle; // Clear any hooks so no weirdness gets called after we save the change $wgHooks['ArticleSave'] = array(); if (!preg_match('/^' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':([^:]*):([^:]*):(.*):([^:]*)$/', $topicTitle, $match)) { throw new Exception("Invalid Title to Rename Version: {$topicTitle}"); } $productName = $match[1]; $title = $match[3]; // Get PonyDocsProduct // TODO: Why don't we just pass the product in instead of re-deriving it from something else and then loading it? $product = PonyDocsProduct::GetProductByShortName($productName); $title = Title::newFromText($topicTitle); $wgTitle = $title; $article = new Article($title); if (!$article->exists()) { // No such title exists in the system throw new Exception("Invalid Title to Rename Version: {$topicTitle}"); } $content = $article->getContent(); $oldCategory = '[[Category:V:' . $product->getShortName() . ':' . $sourceVersion->getVersionName() . ']]'; $newCategory = '[[Category:V:' . $product->getShortName() . ':' . $targetVersion->getVersionName() . ']]'; $message = ''; $editTopic = TRUE; // Get conflicts. $conflicts = PonyDocsBranchInheritEngine::getConflicts($product, $topicTitle, $targetVersion); if (!empty($conflicts)) { // If there's already a topic with the target version, // then we just want to remove the source version from the source topic foreach ($conflicts as $conflict) { $conflictingArticle = new Article(Title::newFromText($conflict)); // No big deal. A conflict no longer exists? Continue. if (!$conflictingArticle->exists()) { continue; } // Conflict was same as source material. Do nothing with it. if ($conflict == $topicTitle) { continue; // Remove source version from source article } else { $content = str_replace($oldCategory, '', $content); $message = "Removed version category {$oldCategory} via RenameVersion"; } } } if (empty($message)) { // If the Topic doesn't contain the source version, it may have been branched, or already processed if (strpos($content, $oldCategory) === FALSE) { $lastColon = strrpos($topicTitle, ':'); $baseTopic = substr_replace($topicTitle, '', $lastColon); $topicTitle = PonyDocsTopic::GetTopicNameFromBaseAndVersion($baseTopic, $productName); // We found an instance of this title with the source version! Let's recurse just this once to handle it. if ($topicTitle) { $editTopic = FALSE; $title = self::changeVersionOnTopic($topicTitle, $sourceVersion, $targetVersion); // We can't find a topic with the source version, so something is odd. Let's complain } else { throw new Exception("Topic {$topicTitle} does not contain source version " . $sourceVersion->getVersionName()); } // If the Topic already has the new version, just remove the old version } elseif (strpos($content, $newCategory) !== FALSE) { $content = str_replace($oldCategory, '', $content); $message = "Removed version category {$oldCategory} via RenameVersion"; // Otherwise replace old with new } else { $content = str_replace($oldCategory, $newCategory, $content, $count); $message = "Renamed version category {$oldCategory} to {$newCategory} in {$count} locations via RenameVersion"; } } // Finally we can edit the topic if ($editTopic) { // TODO: doEdit returns a status that we should check $article->doEdit($content, $message, EDIT_UPDATE); } return $title; }
/** * AJAX method to fetch topics for a specified version and manuals * * @param $productName string product short name * @param $sourceVersion string String representation of the source version * @param $targetVersion string String representation of the target version * @param $manuals string Comma seperated list of manuals to retrieve from * @param $forcedTitle string A specific title to pull from and nothing else * (for individual branch/inherit) * @returns string JSON representation of all titles requested */ public static function ajaxFetchTopics($productName, $sourceVersion, $targetVersion, $manuals, $forcedTitle = null) { PonyDocsProduct::LoadProducts(true); $product = PonyDocsProduct::GetProductByShortName($productName); PonyDocsProductVersion::LoadVersionsForProduct(true, true); $sourceVersion = PonyDocsProductVersion::GetVersionByName($productName, $sourceVersion); $targetVersion = PonyDocsProductVersion::GetVersionByName($productName, $targetVersion); if (!$sourceVersion || !$targetVersion) { $result = array("success", false); $result = json_encode($result); return $result; } $result = array(); // Okay, get manual by name. $manuals = explode(",", $manuals); foreach ($manuals as $manualName) { $manual = PonyDocsProductManual::GetManualByShortName($productName, $manualName); $result[$manualName] = array(); $result[$manualName]['meta'] = array(); // Load up meta. $result[$manualName]['meta']['text'] = $manual->getLongName(); // See if TOC exists for target version. $result[$manualName]['meta']['toc_exists'] = PonyDocsBranchInheritEngine::TOCExists($product, $manual, $targetVersion); $result[$manualName]['sections'] = array(); // Got the version and manual, load the TOC. $ponyTOC = new PonyDocsTOC($manual, $sourceVersion, $product); list($toc, $prev, $next, $start) = $ponyTOC->loadContent(); // Time to iterate through all the items. $section = ''; foreach ($toc as $tocItem) { if ($tocItem['level'] == 0) { $section = $tocItem['text']; $result[$manualName]['sections'][$section] = array(); $result[$manualName]['sections'][$section]['meta'] = array(); $result[$manualName]['sections'][$section]['topics'] = array(); } if ($tocItem['level'] == 1) { // actual topic if ($forcedTitle == null || $tocItem['title'] == $forcedTitle) { $tempEntry = array('title' => $tocItem['title'], 'text' => $tocItem['text'], 'toctitle' => $tocItem['toctitle'], 'conflicts' => PonyDocsBranchInheritEngine::getConflicts($product, $tocItem['title'], $targetVersion)); /** * We want to set to empty, so the UI javascript doesn't * bork out on this. */ if ($tempEntry['conflicts'] == false) { $tempEntry['conflicts'] = ''; } $result[$manualName]['sections'][$section]['topics'][] = $tempEntry; } } } foreach ($result as $manualName => $manual) { foreach ($manual['sections'] as $sectionIndex => $section) { if (count($section['topics']) == 0) { unset($result[$manualName]['sections'][$sectionIndex]); } } } } $result = json_encode($result); return $result; }