/**
  * This is called upon loading the special page. It should write output to the page with $wgOut.
  * 
  * @param string $par The portion of the URI after Special:StaticDocServer/
  */
 public function execute($par)
 {
     #TODO: switch to $this->getOuput() and $this->getRequest() when we upgrade MW
     global $wgOut, $wgRequest;
     $wgOut->disable();
     $found = FALSE;
     list($productName, $versionName, $path) = explode('/', $par, 3);
     if (substr($par, -1, 1) == '/') {
         $par .= 'index.html';
     }
     // Validate parameters are set
     if (isset($productName) && isset($versionName) && PonyDocsProduct::GetProductByShortName($productName) && PonyDocsProductVersion::GetVersionByName($productName, $versionName)) {
         $filename = PONYDOCS_STATIC_DIR . "/{$par}";
         if (file_exists($filename)) {
             $found = TRUE;
         }
     }
     if (!$found) {
         $wgRequest->response()->header("HTTP/1.1 404 Not Found");
         echo "<html>\n";
         echo "<head><title>Not Found</title></head>\n";
         echo "<body>\n";
         echo "<h1>Bad Request</h1>\n";
         echo "<div>The documentation you have requested does not exist.</div>\n";
         echo "</body>\n";
         echo "</html>\n";
     } else {
         $mimeMagic = MimeMagic::singleton();
         $pathParts = pathinfo($filename);
         /* get mime-type for a specific file */
         header('Content-type: ' . $mimeMagic->guessTypesForExtension($pathParts['extension']));
         readfile($filename);
     }
 }
Beispiel #2
0
 /**
  * This is called upon loading the special page.  It should write output to the page with $wgOut.
  * @param string $par the URL path following the special page name
  */
 public function execute($par)
 {
     global $wgOut, $wgArticlePath;
     $dbr = wfGetDB(DB_SLAVE);
     $this->setHeaders();
     /**
      * We need to select ALL pages of the form:
      * 	Documentation:<productShortName>:<manualShortName>TOC*
      * We should group these by manual and then by descending version order.  The simplest way is by assuming that every TOC
      * page is linked to at least one version (category) and thus has an entry in the categorylinks table.  So to do this we
      * must run this query for each manual type, which involes getting the list of manuals defined.
      */
     $out = array();
     // Added for WEB-10802, looking for product name passed in
     // e.g. /Special:TOCList/Splunk
     $parts = explode('/', $par);
     if (isset($parts[0]) && is_string($parts[0]) and $parts[0] != '') {
         $productName = $parts[0];
     } else {
         $productName = PonyDocsProduct::GetSelectedProduct();
     }
     $manuals = PonyDocsProductManual::GetDefinedManuals($productName);
     $allowed_versions = array();
     $product = PonyDocsProduct::GetProductByShortName($productName);
     $wgOut->setPagetitle('Table of Contents Management');
     if ($product) {
         $wgOut->addHTML('<h2>Table of Contents Management Pages for ' . $product->getLongName() . '</h2>');
         foreach (PonyDocsProductVersion::GetVersions($productName) as $v) {
             $allowed_versions[] = $v->getVersionName();
         }
         foreach ($manuals as $pMan) {
             $res = $dbr->select(array('categorylinks', 'page'), array('page_title', 'GROUP_CONCAT( cl_to separator "|") categories'), array('cl_from = page_id', 'page_namespace = "' . NS_PONYDOCS . '"', "page_title LIKE '" . $dbr->strencode("{$productName}:" . $pMan->getShortName()) . "TOC%'", 'cl_to LIKE "V:%:%"', 'cl_type = "page"'), __METHOD__, array('GROUP BY' => 'page_title'));
             while ($row = $dbr->fetchObject($res)) {
                 $versions = array();
                 $categories = explode('|', $row->categories);
                 foreach ($categories as $category) {
                     $categoryParts = explode(':', $category);
                     if (in_array($categoryParts[2], $allowed_versions)) {
                         $versions[] = $categoryParts[2];
                     }
                 }
                 if (sizeof($versions)) {
                     $wgOut->addHTML('<a href="' . str_replace('$1', PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ":{$row->page_title}", $wgArticlePath) . '">' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ":{$row->page_title}" . '</a> - Versions: ' . implode(' | ', $versions) . '<br />');
                 }
             }
         }
         $html = '<h2>Other Useful Management Pages</h2>' . '<a href="' . str_replace('$1', PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':' . $productName . PONYDOCS_PRODUCTVERSION_SUFFIX, $wgArticlePath) . '">Version Management</a> - Define and update available ' . $productName . ' versions.<br />' . '<a href="' . str_replace('$1', PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':' . $productName . PONYDOCS_PRODUCTMANUAL_SUFFIX, $wgArticlePath) . '">Manuals Management</a> - Define the list of available manuals for the Documentation namespace.' . '<br/><br/>';
         $wgOut->addHTML($html);
     } else {
         $wgOut->addHTML("<h2>Table of Contents Management Page</h2>Error: Product {$productName} does not exist.");
     }
 }
 /**
  * Used to render a hidden text field which will hold what version we were 
  * in.  This forced the following edit submission to put us back in the 
  * version we were browsing.
  */
 public static function onShowEditFormFields(&$editpage, &$output)
 {
     // Add our form element to the top of the form.
     $product = PonyDocsProduct::GetSelectedProduct();
     $version = PonyDocsProductVersion::GetSelectedVersion($product);
     $output->mBodytext .= "<input type=\"hidden\" name=\"ponydocsversion\" value=\"" . $version . "\" />";
     $output->mBodytext .= "<input type=\"hidden\" name=\"ponydocsproduct\" value=\"" . $product . "\" />";
     return true;
 }
