/** * 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; }
/** * Here we handle any unknown/custom actions. For now these are: * - 'print': Produce printer ready output of a single topic or entire manual; request param 'type' should * be set to either 'topic' or 'manual' and topic must be defined (as title). * * @static * @param string $action * @param Article $article * @return boolean|string */ public static function onUnknownAction($action, &$article) { global $wgRequest, $wgParser, $wgTitle; global $wgHooks; $ponydocs = PonyDocsWiki::getInstance(); $dbr = wfGetDB(DB_SLAVE); /** * This is accessed when we want to REMOVE version tags from a supplied topic. * title= Topic to remove version tags from. * versions= List of versions, colon delimited, to remove. * * This is intended to be an AJAX call and produces no output. */ if (!strcmp($action, 'ajax-removetags')) { /** * First open the title and strip the [[Category]] tags from the content and save. */ $versions = explode(',', $wgRequest->getVal('versions')); $product = $wgRequest->getVal('product'); $title = Title::newFromText($wgRequest->getVal('title')); $article = new Article($title); $content = $article->getContent(); $findArray = $repArray = array(); foreach ($versions as $v) { $findArray[] = '/\\[\\[\\s*Category\\s*:\\s*V:\\s*' . $v . '\\]\\]/i'; $repArray[] = ''; } $content = preg_replace($findArray, $repArray, $content); $article->doEdit($content, 'Automatic removal of duplicate version tags.', EDIT_UPDATE); /** * Now update the categorylinks (is this needed?). */ $q = "DELETE FROM categorylinks" . " WHERE cl_sortkey = '" . $dbr->strencode(strtoupper($title->getText())) . "'" . " AND cl_to IN ('V:{$product}:" . implode("','V:{$product}:", $versions) . "')" . " AND cl_type = 'page'"; $res = $dbr->query($q, __METHOD__); /** * Do not output anything, this is an AJAX call. */ die; } else { if (!strcmp($action, 'doprint')) { $type = 'topic'; if ($wgRequest->getVal('type') || strlen($wgRequest->getVal('type'))) { if (!strcasecmp($wgRequest->getVal('type'), 'topic') && !strcasecmp($wgRequest->getVal('type'), 'manual')) { // Invalid! } $type = strtolower($wgRequest->getVal('type')); } if (!strcmp($type, 'topic')) { $article = new Article(Title::newFromText($wgRequest->getVal('title'))); $c = $article->getContent(); die; } die("Print!"); } } return true; }
/** * This expects to find: * {{#topic:Text Name}} * * @param Parser $parser * @param string $param1 Full text name of topic, must be converted to wiki topic name. * @return array * * TODO: Much of this function duplicates code above in efGetTitleFromMarkup(), can we DRY? * There really shouldn't be any real code in this file, just calls to class methods... */ function efTopicParserFunction_Render(&$parser, $param1 = '') { global $wgArticlePath, $wgTitle, $action; if (PonyDocsExtension::isSpeedProcessingEnabled()) { return TRUE; } /** * We ignore this parser function if not in a TOC management page. */ if (!preg_match('/' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':(.*):(.*)TOC(.*)/i', $wgTitle->__toString(), $matches)) { return FALSE; } $manualShortName = $matches[2]; $productShortName = $matches[1]; PonyDocsWiki::getInstance($productShortName); /** * Get the earliest tagged version of this TOC page and append it to the wiki page? * Ensure the manual is valid then use PonyDocsManual::getManualByShortName(). * Next attempt to get the version tags for this page -- which may be NONE -- * and from this determine the "earliest" version to which this page applies. * * TODO: This comment is duplicated above in efGetTitleFromMarkup, can we DRY? */ if (!PonyDocsProductManual::IsManual($productShortName, $manualShortName)) { return FALSE; } $pManual = PonyDocsProductManual::GetManualByShortName($productShortName, $manualShortName); $pTopic = new PonyDocsTopic(new Article($wgTitle)); /** * @FIXME: If TOC page is NOT tagged with any versions we cannot create the pages/links to the * topics, right? */ $manVersionList = $pTopic->getProductVersions(); if (!sizeof($manVersionList)) { return $parser->insertStripItem($param1, $parser->mStripState); } $earliestVersion = PonyDocsProductVersion::findEarliest($productShortName, $manVersionList); /** * Clean up the full text name into a wiki-form. This means remove spaces, #, ?, and a few other * characters which are not valid or wanted. It's not important HOW this is done as long as it is * consistent. */ $wikiTopic = preg_replace('/([^' . str_replace(' ', '', Title::legalChars()) . '])/', '', $param1); $wikiPath = PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':' . $productShortName . ':' . $manualShortName . ':' . $wikiTopic; $dbr = wfGetDB(DB_SLAVE); /** * Now look in the database for any instance of this topic name PLUS :<version>. * We need to look in categorylinks for it to find a record with a cl_to (version tag) * which is equal to the set of the versions for this TOC page. * For instance, if the TOC page was for versions 1.0 and 1.1 and our topic was 'How To Foo' * we need to find any cl_sortkey which is 'HowToFoo:%' and has a cl_to equal to 1.0 or 1.1. * There should only be 0 or 1, so we ignore anything beyond 1. * If found, we use THAT cl_sortkey as the link; * if NOT found we create a new topic, the name being the compressed topic name plus the earliest TOC version * ($earliestVersion->getName()). * We then need to ACTUALLY create it in the database, tag it with all the versions the TOC mgmt page is tagged with, * and set the H1 to the text inside the parser function. * * @fixme: Can we test if $action=save here so we don't do this on every page view? */ $versionIn = array(); foreach ($manVersionList as $pV) { $versionIn[] = $productShortName . ':' . $pV->getVersionName(); } $res = $dbr->select(array('categorylinks', 'page'), 'page_title', array('cl_from = page_id', 'page_namespace = "' . NS_PONYDOCS . '"', "cl_to IN ('V:" . implode("','V:", $versionIn) . "')", 'cl_type = "page"', "cl_sortkey LIKE '" . $dbr->strencode(strtoupper($productShortName . ':' . $manualShortName . ':' . $wikiTopic)) . ":%'"), __METHOD__); $topicName = ''; if (!$res->numRows()) { /** * No match -- so this is a "new" topic. Set name. */ $topicName = PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':' . $productShortName . ':' . $manualShortName . ':' . $wikiTopic . ':' . $earliestVersion->getVersionName(); } else { $row = $dbr->fetchObject($res); $topicName = PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ":{$row->page_title}"; } $output = '<a href="' . wfUrlencode(str_replace('$1', $topicName, $wgArticlePath)) . '">' . $param1 . '</a>'; return $parser->insertStripItem($output, $parser->mStripState); }
/** * This is called upon loading the special page. It should write output to the page with $wgOut. */ public function execute() { global $wgOut, $wgArticlePath, $wgScriptPath; global $wgUser; $dbr = wfGetDB(DB_SLAVE); $this->setHeaders(); $wgOut->setPagetitle('Documentation Rename Version'); $forceProduct = PonyDocsProduct::GetSelectedProduct(); $ponydocs = PonyDocsWiki::getInstance($forceProduct); $products = $ponydocs->getProductsForTemplate(); // Security Check $authProductGroup = PonyDocsExtension::getDerivedGroup(PonyDocsExtension::ACCESS_GROUP_PRODUCT, $forceProduct); $groups = $wgUser->getGroups(); if (!in_array($authProductGroup, $groups)) { $wgOut->addHTML('<p>Sorry, but you do not have permission to access this Special page.</p>'); return; } ob_start(); // Grab all versions available for product // We need to get all versions from PonyDocsProductVersion $versions = PonyDocsProductVersion::GetVersions($forceProduct); ?> <input type="hidden" id="force_product" value="<?php echo $forceProduct; ?> " /> <div id="renameversion"> <a name="top"></a> <div class="versionselect"> <h1>Rename Version Console</h1> Select product, source version, and target version. You will be able to approve the list of manuals to rename before you launch the process. <h2>Choose a Product</h2> <?php if (!count($products)) { print "<p>No products defined.</p>"; } else { ?> <div class="product"> <select id="docsProductSelect1" name="selectedProduct" onChange="AjaxChangeProduct1();"> <?php foreach ($products as $idx => $data) { echo '<option value="' . $data['name'] . '" '; if (!strcmp($data['name'], $forceProduct)) { echo 'selected'; } echo '>' . $data['label'] . '</option>'; } ?> </select> </div> <script language="javascript"> function AjaxChangeProduct1_callback( o ) { document.getElementById( 'docsProductSelect1' ).disabled = true; var s = new String( o.responseText ); document.getElementById( 'docsProductSelect1' ).disabled = false; window.location.href = s; } function AjaxChangeProduct1() { var productIndex = document.getElementById( 'docsProductSelect1' ).selectedIndex; var product = document.getElementById( 'docsProductSelect1' )[productIndex].value; var title = '<?php echo $_SERVER['REQUEST_URI']; ?> '; // TODO fix this title var force = true; sajax_do_call( 'efPonyDocsAjaxChangeProduct', [product, title, force], AjaxChangeProduct1_callback, true); } </script> <?php } ?> <h2>Choose a Source Version</h2> <select name="version" id="versionselect_sourceversion"> <?php foreach ($versions as $version) { ?> <option value="<?php echo $version->getVersionName(); ?> "> <?php echo $version->getVersionName() . " - " . $version->getVersionStatus(); ?> </option> <?php } ?> </select> <h2>Choose a Target Version</h2> <select name="version" id="versionselect_targetversion"> <?php foreach ($versions as $version) { ?> <option value="<?php echo $version->getVersionName(); ?> "> <?php echo $version->getVersionName() . " - " . $version->getVersionStatus(); ?> </option> <?php } ?> </select> <div> <input type="button" id="versionselect_submit" value="Fetch Manuals" /> </div> </div> <div class="submitrequest" style="display: none;"> <div id="manuallist"></div> <input type="button" id="renameversion_submit" value="Rename Version" /> <div id="progressconsole"></div> </div> <div class="completed" style="display: none;"> <p class="summary"> <strong>Source Version:</strong> <span class="sourceversion"></span> <strong>Target Version:</strong> <span class="targetversion"></span> </p> <h2>Process Complete</h2> The following is the log of the processed job. Look it over for any potential issues that may have occurred during the Rename Version job. <div> <div class="logconsole" style="font-family: monospace; font-size: 10px;"></div> </div> </div> </div> <?php $buffer = ob_get_clean(); $wgOut->addHTML($buffer); return TRUE; }
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; } }
/** * Called when an unknown action occurs on url. We are only interested in pdfbook action. */ function onUnknownAction($action, $article) { global $wgOut, $wgUser, $wgTitle, $wgParser, $wgRequest; global $wgServer, $wgArticlePath, $wgScriptPath, $wgUploadPath, $wgUploadDirectory, $wgScript, $wgStylePath; // We don't do any processing unless it's pdfbook if ($action != 'pdfbook') { return true; } // Get the title and make sure we're in Documentation namespace $title = $article->getTitle(); if ($title->getNamespace() != NS_PONYDOCS) { return true; } // Grab parser options for the logged in user. $opt = ParserOptions::newFromUser($wgUser); // Log the export $msg = $wgUser->getUserPage()->getPrefixedText() . ' exported as a PonyDocs PDF Book'; $log = new LogPage('ponydocspdfbook', false); $log->addEntry('book', $wgTitle, $msg); // Initialise PDF variables $layout = '--firstpage p1'; $x_margin = '1.25in'; $y_margin = '1in'; $font = 'Arial'; $size = '12'; $linkcol = '4d9bb3'; $levels = '2'; $exclude = array(); $width = '1024'; $width = "--browserwidth 1024"; // Determine articles to gather $articles = array(); $pieces = explode(":", $wgTitle->__toString()); // Try and get rid of the TOC portion of the title if (strpos($pieces[2], "TOC") && count($pieces) == 3) { $pieces[2] = substr($pieces[2], 0, strpos($pieces[2], "TOC")); } else { if (count($pieces) != 5) { // something is wrong, let's get out of here $defaultRedirect = PonyDocsExtension::getDefaultUrl(); if (PONYDOCS_DEBUG) { error_log("DEBUG [" . __METHOD__ . ":" . __LINE__ . "] redirecting to {$defaultRedirect}"); } header("Location: " . $defaultRedirect); exit; } } $productName = $pieces[1]; $ponydocs = PonyDocsWiki::getInstance($productName); $pProduct = PonyDocsProduct::GetProductByShortName($productName); if ($pProduct === NULL) { // product wasn't valid wfProfileOut(__METHOD__); $wgOut->setStatusCode(404); return FALSE; } $productLongName = $pProduct->getLongName(); if (PonyDocsProductManual::isManual($productName, $pieces[2])) { $pManual = PonyDocsProductManual::GetManualByShortName($productName, $pieces[2]); } $versionText = PonyDocsProductVersion::GetSelectedVersion($productName); if (!empty($pManual)) { // We should always have a pManual, if we're printing from a TOC $v = PonyDocsProductVersion::GetVersionByName($productName, $versionText); // We have our version and our manual Check to see if a file already exists for this combination $pdfFileName = "{$wgUploadDirectory}/ponydocspdf-" . $productName . "-" . $versionText . "-" . $pManual->getShortName() . "-book.pdf"; // Check first to see if this PDF has already been created and is up to date. If so, serve it to the user and stop // execution. if (file_exists($pdfFileName)) { error_log("INFO [PonyDocsPdfBook::onUnknownAction] " . php_uname('n') . ": cache serve username=\"" . $wgUser->getName() . "\" product=\"" . $productName . "\" version=\"" . $versionText . "\" " . " manual=\"" . $pManual->getShortName() . "\""); PonyDocsPdfBook::servePdf($pdfFileName, $productName, $versionText, $pManual->getShortName()); // No more processing return false; } } else { error_log("ERROR [PonyDocsPdfBook::onUnknownAction] " . php_uname('n') . ": User attempted to print a pdfbook from a non TOC page with path:" . $wgTitle->__toString()); } $html = self::getManualHTML($pProduct, $pManual, $v); // HTMLDOC does not care for utf8. $html = utf8_decode("{$html}\n"); // Write the HTML to a tmp file $file = "{$wgUploadDirectory}/" . uniqid('ponydocs-pdf-book'); $fh = fopen($file, 'w+'); fwrite($fh, $html); fclose($fh); // Okay, create the title page $titlepagefile = "{$wgUploadDirectory}/" . uniqid('ponydocs-pdf-book-title'); $fh = fopen($titlepagefile, 'w+'); $coverPageHTML = self::getCoverPageHTML($pProduct, $pManual, $v); fwrite($fh, $coverPageHTML); fclose($fh); $format = 'manual'; /* @todo Modify so single topics can be printed in pdf */ $footer = $format == 'single' ? '...' : '.1.'; $toc = $format == 'single' ? '' : " --toclevels {$levels}"; // Send the file to the client via htmldoc converter $wgOut->disable(); $cmd = " --left {$x_margin} --right {$x_margin} --top {$y_margin} --bottom {$y_margin}"; $cmd .= " --header ... --footer {$footer} --tocfooter .i. --quiet --jpeg --color"; $cmd .= " --bodyfont {$font} --fontsize {$size} --linkstyle plain --linkcolor {$linkcol}"; $cmd .= "{$toc} --format pdf14 {$layout} {$width} --titlefile {$titlepagefile} --size letter"; $cmd = "htmldoc -t pdf --book --charset iso-8859-1 --no-numbered {$cmd} {$file} > {$pdfFileName}"; putenv("HTMLDOC_NOCGI=1"); $output = array(); $returnVar = 1; exec($cmd, $output, $returnVar); if ($returnVar != 0) { // 0 is success error_log("INFO [PonyDocsPdfBook::onUnknownAction] " . php_uname('n') . ": Failed to run htmldoc (" . $returnVar . ") Output is as follows: " . implode("-", $output)); print "Failed to create PDF. Our team is looking into it."; } // Delete the htmlfile and title file from the filesystem. @unlink($file); if (file_exists($file)) { error_log("ERROR [PonyDocsPdfBook::onUnknownAction] " . php_uname('n') . ": Failed to delete temp file {$file}"); } @unlink($titlepagefile); if (file_exists($titlepagefile)) { error_log("ERROR [PonyDocsPdfBook::onUnknownAction] " . php_uname('n') . ": Failed to delete temp file {$titlepagefile}"); } // Okay, let's add an entry to the error log to dictate someone requested a pdf error_log("INFO [PonyDocsPdfBook::onUnknownAction] " . php_uname('n') . ": fresh serve username=\"" . $wgUser->getName() . "\" version=\"{$versionText}\" " . " manual=\"" . $pManual->getLongName() . "\""); PonyDocsPdfBook::servePdf($pdfFileName, $productName, $versionText, $pManual->getLongName()); // No more processing return false; }
/** * This is called upon loading the special page. It should write output to the page with $wgOut. */ public function execute() { global $wgOut, $wgArticlePath, $wgScriptPath; global $wgUser; $dbr = wfGetDB(DB_SLAVE); $this->setHeaders(); $wgOut->setPagetitle('Documentation Branch And Inheritance'); // if title is set we have our product and manual, else take selected product if (isset($_GET['titleName'])) { if (!preg_match('/' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':(.*):(.*):(.*):(.*)/', $_GET['titleName'], $match)) { throw new Exception("Invalid Title to Branch From"); } $forceProduct = $match[1]; $forceManual = $match[2]; } else { $forceProduct = PonyDocsProduct::GetSelectedProduct(); $ponydocs = PonyDocsWiki::getInstance($forceProduct); $products = $ponydocs->getProductsForTemplate(); } // Security Check $authProductGroup = PonyDocsExtension::getDerivedGroup(PonyDocsExtension::ACCESS_GROUP_PRODUCT, $forceProduct); $groups = $wgUser->getGroups(); if (!in_array($authProductGroup, $groups)) { $wgOut->addHTML("<p>Sorry, but you do not have permission to access this Special page.</p>"); return; } // Static product check if (PonyDocsProduct::GetProductByShortName($forceProduct)->isStatic()) { $wgOut->addHTML("<p>Sorry, but you cannot branch/inherit a static product.</p>"); return; } ob_start(); // Grab all versions available for product // We need to get all versions from PonyDocsProductVersion $versions = PonyDocsProductVersion::GetVersions($forceProduct); if (isset($_GET['titleName'])) { ?> <input type="hidden" id="force_titleName" value="<?php echo $_GET['titleName']; ?> " /> <input type="hidden" id="force_sourceVersion" value="<?php echo PonyDocsProductVersion::GetVersionByName($forceProduct, PonyDocsProductVersion::GetSelectedVersion($forceProduct))->getVersionName(); ?> " /> <input type="hidden" id="force_manual" value="<?php echo $forceManual; ?> " /> <?php } ?> <input type="hidden" id="force_product" value="<?php echo $forceProduct; ?> " /> <div id="docbranchinherit"> <a name="top"></a> <div class="versionselect"> <h1>Branch and Inheritance Console</h1> Begin by selecting your product, source version material and a target version below. You will then be presented with additional screens to specify branch and inherit behavior. <?php if (isset($_GET['titleName'])) { ?> <p> Requested Operation on Single Topic: <strong><?php echo $_GET['titleName']; ?> </strong> </p> <?php } ?> <h2>Choose a Product</h2> <?php if (isset($_GET['titleName'])) { ?> You have selected a product: <?php echo $forceProduct; ?> <?php } else { if (!count($products)) { print "<p>No products defined.</p>"; } else { ?> <div class="product"> <select id="docsProductSelect1" name="selectedProduct" onChange="AjaxChangeProduct1();"> <?php foreach ($products as $idx => $data) { echo '<option value="' . $data['name'] . '" '; if (!strcmp($data['name'], $forceProduct)) { echo 'selected'; } echo '>' . $data['label'] . '</option>'; } ?> </select> </div> <script language="javascript"> function AjaxChangeProduct1_callback( o ) { document.getElementById('docsProductSelect1').disabled = true; var s = new String( o.responseText ); document.getElementById('docsProductSelect1').disabled = false; window.location.href = s; } function AjaxChangeProduct1( ) { var productIndex = document.getElementById('docsProductSelect1').selectedIndex; var product = document.getElementById('docsProductSelect1')[productIndex].value; var title = '<?php echo $_SERVER['REQUEST_URI']; ?> '; // TODO fix this title var force = true; sajax_do_call( 'efPonyDocsAjaxChangeProduct', [product,title,force], AjaxChangeProduct1_callback,true); } </script> <?php } } ?> <h2>Choose a Source Version</h2> <?php // Determine if topic was set, if so, we should fetch version from currently selected version. if (isset($_GET['titleName'])) { $version = PonyDocsProductVersion::GetVersionByName($forceProduct, PonyDocsProductVersion::GetSelectedVersion($forceProduct)); ?> You have selected a topic. We are using the version you are currently browsing: <?php echo $version->getVersionName(); ?> <?php } else { ?> <select name="version" id="versionselect_sourceversion"> <?php foreach ($versions as $version) { ?> <option value="<?php echo $version->getVersionName(); ?> "><?php echo $version->getVersionName() . " - " . $version->getVersionStatus(); ?> </option> <?php } ?> </select> <?php } ?> <h2>Choose a Target Version</h2> <select name="version" id="versionselect_targetversion"> <?php foreach ($versions as $version) { ?> <option value="<?php echo $version->getVersionName(); ?> "><?php echo $version->getVersionName() . " - " . $version->getVersionStatus(); ?> </option> <?php } ?> </select> <p> <input type="button" id="versionselect_submit" value="Continue to Manuals" /> </p> </div> <div class="manualselect" style="display: none;"> <?php if (isset($_GET['titleName'])) { ?> <p> Requested Operation on Single Topic: <strong><?php echo $_GET['titleName']; ?> </strong> </p> <?php } ?> <p class="summary"> <strong>Source Version:</strong> <span class="sourceversion"></span> <strong>Target Version:</strong> <span class="targetversion"></span> </p> <h1>Choose Manuals To Branch/Inherit From</h1> <div id="manualselect_manuals"> </div> <h1>Choose Default Action For Topics</h1> <input type="radio" selected="selected" name="manualselect_action" value="ignore" id="manualselect_action_ignore"><label for="manualselect_action_ignore">Ignore - Do Nothing</label><br /> <input type="radio" name="manualselect_action" value="inherit" id="manualselect_action_inherit"><label for="manualselect_action_inherit">Inherit - Add Target Version to Existing Topic</label><br /> <input type="radio" name="manualselect_action" value="branch" id="manualselect_action_branch"><label for="manualselect_action_branch">Branch - Create a copy of existing topic with Target Version</label><br /> <br /> <input type="button" id="manualselect_submit" value="Continue to Topics" /> </div> <div class="topicactions" style="display: none;"> <?php if (isset($_GET['titleName'])) { ?> <p> Requested Operation on Single Topic: <strong><?php echo $_GET['titleName']; ?> </strong> </p> <?php } ?> <p class="summary"> <strong>Source Version:</strong> <span class="sourceversion"></span> <strong>Target Version:</strong> <span class="targetversion"></span> </p> <h1>Specify Topic Actions</h1> <div class="container"> </div> <br /> <br /> <input type="button" id="topicactions_submit" value="Process Request" /> <div id="progressconsole"></div> </div> <div class="completed" style="display: none;"> <p class="summary"> <strong>Source Version:</strong> <span class="sourceversion"></span> <strong>Target Version:</strong> <span class="targetversion"></span> </p> <h2>Process Complete</h2> The following is the log of the processed job. Look it over for any potential issues that may have occurred during the branch/inherit job. <div> <div class="logconsole" style="font-family: monospace; font-size: 10px;"> </div> </div> </div> </div> <?php $buffer = ob_get_clean(); $wgOut->addHTML($buffer); return true; }