/** * AJAX method to fetch manuals for a specified product and version * * @param $ver string The string representation of the version to retrieve * manuals for. * @returns string JSON representation of the manuals */ public static function ajaxFetchManuals($product, $ver) { PonyDocsProductVersion::LoadVersionsForProduct($product); PonyDocsProductVersion::SetSelectedVersion($product, $ver); $manuals = PonyDocsProductManual::LoadManualsForProduct($product, TRUE); $result = array(); foreach ($manuals as $manual) { if (!$manual->isStatic()) { $result[] = array("shortname" => $manual->getShortName(), "longname" => $manual->getLongName()); } } $result = json_encode($result); return $result; }
/** * Hook function which * - Sets the version correctly when editing a topic * - Redirects to the first topic in a manual if the user requested a bare manual URL * - Redirect to the landing page when there are no available versions */ public function onArticleFromTitleQuickLookup(&$title, &$article) { global $wgScriptPath; if (preg_match('/&action=edit/', $_SERVER['PATH_INFO'])) { // Check referrer and see if we're coming from a doc page. // If so, we're editing it, so we should force the version // to be from the referrer. if (preg_match('/^' . str_replace("/", "\\/", $wgScriptPath) . '\\/' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . '\\/(\\w+)\\/((latest|[\\w\\.]*)\\/)?(\\w+)\\/?/i', $_SERVER['HTTP_REFERER'], $match)) { $targetProduct = $match[1]; $targetVersion = $match[3]; if ($targetVersion == "latest") { PonyDocsProductVersion::SetSelectedVersion($targetProduct, PonyDocsProductVersion::GetLatestReleasedVersion($targetProduct)->getVersionName()); } else { PonyDocsProductVersion::SetSelectedVersion($targetProduct, $targetVersion); } } } // Match a URL like /Documentation/PRODUCT if (preg_match('/^' . str_replace("/", "\\/", $wgScriptPath) . '\\/' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . '\\/([' . PONYDOCS_PRODUCT_LEGALCHARS . ']+)$/i', $_SERVER['PATH_INFO'], $match)) { $targetProduct = $match[1]; $version = PonyDocsProductVersion::GetVersions($targetProduct, TRUE); //check for product not found if (empty($version)) { PonyDocsExtension::redirectToLandingPage(); return true; } } // Matches a URL like /Documentation/PRODUCT/VERSION/MANUAL // TODO: Should match PONYDOCS_PRODUCTMANUAL_LEGALCHARS instead of \w at the end if (preg_match('/^' . str_replace("/", "\\/", $wgScriptPath) . '\\/' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . '\\/([' . PONYDOCS_PRODUCT_LEGALCHARS . ']+)\\/([' . PONYDOCS_PRODUCTVERSION_LEGALCHARS . ']+)\\/(\\w+)\\/?$/i', $_SERVER['PATH_INFO'], $match)) { $targetProduct = $match[1]; $targetVersion = $match[2]; $targetManual = $match[3]; $p = PonyDocsProduct::GetProductByShortName($targetProduct); if (!$p instanceof PonyDocsProduct) { $wgHooks['BeforePageDisplay'][] = "PonyDocsExtension::handle404"; return false; } // User wants to find first topic in a requested manual. // Load up versions PonyDocsProductVersion::LoadVersionsForProduct($targetProduct); // Determine version if ($targetVersion == '') { // No version specified, use the user's selected version $ver = PonyDocsProductVersion::GetVersionByName($targetProduct, PonyDocsProductVersion::GetSelectedVersion($targetProduct)); } else { if (strtolower($targetVersion) == "latest") { // User wants the latest version. $ver = PonyDocsProductVersion::GetLatestReleasedVersion($targetProduct); } else { // Okay, they want to get a version by a specific name $ver = PonyDocsProductVersion::GetVersionByName($targetProduct, $targetVersion); } } if (!$ver) { if (PONYDOCS_DEBUG) { error_log("DEBUG [" . __METHOD__ . ":" . __LINE__ . "] redirecting to {$wgScriptPath}/" . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME); } header('Location: ' . $wgScriptPath . '/' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME); die; } // Okay, the version is valid, let's set the user's version. PonyDocsProductVersion::SetSelectedVersion($targetProduct, $ver->getVersionName()); PonyDocsProductManual::LoadManualsForProduct($targetProduct); $manual = PonyDocsProductManual::GetManualByShortName($targetProduct, $targetManual); if (!$manual) { // Rewrite to Main documentation if (PONYDOCS_DEBUG) { error_log("DEBUG [" . __METHOD__ . ":" . __LINE__ . "] redirecting to {$wgScriptPath}/" . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME); } header('Location: ' . $wgScriptPath . '/' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME); die; } elseif (!$manual->isStatic()) { // Get the TOC out of here! heehee $toc = new PonyDocsTOC($manual, $ver, $p); list($toc, $prev, $next, $start) = $toc->loadContent(); //Added empty check for WEB-10038 if (empty($toc)) { PonyDocsExtension::redirectToLandingPage(); return FALSE; } foreach ($toc as $entry) { if (isset($entry['link']) && $entry['link'] != "") { // We found the first article in the manual with a link. // Redirect to it. if (PONYDOCS_DEBUG) { error_log("DEBUG [" . __METHOD__ . ":" . __LINE__ . "] redirecting to " . $entry['link']); } header("Location: " . $entry['link']); die; } } //Replace die with a warning log and redirect error_log("WARNING [" . __METHOD__ . ":" . __LINE__ . "] redirecting to " . PonyDocsExtension::getDefaultUrl()); PonyDocsExtension::redirectToLandingPage(); return FALSE; } } return TRUE; }
/** * Our primary setup function simply handles URL rewrites for aliasing (per spec) and calls our PonyDocsWiki singleton instance * to ensure it runs the data retrieval functions for versions and manuals and the like. */ function efPonyDocsSetup() { global $wgPonyDocs, $wgScriptPath, $wgArticlePath; // force mediawiki to start session for anonymous traffic if (session_id() == '') { wfSetupSession(); if (PONYDOCS_DEBUG) { error_log("DEBUG [" . __METHOD__ . "] started session"); } } // Set selected product from URL if (preg_match('/^' . str_replace("/", "\\/", $wgScriptPath) . '\\/((index.php\\?title=)|)' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . '[\\/|:]{1}([' . PONYDOCS_PRODUCT_LEGALCHARS . ']+)[\\/|:]?/i', $_SERVER['PATH_INFO'], $match)) { PonyDocsProduct::SetSelectedProduct($match[3]); } // Set selected version from URL // - every time from /-separated title URLs // - only when no selected version already set from :-separated title $currentVersion = PonyDocsProductVersion::GetSelectedVersion(PonyDocsProduct::GetSelectedProduct(), FALSE); if (preg_match('/^' . str_replace("/", "\\/", $wgScriptPath) . '\\/((index.php\\?title=)|)' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . '\\/([' . PONYDOCS_PRODUCT_LEGALCHARS . ']+)\\/([' . PONYDOCS_PRODUCTVERSION_LEGALCHARS . ']+)/i', $_SERVER['PATH_INFO'], $match) || preg_match('/^' . str_replace("/", "\\/", $wgScriptPath) . '\\/((index.php\\?title=)|)' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . '\\/([' . PONYDOCS_PRODUCT_LEGALCHARS . ']+)\\/[' . PONYDOCS_PRODUCTMANUAL_LEGALCHARS . ']+TOC([' . PONYDOCS_PRODUCTVERSION_LEGALCHARS . ']+)/i', $_SERVER['PATH_INFO'], $match) || !isset($currentVersion) && preg_match('/^' . str_replace("/", "\\/", $wgScriptPath) . '\\/((index.php\\?title=)|)' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':([' . PONYDOCS_PRODUCT_LEGALCHARS . ']+):[' . PONYDOCS_PRODUCTMANUAL_LEGALCHARS . ']+TOC([' . PONYDOCS_PRODUCTVERSION_LEGALCHARS . ']+)/i', $_SERVER['PATH_INFO'], $match) || !isset($currentVersion) && preg_match('/^' . str_replace("/", "\\/", $wgScriptPath) . '\\/((index.php\\?title=)|)' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':([' . PONYDOCS_PRODUCT_LEGALCHARS . ']+):[' . PONYDOCS_PRODUCTMANUAL_LEGALCHARS . ']+:[^:]+:([' . PONYDOCS_PRODUCTVERSION_LEGALCHARS . ']+)/i', $_SERVER['PATH_INFO'], $match)) { $result = PonyDocsProductVersion::SetSelectedVersion($match[3], $match[4]); if (is_null($result)) { // this version isn't available to this user; go away $defaultRedirect = PonyDocsExtension::getDefaultUrl(); if (PONYDOCS_DEBUG) { error_log("DEBUG [" . __METHOD__ . ":" . __LINE__ . "] redirecting to {$defaultRedirect}"); } header("Location: " . $defaultRedirect); exit; } } PonyDocsWiki::getInstance(PonyDocsProduct::GetSelectedProduct()); }
/** * 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; }
/** * This is called when a version change occurs in the select box. It should update the version * only; to update the page the Ajax function in JS should then refresh by using history.go(0) * or something along those lines, otherwise the content may reflect the old version selection. * * @param string $version New version tag to set as current. Should be some checking. * @param string $title The current title that the person resides in, if any. * @param boolean $force Force the change, no matter if a doc is in the same version or not * @return AjaxResponse */ function efPonyDocsAjaxChangeVersion($product, $version, $title, $force = false) { global $wgArticlePath; $dbr = wfGetDB(DB_SLAVE); PonyDocsProduct::SetSelectedProduct($product); PonyDocsProductVersion::SetSelectedVersion($product, $version); $response = new AjaxResponse(); if ($force) { // This is coming from the search page. let's not do any title look up, // and instead just pass back the same url. $leadingSlash = "/"; if (substr($title, 0, 1) == "/") { $leadingSlash = ""; } $response->addText($leadingSlash . $title); // Need to make the url non-relative return $response; } $defaultTitle = PONYDOCS_DOCUMENTATION_NAMESPACE_NAME; if (preg_match('/' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':(.*):(.*):(.*):(.*)/i', $title, $match)) { $res = $dbr->select('categorylinks', 'cl_from', array("cl_to = 'V:" . $dbr->strencode($product . ":" . $version) . "'", 'cl_type = "page"', "cl_sortkey LIKE '" . $dbr->strencode(strtoupper($product . ':' . $match[2] . ':' . $match[3])) . ":%'"), __METHOD__); if ($res->numRows()) { $response->addText(str_replace('$1', PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . '/' . $product . '/' . $version . '/' . $match[2] . '/' . $match[3], $wgArticlePath)); if (PONYDOCS_DEBUG) { error_log("DEBUG [" . __METHOD__ . ":" . __LINE__ . "] ajax redirect rule 1"); } } else { // same manual/topic doesn't exist for newly selected version, redirect to default $response->addText(str_replace('$1', PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . '/' . $product . '/' . $version, $wgArticlePath)); if (PONYDOCS_DEBUG) { error_log("DEBUG [" . __METHOD__ . ":" . __LINE__ . "] ajax redirect rule 2"); } } } elseif (preg_match('/' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':(.*):(Manuals|Versions)/i', $title, $match)) { // this is a manuals or versions page $add_text = str_replace('$1', PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':' . $product . ':' . $match[2], $wgArticlePath); /// FIXME we probably need to clear objectcache for this [product]:Manuals page, or even better, do not cache it(?) $response->addText($add_text); if (PONYDOCS_DEBUG) { error_log("DEBUG [" . __METHOD__ . ":" . __LINE__ . "] ajax redirect rule 3"); } } elseif (preg_match('/' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . '\\/(.*)\\/(.*)\\/(.*)\\/(.*)/i', $title, $match)) { /** * Just swap out the source version tag ($match[2]) with the selected version in the output URL. */ $response->addText(str_replace('$1', PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . '/' . $product . '/' . $version . '/' . $match[3] . '/' . $match[4], $wgArticlePath)); if (PONYDOCS_DEBUG) { error_log("DEBUG [" . __METHOD__ . ":" . __LINE__ . "] ajax redirect rule 4"); } } elseif (preg_match('/' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . '\\/(.*)\\/(.*)\\/(.*)/i', $title, $match)) { //Redirection for WEB-10264 $response->addText(str_replace('$1', PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . '/' . $product . '/' . $version . '/' . $match[3], $wgArticlePath)); if (PONYDOCS_DEBUG) { error_log("DEBUG [" . __METHOD__ . ":" . __LINE__ . "] ajax redirect rule to switch versions on a static manual"); } } else { $add_text = str_replace('$1', $defaultTitle . '/' . $product . '/' . $version, $wgArticlePath); $response->addText($add_text); if (PONYDOCS_DEBUG) { error_log("DEBUG [" . __METHOD__ . ":" . __LINE__ . "] ajax redirect rule 5"); } } if (PONYDOCS_DEBUG) { error_log("DEBUG [" . __METHOD__ . ":" . __LINE__ . "] ajax redirect result " . print_r($response, true)); } return $response; }