/** * Returns the manual data for a version in cache. If the cache is not populated for * that version, then build it and return it. * @param string $product product short name * @param string $version version name * @return array of manual navigation items */ public static function fetchNavDataForVersion($product, $version) { $key = "NAVDATA-" . $product . "-" . $version; $cache = PonyDocsCache::getInstance(); $cacheEntry = $cache->get($key); if ($cacheEntry === null) { if (PONYDOCS_DEBUG) { error_log("DEBUG [" . __METHOD__ . "] Creating new navigation cache file for product {$product} version {$version}"); } $oldVersion = PonyDocsProductVersion::GetSelectedVersion($product); PonyDocsProductVersion::SetSelectedVersion($product, $version); $ver = PonyDocsProductVersion::GetVersionByName($product, PonyDocsProductVersion::GetSelectedVersion($product)); if (!is_object($ver)) { return array(); } $pr = PonyDocsProduct::GetProductByShortName($product); $manuals = PonyDocsProductManual::LoadManualsForProduct($product, TRUE); $cacheEntry = array(); foreach ($manuals as $manual) { if ($manual->isStatic()) { $staticVersions = $manual->getStaticVersionNames(); if (in_array($version, $staticVersions)) { $cacheEntry[$manual->getShortName()] = array('shortName' => $manual->getShortName(), 'longName' => $manual->getLongName(), 'firstUrl' => '/' . implode('/', array(PONYDOCS_DOCUMENTATION_NAMESPACE_NAME, $product, $version, $manual->getShortName()))); } } else { $toc = new PonyDocsTOC($manual, $ver, $pr); list($items, $prev, $next, $start) = $toc->loadContent(); foreach ($items as $entry) { if (isset($entry['link']) && $entry['link'] != '') { // Found first article. $cacheEntry[$manual->getShortName()] = array('shortName' => $manual->getShortName(), 'longName' => $manual->getLongName(), 'categories' => implode(',', $manual->getCategories()), 'description' => $toc->getManualDescription(), 'firstTitle' => $entry['title'], 'firstUrl' => $entry['link']); break; } } } } $cache->put($key, $cacheEntry, NAVDATA_CACHE_TTL, NAVDATA_CACHE_TTL / 4); // Restore old version PonyDocsProductVersion::SetSelectedVersion($product, $oldVersion); PonyDocsProductManual::LoadManualsForProduct($product, TRUE); } else { if (PONYDOCS_DEBUG) { error_log("DEBUG [" . __METHOD__ . "] Fetched navigation cache from PonyDocsCache for product {$product}"); } } return $cacheEntry; }
/** * 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; }
/** * 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; }
/** * 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 only when positive lookbehind is a whitespace character // replace \n -> <br/> // replace \t -> 8 * /* 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(" ", "<br/>", " "), $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; }
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; } }
/** * 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; }