/** * This is used to scan a topic in the Documentation namespace when saved for wiki links, and when it finds them, it should * create the topic in the namespace (if it does not exist) then set the H1 to the alternate text (if supplied) and then * tag it for the versions of the currently being viewed page? We can assume Documentation namespace. * * [[SomeTopic|My Topic Here]] <- Creates Documentation:<currentProduct>:<currentManual>:SomeTopic:<selectedVersion> and sets H1. * [[Dev:HowToFoo|How To Foo]] <- Creates Dev:HowToFoo and sets H1. * [[Documentation:User:SomeTopic|Some Topic]] <- To create link to another manual, will use selected version. * [[Documentation:User:SomeTopic:1.0|Topic]] <- Specific title in another manual. * [[:Main_Page|Main Page]] <- Link to a page in the global namespace. * * Forms which can exist are as such: * [[TopicNameOnly]] Links to Documentation:<currentProduct>:<currentManual>:<topicName>:<selectedVersion> * [[Documentation:Manual:Topic]] Links to a different manual from a manual (uses selectedVersion and selectedProduct). * [[Documentation:Product:Manual:Topic]] Links to a different product and a different manual. * [[Documentation:Product:Manual:Topic:Version]] Links to a different product and a different manual. * [[Dev:SomeTopicName]] Links to another namespace and topic explicitly. * * When creating the link in Documentation namespace, it uses the CURRENT MANUAL being viewed.. and the selected version? * * @param Article $article * @param User $user * @param string $text * @param string $summary * @param boolean $minor * @param boolean $watch * @param $sectionanchor * @param integer $flags * * @deprecated Use PageContentSave hook instead */ public static function onArticleSave_AutoLinks(&$article, &$user, &$text, &$summary, $minor, $watch, $sectionanchor, &$flags) { global $wgRequest, $wgOut, $wgArticlePath, $wgRequest, $wgScriptPath; // Retrieve read/slave handler for fetching from DB. $dbr = wfGetDB(DB_SLAVE); // Dangerous. Only set the flag if you know that you should be skipping this processing. Currently used for branch/inherit. if (PonyDocsExtension::isSpeedProcessingEnabled()) { return true; } $title = $article->getTitle(); // We only perform this in Documentation namespace. if (!preg_match('/' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':/', $title->__toString())) { return true; } $missingTopics = array(); // If this is not a TOC and we don't want to create on article edit, then simply return. if (!preg_match('/^' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':(.*):(.*)TOC(.*)/i', $title) && !PONYDOCS_AUTOCREATE_ON_ARTICLE_EDIT) { return true; } if (preg_match_all("/\\[\\[([" . Title::legalChars() . "]*)([|]?([^\\]]*))\\]\\]/", $text, $matches, PREG_SET_ORDER)) { /** * $match[1] = Wiki Link * $match[3] = Alternate Text */ foreach ($matches as $match) { /** * Forms which can exist are as such: * [[TopicNameOnly]] Links to Documentation:<currentProduct>:<currentManual>:<topicName>:<selectedVersion> * [[Documentation:Manual:Topic]] Links to a different manual from a manual (uses selectedVersion and selectedProduct). * [[Documentation:Product:Manual:Topic]] Links to a different product and a different manual. * [[Documentation:Product:Manual:Topic:Version]] Links to a different product and a different manual. * [[Dev:SomeTopicName]] Links to another namespace and topic explicitly. * So we first need to detect the use of a namespace. */ if (strpos($match[1], ':') !== false) { $pieces = explode(':', $match[1]); if (!strcasecmp($pieces[0], PONYDOCS_DOCUMENTATION_NAMESPACE_NAME)) { /** * Handle [[Documentation:Manual:Topic]] referencing selected version -AND- * [[Documentation:User:HowToFoo]] as an explicit link to a page. * [[Documentation:Product:Manual:Topic|Some Alternate Text]] */ if (sizeof($pieces) == 3 || sizeof($pieces) == 4) { if (sizeof($pieces) == 3) { $product = PonyDocsProduct::GetSelectedProduct(); $manual = $pieces[1]; $topic = $pieces[2]; } else { $product = $pieces[1]; $manual = $pieces[2]; $topic = $pieces[3]; } // if link is to current product, get currect selected version, otherwise we have to guess // and get the latest released version of the linked product if ($product == PonyDocsProduct::GetSelectedProduct()) { $version = PonyDocsProductVersion::GetSelectedVersion($product); } else { if (PonyDocsProduct::IsProduct($product)) { // Need to load the product versions if this topic is for a different product PonyDocsProductVersion::LoadVersionsForProduct($product); $pVersion = PonyDocsProductVersion::GetLatestReleasedVersion($product); // If there is no available latest released version go to the next match if (!$pVersion) { continue; } $version = $pVersion->getVersionName(); } } /** * Does this topic exist? Look for a topic with this name tagged for the current version and current product. * If nothing is found, we create a new article. */ $sqlMatch = $product . ':' . $manual . ':' . $topic; $res = $dbr->select('categorylinks', 'cl_from', array("cl_to = 'V:" . $dbr->strencode("{$product}:{$version}") . "'", 'cl_type = "page"', "cl_sortkey LIKE '" . $dbr->strencode(strtoupper($sqlMatch)) . ":%'"), __METHOD__); if (!$res->numRows()) { $topicTitle = PONYDOCS_DOCUMENTATION_PREFIX . $sqlMatch . ':' . $version; $tempArticle = new Article(Title::newFromText($topicTitle)); if (!$tempArticle->exists()) { /** * Create the new article in the system; if we have alternate text then set our H1 to this. * Tag it with the currently selected version only. */ $content = ''; if (strlen($match[3])) { $content = '= ' . $match[3] . " =\n"; } else { $content = '= ' . $topicTitle . " =\n"; } $content .= "\n[[Category:V:{$product}:{$version}]]"; $tempArticle->doEdit($content, "Auto-creation of topic {$topicTitle} via reference from " . $title->__toString() . '.', EDIT_NEW); if (PONYDOCS_DEBUG) { error_log("DEBUG [" . __METHOD__ . ":" . __LINE__ . "]" . " Auto-created {$topicTitle} using link {$match[1]} in " . $title->__toString()); } } } /** * Explicit link of the form: * [[Documentation:Product:Manual:Topic:Version|Some Alternate Text]] */ } else { if (sizeof($pieces) == 5) { $product = $pieces[1]; $version = PonyDocsProductVersion::GetSelectedVersion($product); $version = $pieces[4]; $topicTitle = $match[1]; $tempArticle = new Article(Title::newFromText($topicTitle)); if (!$tempArticle->exists()) { /** * Create the new article in the system; if we have alternate text then set our H1 to this. */ $content = ''; if (strlen($match[3])) { $content = '= ' . $match[3] . " =\n"; } else { $content = '= ' . $topicTitle . " =\n"; } $content .= "\n[[Category:V:" . $product . ':' . $version . "]]"; $tempArticle->doEdit($content, 'Auto-creation of topic ' . $topicTitle . ' via reference from ' . $title->__toString() . '.', EDIT_NEW); if (PONYDOCS_DEBUG) { error_log("DEBUG [" . __METHOD__ . ":" . __LINE__ . "] Auto-created {$topicTitle} using link " . $match[1] . " in " . $title->__toString()); } } } } /** * Handle non-Documentation NS references, such as 'Dev:SomeTopic'. This is much simpler -- if it doesn't exist, * create it and add the H1. Nothing else. */ } else { $topicTitle = $match[1]; $tempTitleForArticle = Title::newFromText($topicTitle); if (is_object($tempTitleForArticle)) { $tempArticle = new Article($tempTitleForArticle); if (!$tempArticle->exists()) { /** * Create the new article in the system; if we have alternate text then set our H1 to this. */ $content = ''; if (strlen($match[3])) { $content = '= ' . $match[3] . " =\n"; } else { $content = '= ' . $match[1] . " =\n"; } $tempArticle->doEdit($content, 'Auto-creation of topic ' . $topicTitle . ' via reference from ' . $title->__toString() . '.', EDIT_NEW); if (PONYDOCS_DEBUG) { error_log("DEBUG [" . __METHOD__ . ":" . __LINE__ . "] Auto-created " . $topicTitle . " using link " . $match[1] . " in " . $title->__toString()); } } } } /** * Here we handle simple topic links: * [[SomeTopic|Some Display Title]] * Which assumes CURRENT manual in Documentation namespace. It finds the topic which must share a version tag * with the currently displayed title. */ } else { $product = PonyDocsProduct::GetSelectedProduct(); $pManual = PonyDocsProductManual::GetCurrentManual($product); $version = PonyDocsProductVersion::GetSelectedVersion($product); if (!$pManual) { // Cancel out. return true; } /** * Does this topic exist? Look for a topic with this name tagged for the current version. * If nothing is found, we create a new article. */ $sqlMatch = $product . ':' . $pManual->getShortName() . ':' . $match[1]; $res = $dbr->select('categorylinks', 'cl_from', array("cl_to = 'V:" . $dbr->strencode("{$product}:{$version}") . "'", 'cl_type = "page"', "cl_sortkey LIKE '" . $dbr->strencode(strtoupper($sqlMatch)) . ":%'"), __METHOD__); if (!$res->numRows()) { $topicTitle = PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ":{$sqlMatch}:{$version}"; $tempArticle = new Article(Title::newFromText($topicTitle)); if (!$tempArticle->exists()) { /** * Create the new article in the system; if we have alternate text then set our H1 to this. */ $content = ''; if (strlen($match[3])) { $content = '= ' . $match[3] . " =\n"; } else { $content = '= ' . $topicTitle . " =\n"; } $content .= "\n[[Category:V:" . $product . ':' . $version . "]]"; $tempArticle->doEdit($content, 'Auto-creation of topic ' . $topicTitle . ' via reference from ' . $title->__toString() . '.', EDIT_NEW); if (PONYDOCS_DEBUG) { error_log("DEBUG [" . __METHOD__ . ":" . __LINE__ . "] Auto-created {$topicTitle} using link " . $match[1] . " in " . $title->__toString()); } } } } } } return TRUE; }
/** * 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; }
/** * Called when an article is deleted, we want to purge any doclinks entries * that refer to that article if it's in the documentation namespace. * * NB $article is a WikiPage and not an article */ public static function onArticleDelete(&$article, &$user, &$user, $error) { $title = $article->getTitle(); $realArticle = Article::newFromWikiPage($article, RequestContext::getMain()); // Delete doc links PonyDocsExtension::updateOrDeleteDocLinks("delete", $realArticle); //Delete the PDF on deleting the topic -WEB-7042 if (strpos($title->getPrefixedText(), PONYDOCS_DOCUMENTATION_NAMESPACE_NAME) === 0 && strpos($title->getPrefixedText(), ':') !== FALSE) { PonyDocsExtension::clearArticleCategoryCache($realArticle); $productArr = explode(':', $title->getText()); $productName = $productArr[0]; if (PonyDocsProduct::IsProduct($productName) && count($productArr) == 4 && preg_match(PONYDOCS_PRODUCTMANUAL_REGEX, $productArr[1]) && preg_match(PONYDOCS_PRODUCTVERSION_REGEX, $productArr[3])) { $topic = new PonyDocsTopic($realArticle); $topicVersions = $topic->getProductVersions(); $manual = PonyDocsProductManual::GetCurrentManual($productName, $title); if ($manual != null) { foreach ($topicVersions as $key => $version) { PonyDocsPdfBook::removeCachedFile($productName, $manual->getShortName(), $version->getVersionName()); } } } } // Okay, article is in doc namespace return true; }
/** * Return the current product object based on the title object; returns null otherwise. * * @static * @return PonyDocsProduct */ public static function GetCurrentProduct($title = NULL) { global $wgTitle; $targetTitle = $title == NULL ? $wgTitle : $title; $pcs = explode(':', $targetTitle->__toString()); if (!PonyDocsProduct::IsProduct($pcs[1])) { return NULL; } return PonyDocsProduct::GetProductByShortName($pcs[1]); }