Beispiel #4
0
 /**
  * Called when an unknown action occurs on url.  We are only interested in zipmanual action.
  */
 function onUnknownAction($action, $article)
 {
     global $wgOut, $wgUser, $wgTitle, $wgParser, $wgRequest;
     global $wgServer, $wgArticlePath, $wgScriptPath, $wgUploadPath, $wgUploadDirectory, $wgScript, $wgStylePath;
     // We don't do any processing unless it's zipmanual
     if ($action != 'zipmanual') {
         return true;
     }
     $zipAllowed = false;
     PonyDocsExtension::onUserCan($wgTitle, $wgUser, 'zipmanual', &$zipAllowed);
     if (!$zipAllowed) {
         error_log("WARNING [" . __METHOD__ . "] User attempted to perform a ZIP Export without permission.");
         $defaultRedirect = PonyDocsExtension::getDefaultUrl();
         header("Location: " . $defaultRedirect);
         exit;
     }
     // Get the title and make sure we're in Documentation namespace
     $title = $article->getTitle();
     if ($title->getNamespace() != NS_PONYDOCS) {
         return true;
     }
     // Grab parser options for the logged in user.
     $opt = ParserOptions::newFromUser($wgUser);
     // Any potential titles to exclude
     $exclude = array();
     // Determine articles to gather
     $articles = array();
     $pieces = explode(":", $wgTitle->__toString());
     // Try and get rid of the TOC portion of the title
     if (strpos($pieces[2], "TOC") && count($pieces) == 3) {
         $pieces[2] = substr($pieces[2], 0, strpos($pieces[2], "TOC"));
     } else {
         if (count($pieces) != 5) {
             // something is wrong, let's get out of here
             $defaultRedirect = PonyDocsExtension::getDefaultUrl();
             if (PONYDOCS_DEBUG) {
                 error_log("DEBUG [" . __METHOD__ . ":" . __LINE__ . "] redirecting to {$defaultRedirect}");
             }
             header("Location: " . $defaultRedirect);
             exit;
         }
     }
     $productName = $pieces[1];
     $ponydocs = PonyDocsWiki::getInstance($productName);
     $pProduct = PonyDocsProduct::GetProductByShortName($productName);
     if ($pProduct === NULL) {
         // product wasn't valid
         wfProfileOut(__METHOD__);
         $wgOut->setStatusCode(404);
         return FALSE;
     }
     $productLongName = $pProduct->getLongName();
     if (PonyDocsProductManual::isManual($productName, $pieces[2])) {
         $pManual = PonyDocsProductManual::GetManualByShortName($productName, $pieces[2]);
     }
     $versionText = PonyDocsProductVersion::GetSelectedVersion($productName);
     if (!empty($pManual)) {
         // We should always have a pManual, if we're printing
         // from a TOC
         $v = PonyDocsProductVersion::GetVersionByName($productName, $versionText);
         $toc = new PonyDocsTOC($pManual, $v, $pProduct);
         list($manualtoc, $tocprev, $tocnext, $tocstart) = $toc->loadContent();
         // We successfully got our table of contents.  It's
         // stored in $manualtoc
         foreach ($manualtoc as $tocEntry) {
             if ($tocEntry['level'] > 0 && strlen($tocEntry['title']) > 0) {
                 $title = Title::newFromText($tocEntry['title']);
                 $articles[$tocEntry['section']][] = array('title' => $title, 'text' => $tocEntry['text']);
             }
         }
     } else {
         error_log("WARNING [" . __METHOD__ . "] " . php_uname('n') . ": User attempted to export ZIP from a non TOC page with path:" . $wgTitle->__toString());
     }
     $html = self::getManualHTML($pProduct, $pManual, $v);
     $coverPageHTML = self::getCoverPageHTML($pProduct, $pManual, $v, false);
     // Make a temporary directory to store our archive contents.
     $tempDirPath = sys_get_temp_dir() . '/ponydocs-zip-export-' . time();
     $success = @mkdir($tempDirPath);
     if (!$success) {
         error_log("FATAL [" . __METHOD__ . "] Failed to create temporary directory " . $tempDirPath . " for Zip Export.");
         throw new Exception('Failed to create temporary directory for Zip Export.');
     }
     // Now, let's fetch all the img elements for both and grab them all in
     // parallel.
     $imgData = array();
     // Initialize our RollingCurl instance
     $rollingCurl = new \RollingCurl\RollingCurl();
     $mh = curl_multi_init();
     $manualDoc = new DOMDocument();
     @$manualDoc->loadHTML($html);
     $coverPageDoc = new DOMDocument();
     @$coverPageDoc->loadHTML($coverPageHTML);
     self::prepareImageRequests($manualDoc, $rollingCurl, $tempDirPath, &$imgData);
     self::prepareImageRequests($coverPageDoc, $rollingCurl, $tempDirPath, &$imgData);
     // Execute the RollingCurl requests
     $rollingCurl->execute();
     // Now update all our image elements in our appropriate DOMDocs.
     foreach ($imgData as $img) {
         // Put the data into it.
         file_put_contents($img['local_path'], $img['request']->getResponseText());
         // Modify element
         $img['element']->setAttribute('src', $img['new_path']);
         // Do curl cleanup
     }
     $html = $manualDoc->saveHTML();
     $coverPageHTML = $coverPageDoc->saveHTML();
     // Write the HTML to a tmp file
     $file = tempnam($tempDirPath, "zipexport-");
     $fh = fopen($file, 'w+');
     fwrite($fh, $html);
     fclose($fh);
     // Okay, write the title page
     $titlepagefile = tempnam($tempDirPath, "zipexport-");
     $fh = fopen($titlepagefile, 'w+');
     fwrite($fh, $coverPageHTML);
     fclose($fh);
     // Disable output of our standard mediawiki output.  We will be outputting a zip file instead.
     $wgOut->disable();
     // Create ZIP Archive which contains a cover and manual html
     $zip = new ZipArchive();
     $tempZipFilePath = tempnam($tempDirPath, "zipexport-");
     $zipFileName = $productName . '-' . $versionText . '-' . $pManual->getShortName() . '.zip';
     $zip->open($tempZipFilePath, ZipArchive::OVERWRITE);
     $zip->addFile($titlepagefile, 'cover.html');
     $zip->addFile($file, 'manual.html');
     // Iterate through all the images
     foreach ($imgData as $img) {
         $zip->addFile($img['local_path'], $img['new_path']);
     }
     $zip->close();
     header("Content-Type: application/zip");
     header("Content-Length: " . filesize($tempZipFilePath));
     header("Content-Disposition: attachment; filename=\"" . $zipFileName . "\"");
     readfile($tempZipFilePath);
     // Now remove all temp files
     self::rrmdir($tempDirPath);
     // Okay, let's add an entry to the error log to dictate someone requested a pdf
     error_log("INFO [" . __METHOD__ . "] " . php_uname('n') . ": zip export serve username=\"" . $wgUser->getName() . "\" version=\"{$versionText}\" " . " manual=\"" . $pManual->getShortName() . "\"");
     // No more processing
     return false;
 }
