PluginBase($source); $this->id = "related"; $this->desc = "The Related plugin will generate a list of related posts."; $this->author = "Paul Westbrook"; $this->locales = Array( "en_UK" ); $this->version = "20070602"; if( $source == "admin" ) $this->initAdmin(); } function initAdmin() { $this->registerAdminAction( "related", "PluginRelatedConfigAction" ); $this->registerAdminAction( "updateRelatedConfig", "PluginRelatedUpdateConfigAction" ); $menu =& Menu::getMenu(); if( !$menu->entryExists( "/menu/controlCenter/manageAppearancePlugins" )) $this->addMenuEntry( "/menu/controlCenter", "manageAppearancePlugins", "", "", true, false ); $this->addMenuEntry( "/menu/controlCenter/manageAppearancePlugins", "related", "?op=related", "" ); } function register() { $blogSettings = $this->blogInfo->getSettings(); $this->pluginEnabled = $blogSettings->getValue( "plugin_related_enabled" ); $this->numRelatedArticles = $blogSettings->getValue( "plugin_related_num_articles" ); $this->minWordLength = $blogSettings->getValue( "plugin_related_min_word_length" ); $this->refreshInterval = $blogSettings->getValue( "plugin_related_refresh_interval" ); $this->extractKeywordsFromBody = $blogSettings->getValue( "plugin_related_extract_keywords_from_body" ); $this->bannedWords = $blogSettings->getValue( "plugin_related_banned_keywords" ); $this->bannedWords = explode(",", strtolower($this->bannedWords)); if(!$this->isEnabled()) return; $config =& Config::getConfig(); $this->cacheFolder = $config->getValue('temp_folder'); $this->cacheFolder = $this->cacheFolder.'/related/'.$this->blogInfo->getId(); if( !File::exists( $this->cacheFolder )) { File::createDir( $this->cacheFolder, 0777 ); } } function isEnabled() { return $this->pluginEnabled; } function relatedArticles($articleId) { $relatedArticles = Array(); if (!$this->isEnabled()) { return $relatedArticles; } $tempList = Array(); lt_include( PLOG_CLASS_PATH."class/dao/articles.class.php" ); $articles = new Articles(); if ($this->relatedArticleIdsCached($articleId)) { // Look for the related articles in the cache $temp = $this->loadArticleIds($articleId); $articles = new Articles(); $i=0; foreach($temp as $key=>$tmp) { if($i < $this->numRelatedArticles) { $article = $articles->getArticle($key); $tempList[$key]['score'] = $tmp; $tempList[$key]['article'] = $article; } else { break; } $i++; } return $tempList; } else { // Get the article specified by the article ID $article = $articles->getArticle($articleId); if ($article === false) { return $relatedArticles; } $categories = $article->getCategories(); // Get the keywords $keywords = $this->getArticleKeywords($article); foreach($keywords as $word) { // Build the list of articles that have this keyword lt_include( PLOG_CLASS_PATH."class/dao/searchengine.class.php" ); lt_include( PLOG_CLASS_PATH."class/dao/articlestatus.class.php" ); $searchEngine = new SearchEngine(); $results = $searchEngine->search( $this->blogInfo->getId(), $word, POST_STATUS_PUBLISHED, true, -1, 25 ); // Now add the article results to the internal list of related articles foreach( $results as $result ) { if( $result->getType() == SEARCH_RESULT_ARTICLE ) { $foundArticle = $result->getArticle(); if ($foundArticle->getId() != $article->getId() ) { $foundCategories = $foundArticle->getCategories(); foreach($foundCategories as $foundCategory) { foreach($categories as $category) { if($foundCategory->getName() == $category->getName()) { if (isset($tempList[$foundArticle->getId() ])){ $tempList[$foundArticle->getId() ]['score'] += 1; $tempScore[$foundArticle->getId() ] += 1; } else{ $tempList[$foundArticle->getId() ]['score'] = 1; $tempScore[$foundArticle->getId() ] = 1; } } } } if (isset($tempList[$foundArticle->getId() ])){ $tempList[$foundArticle->getId() ]['score'] += 1; $tempScore[$foundArticle->getId() ] += 1; } else{ $tempList[$foundArticle->getId() ]['score'] = 1; $tempScore[$foundArticle->getId() ] = 1; } } if($foundArticle->getStatus() == POST_STATUS_PUBLISHED){ $tempList[$foundArticle->getId() ]['article'] = $foundArticle; } } } } // Now sort by score, in reverse order arsort($tempList); arsort($tempScore); // Cache this list of related articles $this->saveArticleIds( $articleId, $tempScore); } return $tempList; } function saveArticleIds($articleId, $relatedArticles) { // Create the serialized array $saveFile = $this->cacheFolder."/".$articleId; $unserializedData = Array( "timestamp" => time(), "related" => $relatedArticles ); $serializedArray = serialize( $unserializedData ); $fh = fopen( $saveFile, "w"); if ($fh) { fwrite($fh, $serializedArray); } fclose($fh); File::chMod($saveFile, 0644); } function loadArticleIds($articleId) { $saveFile = $this->cacheFolder."/".$articleId; $articles = Array(); if(File::isReadable($saveFile)){ $fh = fopen( $saveFile, "r"); if ($fh) { $serializedData = fread($fh, filesize($saveFile)); fclose($fh); $unserializedData = unserialize($serializedData); $articles = $unserializedData["related"]; } } return $articles; } // // Returns true if the list of related article Ids have been // cached function relatedArticleIdsCached($articleId) { $saveFile = $this->cacheFolder."/".$articleId; // If the file exists if( !File::exists($saveFile)) { return false; } // Make sure the file is readable if( !File::isReadable($saveFile)){ return false; } // If the setting states that we should never refresh the cache // we want to use the if ($this->refreshInterval == -1) { return true; } // If we are never supposed to use the cache, if ($this->refreshInterval == 0) { return false; } $fh = fopen( $saveFile, "r"); if ($fh) { $serializedData = fread($fh, filesize($saveFile)); fclose($fh); $unserializedData = unserialize($serializedData); $timestamp = $unserializedData["timestamp"]; // Make sure that this data is not older that what we are allowing // Get the difference in the times $timeDiff = time() - $timestamp; if ($timeDiff > $this->refreshInterval * 60 * 60 ) { return false; } } return true; } // Returns an array with the keywords for an article function getArticleKeywords( $article ) { // Get the title of the article // XXX NOTE: There probably is a better way to get a list of // keywords from a post. But, until then, just use the title. // (Assuming the author used a relevant title) $text = $article->getTopic(); if ($this->extractKeywordsFromBody) { // Get the body $body = strip_tags($article->getText()); $text = $text . " " . $body; } // Split keywords $words = preg_split('/\s*[\s+\.|\?|,|(|)|\-+|\'|\"|!|=|;|×|\$|\/|:|{|}]\s*/i', strtolower($text)); $keywords = array_unique( $words ); $filteredKeywords = Array(); foreach($keywords as $word) { // Make sure that it is not in the banned list $found = in_array($word,$this->bannedWords); if(($found === FALSE) && (strlen($word) >= $this->minWordLength)) { if (!isset($filteredKeywords[$word])) { $filteredKeywords[$word] = $word; } } } return $filteredKeywords; } function getConfiguration() { $temp['plugin_related_enabled'] = $this->pluginEnabled; $temp['plugin_related_num_articles'] = $this->numRelatedArticles; $temp['plugin_related_min_word_length'] = $this->minWordLength; $temp['plugin_related_refresh_interval'] = $this->refreshInterval; $temp['plugin_related_extract_keywords_from_body'] = $this->extractKeywordsFromBody; $temp['plugin_related_banned_keywords'] = $this->bannedWords; return $temp; } function getPluginConfigurationKeys() { return( Array( Array( "name" => "plugin_related_enabled", "type" => "boolean" ), Array( "name" => "plugin_related_num_articles", "type" => "integer" ), Array( "name" => "plugin_related_min_word_length", "type" => "integer" ), Array( "name" => "plugin_related_refresh_interval", "type" => "list", "options" => Array( "-1" => "-1", "0" => "0", "1" => "1", "24" => "24", "168" => "168", "720" => "720" )), Array( "name" => "plugin_related_extract_keywords_from_body", "type" => "boolean" ), Array( "name" => "plugin_related_banned_keywords", "validator" => new StringValidator(), "type" => "string", "allowEmpty" => true ) )); } } ?>