<?php

    /*
    Copyright 2006 Paul Westbrook (paul@westbrooks.org)
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    */

	lt_include( PLOG_CLASS_PATH."class/plugin/pluginbase.class.php" );

	class PluginRelated extends PluginBase
	{
		var $pluginEnabled;
		var $numRelatedArticles;
		var $minWordLength;
		var $refreshInterval;
        var $cacheFolder;
        var $extractKeywordsFromBody;
        var $bannedWords;
		
		function PluginRelated( $source = "" )
		{
			$this->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, 0755 );
            }

		}

	    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
	        
	            $tempList = $this->loadArticleIds($articleId);
	        } else {
                
                // Get the article specified by the article ID
                $article = $articles->getArticle($articleId);
                
                if ($article === false) {
                    return $relatedArticles;
                }
    
 
                // 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 );	
                    
                    // 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() ) {
                                if (isset($tempList[$foundArticle->getId() ])) 
                                    $tempList[$foundArticle->getId() ] += 1;
                                else 
                                    $tempList[$foundArticle->getId() ] = 1;
                            }
                        }
                    }
                }
                
                // Now sort by score, in reverse order
                arsort($tempList);
                
                // Cache this list of related articles
                $this->saveArticleIds( $articleId, $tempList);
                
    		}

            $numArticles = 0;
            // Now return the list of articles described by the entries in this array
            foreach( $tempList as $id=>$score) {
                if ($numArticles >= $this->numRelatedArticles) {
                    break;
                }
                
                $relatedArticle = $articles->getArticle( $id );
                if ($relatedArticle && ($relatedArticle->getStatus() == POST_STATUS_PUBLISHED)) {
                    array_push( $relatedArticles, $relatedArticle);
                    $numArticles += 1;
                }
            }

            return $relatedArticles;
	    }
	    
	    
	    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+\.|\?|,|(|)|\-+|\'|\"|!|=|;|&#0215;|\$|\/|:|{|}]\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 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" ),
			));
		}
		
	}
?>