Beispiel #5
0
 /**
  * Parse the content of the TOC management page. 
  * 
  * It should be loaded and stored and this sort of breaks the design
  * in the way that it returns the template ready array of data which PonyDocsWiki is really supposed to be returning,
  * but I do not see the point or use of an intermediate format other than to bloat the code. 
  * 
  * It returns an array of arrays, which can be stored as a single array or separated using the list() = loadContnet() syntax.
  *
  * toc: This is the actual TOC as a list of arrays,
  *	    each array having a set of keys available to specify the TOC level, text, href, etc.
  * prev: Assoc array containing the 'previous' link data (text, href), or empty if there isn't one.
  * next: Assoc array containing the 'next' link data or empty if there isn't one.
  * start: Assoc array containing the data for the FIRST topic in the TOC.
  *
  * These can be captured in a variable when calling and individually accessed or captured using the list() construct
  * i.e.: list( $toc, $prev, $next, $start ) = $toc->loadContent().
  *
  * @FIXME: Store results internally and then have a $reload flag as param.
  * $content = $toc-
  * 
  * @return array
  */
 public function loadContent()
 {
     global $wgArticlePath;
     global $wgTitle;
     global $wgScriptPath;
     global $wgPonyDocs;
     global $title;
     /**
      * From this we have the page ID of the TOC page to use -- fetch it then  parse it so we can produce an output TOC array.
      * 
      * This array will contain one array per item with the following keys:
      * - 'level': 0= Arbitary Section Name, 1= Actual topic link.
      * - 'link': Link (wiki path) to item; may be unset for section headers (or set to first section H1)?
      * - 'text': Text to show in sidebar TOC.
      * - 'current': 1 if this is the currently selected topic, 0 otherwise.
      * 
      * We also have to store the index of the current section in our loop. 
      * 
      * The reason for this is so that we can remove any sections which have no defined/valid topics listed. 
      * 
      * This will also assist in our prev/next links which are stored in special indices.
      */
     // Our title is our url.
     // We should check to see if latest is our version.
     // If so, we want to FORCE the URL to include /latest/ as the version instead of the version that the user is currently in
     $tempParts = explode("/", $title);
     $latest = FALSE;
     if (isset($tempParts[1]) && !strcmp($tempParts[1], "latest")) {
         $latest = TRUE;
     }
     $selectedProduct = $this->pProduct->getShortName();
     $selectedVersion = $this->pInitialVersion->getVersionName();
     $selectedManual = $this->pManual->getShortName();
     // Okay, let's determine if the VERSION that the user is in is latest, if so, we should set latest to true.
     if (PonyDocsProductVersion::GetLatestReleasedVersion($selectedProduct) != NULL) {
         if ($selectedVersion == PonyDocsProductVersion::GetLatestReleasedVersion($selectedProduct)->getVersionName()) {
             $latest = TRUE;
         }
     }
     $cache = PonyDocsCache::getInstance();
     $key = "TOCCACHE-" . $selectedProduct . "-" . $selectedManual . "-" . $selectedVersion;
     $toc = $cache->get($key);
     // Cache did not exist, let's load our content is build up our cache entry.
     if ($toc === NULL && is_object($this->pTOCArticle) && is_a($this->pTOCArticle, 'Article')) {
         // The current index of the element in $toc we will work on
         $idx = 0;
         $section = -1;
         $content = $this->pTOCArticle->getContent();
         $lines = explode("\n", $content);
         foreach ($lines as $line) {
             /**
              * Indicates an arbitrary section header if it does not begin with a bullet point.
              * This is level 0 in our TOC and is not a link of any type (?).
              */
             if (!isset($line[0]) || $line[0] != '*') {
                 /**
                  * See if we are CLOSING a section (i.e. $section != -1). If so, check 'subs' and ensure its >0, 
                  * otherwise we need to remove the section from the list.
                  */
                 if ($section != -1 && !$toc[$section]['subs']) {
                     unset($toc[$section]);
                 }
                 if (isset($line[0]) && ctype_alnum($line[0])) {
                     $toc[$idx] = array('level' => 0, 'subs' => 0, 'link' => '', 'text' => $line, 'current' => FALSE);
                     $section = $idx;
                 }
                 /**
                  * This is a bullet point and thus an actual topic which can be linked to in MediaWiki. 
                  * {{#topic:H1 Of Topic Page}}
                  */
             } else {
                 if (-1 == $section) {
                     continue;
                 }
                 $topicRegex = '/' . PonyDocsTopic::getTopicRegex() . '/i';
                 if (!preg_match($topicRegex, $line, $matches)) {
                     continue;
                 }
                 $baseTopic = $matches[1];
                 $title_suffix = preg_replace('/([^' . str_replace(' ', '', Title::legalChars()) . '])/', '', $baseTopic);
                 $title = PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ":{$selectedProduct}:{$selectedManual}:{$title_suffix}";
                 $newTitle = PonyDocsTopic::GetTopicNameFromBaseAndVersion($title, $selectedProduct);
                 /**
                  * Hide topics which have no content (i.e. have not been created yet) from the user viewing. 
                  * 
                  * Authors must go to the TOC page in order to view and edit these.
                  * 
                  * The only way to do this (the cleanest/quickest) is to create a Title object then see if its article ID is 0
                  * 
                  * @tbd: Fix so that the section name is hidden if no topics are visible?
                  */
                 $t = Title::newFromText($newTitle);
                 if (!$t || !$t->getArticleID()) {
                     continue;
                 }
                 /**
                  * Obtain H1 content from the article -- WE NEED TO CACHE THIS!
                  */
                 $h1 = PonyDocsTopic::FindH1ForTitle($newTitle);
                 if ($h1 === FALSE) {
                     $h1 = $newTitle;
                 }
                 $href = str_replace('$1', PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . "/{$selectedProduct}/{$selectedVersion}/{$selectedManual}/{$title_suffix}", $wgArticlePath);
                 $toc[$idx] = array('level' => 1, 'page_id' => $t->getArticleID(), 'link' => $href, 'toctitle' => $baseTopic, 'text' => $h1, 'section' => $toc[$section]['text'], 'title' => $newTitle, 'class' => 'toclevel-1');
                 $toc[$section]['subs']++;
             }
             $idx++;
         }
         if (!$toc[$section]['subs']) {
             unset($toc[$section]);
         }
         // Okay, let's store in our cache.
         $cache->put($key, $toc, TOC_CACHE_TTL, TOC_CACHE_TTL / 4);
     }
     if ($toc) {
         $currentIndex = -1;
         $start = array();
         // Go through and determine start, prev, next and current elements.
         foreach ($toc as $idx => &$entry) {
             // Not using $entry. Only interested in $idx.
             // This allows us to process tocs with removed key indexes.
             if ($toc[$idx]['level'] == 1) {
                 if (empty($start)) {
                     $start = $toc[$idx];
                 }
                 // Determine current
                 $toc[$idx]['current'] = strcmp($wgTitle->getPrefixedText(), $toc[$idx]['title']) ? FALSE : TRUE;
                 if ($toc[$idx]['current']) {
                     $currentIndex = $idx;
                 }
                 // Now rewrite link with latest, if we are in latest
                 if ($latest) {
                     $safeVersion = preg_quote($selectedVersion, '#');
                     // Lets be specific and replace the version and not some other part of the URI that might match...
                     $toc[$idx]['link'] = preg_replace('#^/' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . '/([' . PONYDOCS_PRODUCT_LEGALCHARS . ']+)/' . "{$safeVersion}#", '/' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . '/$1/latest', $toc[$idx]['link'], 1);
                 }
             }
         }
         /**
          * Figure out previous and next links.
          * 
          * Previous should point to previous topic regardless of section, so our best bet is to skip any 'level=0'. 
          * 
          * Next works the same way.
          */
         $prev = $next = $idx = -1;
         if ($currentIndex >= 0) {
             $idx = $currentIndex;
             while ($idx >= 0) {
                 --$idx;
                 if (isset($toc[$idx]) && $toc[$idx]['level'] == 1) {
                     $prev = $idx;
                     break;
                 }
             }
             $idx = $currentIndex;
             // Array is sparse, so sizeof() truncates the end. Use max key instead.
             while ($idx <= max(array_keys($toc))) {
                 ++$idx;
                 if (isset($toc[$idx]) && $toc[$idx]['level'] == 1) {
                     $next = $idx;
                     break;
                 }
             }
             if ($prev != -1) {
                 $prev = array('link' => $toc[$prev]['link'], 'text' => $toc[$prev]['text']);
             }
             if ($next != -1) {
                 $next = array('link' => $toc[$next]['link'], 'text' => $toc[$next]['text']);
             }
         }
         /**
          * You should typically capture this by doing:
          * list( $toc, $prev, $next, $start ) = $ponydocstoc->loadContent();
          *
          * @FIXME: Previous and next links change based on the page you are on, so we cannot CACHE those!
          *
          * $obj = new stdClass();
          * $obj->toc = $toc;
          * $obj->prev = $prev;
          * $obj->next = $next;
          * $obj->start = $start;
          * $cache->addKey($tocKey, $obj);
          */
         // Last but not least, get the manual description if there is one.
         if (is_object($this->pTOCArticle) && preg_match('/{{#manualDescription:([^}]*)}}/', $this->pTOCArticle->getContent(), $matches)) {
             $this->mManualDescription = $matches[1];
         }
         // $this->pTOCArticle is empty, we're probably creating a new TOC
     } else {
         $toc = array();
         $prev = array();
         $next = array();
         $start = array();
     }
     return array($toc, $prev, $next, $start);
 }
    /**
     * This is called upon loading the special page.  It should write output to 
     * the page with $wgOut
     */
    public function execute()
    {
        global $wgOut, $wgArticlePath, $wgScriptPath, $wgUser;
        global $wgRequest;
        global $wgDBprefix;
        $currentProduct = '';
        $currentVersion = '';
        $collapseAll = FALSE;
        $dbr = wfGetDB(DB_SLAVE);
        // Set headers and title of the page, get value of "t" from GET/POST
        $this->setHeaders();
        $title = $wgRequest->getVal('t');
        if (empty($title)) {
            $wgOut->setPagetitle("Documentation Linkage");
            $wgOut->addHTML('No topic specified.');
            return;
        }
        $wgOut->setPagetitle("Documentation Linkage For " . $title);
        // Parse "t" (the title we're looking for inbound links to)
        // Find titles for all inherited versions, etc.
        $titlePieces = explode(':', $title);
        $plainTitle = $title;
        // Create a new Title from text, such as what one would find in a link. Decodes any HTML entities in the text.
        $title = Title::newFromText($title);
        $toUrls = array();
        // Do PonyDocs-specific stuff (loop through all inherited versions)
        if ($titlePieces[0] == PONYDOCS_DOCUMENTATION_NAMESPACE_NAME) {
            // Get all the versions in the product
            $versions = PonyDocsProductVersion::LoadVersionsForProduct($titlePieces[1], true);
            if (empty($versions)) {
                error_log('WARNING [PonyDocs] [' . __CLASS__ . '] Unable to find product versions for this topic: ' . $title);
            }
            $currentProduct = $titlePieces[1];
            $currentVersion = $titlePieces[4];
            // Get the latest released version of this product
            $latestVersionObj = PonyDocsProductVersion::GetLatestReleasedVersion($titlePieces[1]);
            if (is_object($latestVersionObj)) {
                $latestVersion = $latestVersionObj->getVersionName();
            } else {
                error_log('WARNING [PonyDocs] [' . __CLASS__ . '] Unable to find latest released version of ' . $titlePieces[1]);
            }
            // Generate a title without the version so we can dynamically generate a list of titles with all inherited versions
            $titleNoVersion = $titlePieces[0] . ":" . $titlePieces[1] . ":" . $titlePieces[2] . ":" . $titlePieces[3];
            // Search the database for matching to_links for each inherited version
            if (is_array($versions)) {
                foreach ($versions as $ver) {
                    // Add this URL to array of URLs to search db for
                    $toUrls[] = PonyDocsExtension::translateTopicTitleForDocLinks($titleNoVersion, NULL, $ver);
                    // Compare this version with latest version. If they're the same, add the URL with "latest" too.
                    $thisVersion = $ver->getVersionName();
                    if ($thisVersion == $latestVersion) {
                        $titleLatestVersion = $titlePieces[0] . ':' . $titlePieces[1] . ':' . $titlePieces[2] . ':' . $titlePieces[3] . ':latest';
                        $toUrls[] = PonyDocsExtension::translateTopicTitleForDocLinks($titleLatestVersion);
                    }
                }
            } else {
                error_log('WARNING [PonyDocs] [' . __CLASS__ . '] Unable to find versions for ' . $title);
            }
        } else {
            // Do generic mediawiki stuff for non-PonyDocs namespaces
            $collapseAll = TRUE;
            $toUrls[] = PonyDocsExtension::translateTopicTitleForDocLinks($title);
        }
        // Query the database for the list of toUrls we've collated
        if (!empty($toUrls)) {
            foreach ($toUrls as &$toUrl) {
                $toUrl = $dbr->strencode($toUrl);
            }
            $inUrls = "'" . implode("','", $toUrls) . "'";
            $query = "SELECT * FROM " . $wgDBprefix . "ponydocs_doclinks WHERE to_link IN ({$inUrls})";
            $results = $dbr->query($query);
        }
        // Create array of links, sorted by product and version
        $links = array();
        // Loop through results and save into handy dandy links array
        if (!empty($results)) {
            foreach ($results as $result) {
                $fromProduct = '';
                $fromVersion = '';
                $displayUrl = '';
                if (strpos($result->from_link, PONYDOCS_DOCUMENTATION_NAMESPACE_NAME) !== false) {
                    // If this is a PonyDocs style links, with slashes,
                    // save product, version, display URL accordingly.
                    $pieces = explode('/', $result->from_link);
                    $fromProduct = $pieces[1];
                    $fromVersion = $pieces[2];
                    $displayUrl = $result->from_link;
                } else {
                    // If this is a generic mediawiki style link, with colons (or not),
                    // set product to the namespace, and remove namespace
                    // from the display URL. Leave version blank.
                    if (strpos($result->from_link, ':') !== false) {
                        $pieces = explode(':', $result->from_link);
                        $fromProduct = $pieces[0];
                        // The "product" will be the namespace
                        $displayUrl = $pieces[1];
                        // So the namespace doesn't show in every URL
                    } else {
                        // it's possible to have a link with no colons
                        $fromProduct = 'Other';
                        // No namespace, so the "product" will be the string "Other"
                        $displayUrl = $result->from_link;
                    }
                    $fromVersion = 'None';
                    // No concept of versions outside of PonyDocs
                }
                // Put all this stuff in an array that we can use to generate HTML
                $links[$fromProduct][$fromVersion][] = array('from_link' => $result->from_link, 'to_link' => $result->to_link, 'display_url' => $displayUrl);
            }
        }
        // Make HTML go!
        ob_start();
        ?>

		<div class="doclinks">
			<h2>Inbound links to <?php 
        echo $plainTitle;
        ?>
 from other topics.</h2>

			<?php 
        // If there are no links, display a message saying as much
        if (empty($links)) {
            ?>
				<p>No links to <?php 
            echo $plainTitle;
            ?>
 (and its inherited versions) from other topics.</p>
			<?php 
        } else {
            // Display all links, ordered by product then version
            foreach ($links as $fromProduct => $fromVersions) {
                // If this is a PonyDocs Product
                if (PonyDocsProduct::IsProduct($fromProduct)) {
                    // Get versions for this product, so we can display the versions in the correct order
                    PonyDocsProductVersion::LoadVersionsForProduct($fromProduct, true);
                    $fromProductVersions = PonyDocsProductVersion::GetVersions($fromProduct);
                    // If there are no valid versions for this product/user, then skip the product name header.
                    if (!count($fromProductVersions)) {
                        continue;
                    }
                    ?>
						<h2><?php 
                    echo $fromProduct;
                    ?>
</h2>
						<?php 
                    foreach ($fromProductVersions as $fromProductVersionObj) {
                        $fromProductVersionName = $fromProductVersionObj->getVersionName();
                        // If there are doclinks from this version, print them
                        if (array_key_exists($fromProductVersionName, $fromVersions)) {
                            // Expand containers of incoming links from the current Product and Version
                            // Expand containers of incoming links from other Products
                            // But don't expand any containers if this is not a PonyDocs product
                            $selected = '';
                            if (($currentProduct != $fromProduct || $currentVersion == $fromProductVersionName) && !$collapseAll) {
                                $selected = 'selected';
                            }
                            ?>
								<h3 class="doclinks-collapsible <?php 
                            print $selected;
                            ?>
">
									<?php 
                            echo $fromProduct . ' ' . $fromProductVersionName;
                            ?>
								</h3>
								<ul>
								<?php 
                            foreach ($fromVersions[$fromProductVersionName] as $linkAry) {
                                ?>
									<li>
										<a href="<?php 
                                echo str_replace('$1', $linkAry['from_link'], $wgArticlePath);
                                ?>
">
											<?php 
                                echo $linkAry['display_url'];
                                ?>
										</a>
									</li>
								<?php 
                            }
                            ?>
								</ul>
								<?php 
                        }
                    }
                } else {
                    ?>
						<h2><?php 
                    echo $fromProduct;
                    ?>
 </h2>
						<h3 class="doclinks-collapsible selected">Latest</h3>
						<?php 
                    // This is not a PonyDocs product, don't worry about sorting
                    foreach ($fromVersions as $fromVersion => $fromVersionData) {
                        ?>
							<ul>
							<?php 
                        foreach ($fromVersionData as $linkAry) {
                            ?>
								<li>
									<a href="<?php 
                            echo str_replace('$1', $linkAry['from_link'], $wgArticlePath);
                            ?>
">
										<?php 
                            echo $linkAry['display_url'];
                            ?>
									</a>
								</li>
							<?php 
                        }
                        ?>
							</ul>
							<?php 
                    }
                }
            }
        }
        ?>
		</div>

		<?php 
        $htmlContent = ob_get_contents();
        ob_end_clean();
        $wgOut->addHTML($htmlContent);
        return true;
    }
 /**
  * Branches a topic from a source title to a target title.
  *
  * @param $topicTitle string The name of the internal topic.
  * @param $version PonyDocsVersion The target Version
  * @param $tocSection The TOC section this title resides in.
  * @param $tocTitle The toc title that references this topic.
  * @param $deleteExisting boolean Should we purge any existing conflicts?
  * @param $split Should we create a new page?
  * @returns boolean
  */
 static function branchTopic($topicTitle, $version, $tocSection, $tocTitle, $deleteExisting, $split)
 {
     // Clear any hooks so no weirdness gets called after we create the
     // branch
     $wgHooks['ArticleSave'] = array();
     if (!preg_match('/^' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':([^:]*):([^:]*):(.*):([^:]*)$/', $topicTitle, $match)) {
         throw new Exception("Invalid Title to Branch From");
     }
     $productName = $match[1];
     $manualName = $match[2];
     $title = $match[3];
     // Get the PonyDocsProduct and PonyDocsProductManual
     $product = PonyDocsProduct::GetProductByShortName($productName);
     $manual = PonyDocsProductManual::GetManualByShortName($productName, $manualName);
     // Get conflicts.
     $conflicts = self::getConflicts($product, $topicTitle, $version);
     if (!empty($conflicts)) {
         if ($deleteExisting && !$split) {
             // We want to purge each conflicting title completely.
             foreach ($conflicts as $conflict) {
                 $article = new Article(Title::newFromText($conflict));
                 if (!$article->exists()) {
                     // Article doesn't exist. Should never occur, but if it doesn't, no big deal since it was a conflict.
                     continue;
                 }
                 if ($conflict == $topicTitle) {
                     // Then the conflict is same as source material, do nothing.
                     continue;
                 } else {
                     // Do actual delete.
                     $article->doDelete("Requested purge of conficting article when branching topic " . $topicTitle . " with version: " . $version->getVersionName(), false);
                 }
             }
         } elseif (!$split) {
             // Ruh oh, there's conflicts and we didn't want to purge or split. Cancel out.
             throw new Exception("When calling branchTitle, there were conflicts and purge was not requested and we're not splitting.");
         }
     }
     // Load existing article to branch from
     $existingArticle = PonyDocsArticleFactory::getArticleByTitle($topicTitle);
     if (!$existingArticle->exists()) {
         // No such title exists in the system
         throw new Exception("Invalid Title to Branch From. Target Article does not exist:" . $topicTitle);
     }
     $title = PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':' . $product->getShortName() . ':' . $manual->getShortName() . ':' . $title . ':' . $version->getVersionName();
     $newArticle = PonyDocsArticleFactory::getArticleByTitle($title);
     if ($newArticle->exists()) {
         throw new Exception("Article already exists:" . $title);
     }
     // Copy content
     $existingContent = $existingArticle->getContent();
     $newContent = $existingContent;
     // Build the versions which will go into the new array.
     $newVersions = array();
     // Text representation of the versions
     $newVersions[] = $version->getVersionName();
     if ($split) {
         // We need to get all versions from PonyDocsVersion
         $rawVersions = PonyDocsProductVersion::GetVersions($productName);
         $existingVersions = array();
         foreach ($rawVersions as $rawVersion) {
             $existingVersions[] = $rawVersion->getVersionName();
         }
         // $existingVersions is now an array of version names in incremental order
         $versionIndex = array_search($version->getVersionName(), $existingVersions);
         // versionIndex is the index where our target version is
         // we will use this to determine what versions need to be brought over.
         preg_match_all("/\\[\\[Category:V:([^\\]]*):([^\\]]*)\\]\\]/", $existingContent, $matches);
         foreach ($matches[2] as $match) {
             $index = array_search($match, $existingVersions);
             if ($index > $versionIndex) {
                 $newVersions[] = $match;
             }
         }
     }
     // $newVersions contains all the versions that need to be pulled from the existing Content and put into the new content.
     // So let's now remove it form the original content
     foreach ($newVersions as $tempVersion) {
         $existingContent = preg_replace("/\\[\\[Category:V:" . $productName . ":" . $tempVersion . "\\]\\]/", "", $existingContent);
     }
     // Now let's do the edit on the original content.
     // Set version and manual
     $existingArticle->doEdit($existingContent, "Removed versions from existing article when branching Topic " . $topicTitle, EDIT_UPDATE);
     // Clear categories tags from new article content
     $newContent = preg_replace("/\\[\\[Category:V:([^\\]]*)]]/", "", $newContent);
     // add new category tags to new content
     foreach ($newVersions as $version) {
         $newContent .= "[[Category:V:" . $productName . ":" . $version . "]]";
     }
     $newContent .= "\n";
     // doEdit on new article
     $newArticle->doEdit($newContent, "Created new topic from branched topic " . $topicTitle, EDIT_NEW);
     return $title;
 }
Beispiel #8
0
/**
 * 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;
    }
Beispiel #10
0
 /**
  * Create a URL path (e.g. Documentation/Foo/latest/Bar/Bas) for a Topic
  * 
  * @param string $productName
  * @param string $manualName
  * @param string $topicName
  * @param string $versionName - Optional. We'll get the selected version (which defaults to 'latest') if empty
  * 
  * @return string
  * 
  * TODO: We should really be passing a topic object into this and not a string
  */
 public static function getTopicURLPath($productName, $manualName, $topicName, $versionName = NULL)
 {
     global $wgArticlePath;
     if (!isset($versionName)) {
         $versionName = PonyDocsProductVersion::GetSelectedVersion($productName);
     }
     $latestVersion = PonyDocsProductVersion::GetLatestReleasedVersion($productName);
     if ($latestVersion) {
         if ($versionName == $latestVersion->getVersionName()) {
             $versionName = 'latest';
         }
     }
     $base = str_replace('$1', PONYDOCS_DOCUMENTATION_NAMESPACE_NAME, $wgArticlePath);
     return "{$base}/{$productName}/{$versionName}/{$manualName}/{$topicName}";
 }
Beispiel #11
0
 /**
  * This returns the list of available manuals (active ones) in a more useful way for templates.  It is an associative array
  * where the key is the short name of the manual and the value is the display/long name.
  *
  * @return array
  */
 public function getManualsForProduct($product)
 {
     PonyDocsProductVersion::LoadVersionsForProduct($product);
     // Dependency
     PonyDocsProductVersion::getSelectedVersion($product);
     $manuals = PonyDocsProductManual::LoadManualsForProduct($product);
     // Dependency
     $out = array();
     foreach ($manuals as $m) {
         $out[$m->getShortName()] = $m->getLongName();
     }
     return $out;
 }
 /**
  * Clear NAVDATA cache by product and version
  * @param string $product 
  * @param string $version
  */
 private function clearProductCache($productName, $versionName)
 {
     //verify product has the version
     $versionObj = PonyDocsProductVersion::GetVersionByName($productName, $versionName);
     if ($versionObj != FALSE) {
         PonyDocsProductVersion::clearNAVCache($versionObj);
     }
 }
Beispiel #13
0
/**
 * 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;
}
Beispiel #14
0
 public function prepareDocumentation()
 {
     global $wgOut, $wgParser, $wgScriptPath, $wgTitle, $wgUser;
     /**
      * We need a lot of stuff from our PonyDocs extension!
      */
     $ponydocs = PonyDocsWiki::getInstance($this->data['selectedProduct']);
     $this->data['manuals'] = $ponydocs->getManualsForProduct($this->data['selectedProduct']);
     /**
      * Adjust content actions as needed, such as add 'view all' link.
      */
     $this->contentActions();
     $this->navURLS();
     /**
      * Possible topic syntax we must handle:
      * 
      * Documentation:<topic> *Which may include a version tag at the end, we don't care about this.
      * Documentation:<productShortName>:<manualShortName>:<topic>:<version>
      * Documentation:<productShortName>:<manualShortName>
      */
     /**
      * Based on the name; i.e. 'Documentation:Product:Manual:Topic' we need to parse it out and store the manual name and
      * the topic name as parameters. We store manual in 'manualname' and topic in 'topicname'. Special handling
      * needs to be done for versions and TOC?
      *
      * 	0=NS (Documentation)
      *  1=Product (Short name)
      *  2=Manual (Short name)
      *  3=Topic
      *  4=Version
      */
     $pManual = null;
     $pieces = explode(':', $wgTitle->__toString());
     $helpClass = '';
     /**
      * This isn't a specific topic+version -- handle appropriately.
      */
     if (sizeof($pieces) < 4) {
         if (!strcmp(PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':' . $this->data['selectedProduct'] . PONYDOCS_PRODUCTVERSION_SUFFIX, $wgTitle->__toString())) {
             $this->data['titletext'] = 'Versions Management - ' . $this->data['selectedProduct'];
             $wgOut->addHTML('<br><span class="' . $helpClass . '"><i>* Use {{#version:name|status}} to define a new version,' . ' where status is released, unreleased, or preview.' . ' Valid chars in version name are A-Z, 0-9, period, comma, and dash.</i></span>');
             $wgOut->addHTML('<br><span class="' . $helpClass . '"><i>* Use {{#versiongroup:name|message}} to set a banner' . ' message that will appear on every topic in every version following the versiongroup.</i></span>');
         } elseif (!strcmp(PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':' . $this->data['selectedProduct'] . PONYDOCS_PRODUCTMANUAL_SUFFIX, $wgTitle->__toString())) {
             $this->data['titletext'] = 'Manuals Management - ' . $this->data['selectedProduct'];
             $wgOut->addHTML('<br><span class="' . $helpClass . '"><i>' . '* Use {{#manual:manualShortName|displayName|categories}} to define a new manual.');
             $wgOut->addHTML('<br><span class="' . $helpClass . '"><i>' . '* Prepend manual short name with ' . PONYDOCS_PRODUCT_STATIC_PREFIX . ' to define a static manual.' . '</i></span>');
             $wgOut->addHTML('<br><span class="' . $helpClass . '">' . '<i>* If you omit display name, the short name will be used in links.</i></span>');
             $wgOut->addHTML('<br><span class="' . $helpClass . '">' . '<i>* Categories is a comma-separated list of categories</i></span>');
         } elseif (!strcmp(PONYDOCS_DOCUMENTATION_PRODUCTS_TITLE, $wgTitle->__toString())) {
             $this->data['titletext'] = 'Products Management';
             $wgOut->addHTML('<br><span class="' . $helpClass . '"><i>' . '* Use {{#product:productShortName|displayName|description|parent|categories}} to define a new product.' . '</i></span>');
             $wgOut->addHTML('<br><span class="' . $helpClass . '"><i>' . '* Prepend product short name with ' . PONYDOCS_PRODUCT_STATIC_PREFIX . ' to define a static product.' . '</i></span>');
             $wgOut->addHTML('<br><span class="' . $helpClass . '"><i>' . '* displayName, description, parent, and categories can be left empty.</i></span>');
             $wgOut->addHTML('<br><span class="' . $helpClass . '">' . '<i>* If you leave displayName empty, productShortName will be used in links.</i></span>');
             $wgOut->addHTML('<br><span class="' . $helpClass . '">' . '<i>* Categories is a comma-separated list of categories.</i></span>');
             $wgOut->addHTML('<br><span class="' . $helpClass . '">' . '<i>* Each product here <b>MUST</b> also be listed in $ponyDocsProductsList,' . ' usually configured in LocalSettings.php.</i></span>');
         } elseif (preg_match('/(.*)TOC(.*)/', $pieces[2], $matches)) {
             $this->data['titletext'] = $matches[1] . ' Table of Contents Page';
             $wgOut->addHTML('<br><span class="' . $helpClass . '"><i>' . '* Optionally start this page with {{#manualDescription:Manual Description.}}' . ' followed by two line-breaks to set a manual description for the Manual this TOC belongs to.' . '</i></span>');
             $wgOut->addHTML('<br><span class="' . $helpClass . '"><i>' . '* Topics are grouped into sections by section headers.' . ' Any line without markup is considered a section header.' . ' A section header is required before the the first topic tag.</i></span>');
             $wgOut->addHTML('<br><span class="' . $helpClass . '"><i>' . '* Topic tags must be part of an unordered list.' . ' Use {{#topic:Display Name}} after a * (list item markup) to create topics.</i></span>');
         } elseif (sizeof($pieces) >= 2 && PonyDocsProductManual::IsManual($pieces[1], $pieces[2])) {
             $pManual = PonyDocsProductManual::GetManualByShortName($pieces[1], $pieces[2]);
             if ($pManual) {
                 $this->data['manualname'] = $pManual->getLongName();
             } else {
                 $this->data['manualname'] = $pieces[2];
             }
             $this->data['topicname'] = $pieces[3];
             $this->data['titletext'] = $pieces[2];
         } else {
             $this->data['topicname'] = $pieces[2];
         }
     } else {
         $pManual = PonyDocsProductManual::GetManualByShortName($pieces[1], $pieces[2]);
         if ($pManual) {
             $this->data['manualname'] = $pManual->getLongName();
         } else {
             $this->data['manualname'] = $pieces[2];
         }
         $this->data['topicname'] = $pieces[3];
         $h1 = PonyDocsTopic::FindH1ForTitle($wgTitle->__toString());
         if ($h1 !== FALSE) {
             $this->data['titletext'] = $h1;
         }
     }
     /**
      * Get current topic, passing it our global Article object.
      * From this, generate our TOC based on the current topic selected.
      * This generates our left sidebar TOC plus our prev/next/start navigation links.
      * This should ONLY be done if we actually are WITHIN a manual, so special pages like TOC, etc. should not do this!
      */
     if ($pManual) {
         $p = PonyDocsProduct::GetProductByShortName($this->data['selectedProduct']);
         $v = PonyDocsProductVersion::GetVersionByName($this->data['selectedProduct'], $this->data['selectedVersion']);
         $toc = new PonyDocsTOC($pManual, $v, $p);
         list($this->data['manualtoc'], $this->data['tocprev'], $this->data['tocnext'], $this->data['tocstart']) = $toc->loadContent();
         $this->data['toctitle'] = $toc->getTOCPageTitle();
     }
     /**
      * Create a PonyDocsTopic from our article. From this we populate:
      *
      * topicversions:  List of version names topic is tagged with.
      * inlinetoc:  Inline TOC shown above article body.
      * catcode:  Special category code.
      * cattext:  Category description.
      * basetopicname:  Base topic name (w/o :<version> at end).
      * basetopiclink:  Link to special TopicList page to view all same topics.
      */
     $context = $this->skin->getContext();
     $article = Article::newFromTitle($context->getTitle(), $context);
     $topic = new PonyDocsTopic($article);
     if (preg_match('/^' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':(.*):(.*):(.*):(.*)/', $wgTitle->__toString()) || preg_match('/^' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':.*:.*TOC.*/', $wgTitle->__toString())) {
         $this->data['topicversions'] = PonyDocsWiki::getVersionsForTopic($topic);
         $this->data['inlinetoc'] = $topic->getSubContents();
         $this->data['versionclasses'] = $topic->getVersionClasses();
         $this->data['versionGroupMessage'] = $this->data['pVersion']->getVersionGroupMessage();
         /**
          * Sort of a hack -- we only use this right now when loading a TOC page which is new/does not exist.
          * When this happens a hook (AlternateEdit) adds an inline script to define this JS function,
          * which populates the edit box with the proper Category tag based on the currently selected version.
          */
         $this->data['body_onload'] = 'ponyDocsOnLoad();';
         switch ($this->data['catcode']) {
             case 0:
                 $this->data['cattext'] = 'Applies to latest version which is currently unreleased.';
                 break;
             case 1:
                 $this->data['cattext'] = 'Applies to latest version.';
                 break;
             case 2:
                 $this->data['cattext'] = 'Applies to released version(s) but not the latest.';
                 break;
             case 3:
                 $this->data['cattext'] = 'Applies to latest preview version.';
                 break;
             case 4:
                 $this->data['cattext'] = 'Applies to one or more preview version(s) only.';
                 break;
             case 5:
                 $this->data['cattext'] = 'Applies to one or more unreleased version(s) only.';
                 break;
             case -2:
                 /** Means its not a a title name which should be checked. */
                 break;
             default:
                 $this->data['cattext'] = 'Does not apply to any version of PonyDocs.';
                 break;
         }
     }
     $this->data['basetopicname'] = $topic->getBaseTopicName();
     if (strlen($this->data['basetopicname'])) {
         $this->data['basetopiclink'] = '<a href="' . $wgScriptPath . '/index.php?title=Special:TopicList&topic=' . $this->data['basetopicname'] . '">View All</a>';
     }
     $temp = PonyDocsTopic::FindH1ForTitle(PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':' . $topic->getTitle()->getText());
     if ($temp !== false) {
         // We got an H1!
         $this->data['pagetitle'] = $temp;
     }
 }
Beispiel #15
0
 /**
  * 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;
    }