Edit file File name : AOD_Index.php Content :<?PHP /********************************************************************************* * SugarCRM Community Edition is a customer relationship management program developed by * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU Affero General Public License version 3 as published by the * Free Software Foundation with the addition of the following permission added * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. * * 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 Affero General Public License for more * details. * * You should have received a copy of the GNU Affero General Public License along with * this program; if not, see http://www.gnu.org/licenses or write to the Free * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. * * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road, * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com. * * The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under * Section 5 of the GNU Affero General Public License version 3. * * In accordance with Section 7(b) of the GNU Affero General Public License version 3, * these Appropriate Legal Notices must retain the display of the "Powered by * SugarCRM" logo. If the display of the logo is not reasonably feasible for * technical reasons, the Appropriate Legal Notices must display the words * "Powered by SugarCRM". ********************************************************************************/ /** * THIS CLASS IS FOR DEVELOPERS TO MAKE CUSTOMIZATIONS IN */ require_once('modules/AOD_Index/AOD_Index_sugar.php'); require_once('modules/AOD_Index/LuceneUtils.php'); requireLucene(); class AOD_Index extends AOD_Index_sugar { function __construct(){ parent::__construct(); Zend_Search_Lucene_Search_QueryParser::setDefaultEncoding('utf-8'); Zend_Search_Lucene_Analysis_Analyzer::setDefault(new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8Num_CaseInsensitive()); } /** * @deprecated deprecated since version 7.6, PHP4 Style Constructors are deprecated and will be remove in 7.8, please update your code, use __construct instead */ function AOD_Index(){ $deprecatedMessage = 'PHP4 Style Constructors are deprecated and will be remove in 7.8, please update your code'; if(isset($GLOBALS['log'])) { $GLOBALS['log']->deprecated($deprecatedMessage); } else { trigger_error($deprecatedMessage, E_USER_DEPRECATED); } self::__construct(); } function isEnabled(){ global $sugar_config; return !empty($sugar_config['aod']['enable_aod']); } function find($queryString){ $queryString = strtolower($queryString); $hits = $this->getLuceneIndex()->find($queryString); return $hits; } function optimise(){ if(!$this->isEnabled()){ return; } global $timedate; $this->getLuceneIndex()->optimize(); $this->last_optimised = $timedate->getNow()->asDb(); $this->save(); } public function getIndex(){ $index = BeanFactory::getBean('AOD_Index',1); if(!empty($index) && !empty($index->id)){ return $index; }else{ $index = new AOD_Index(); $index->id = 1; $index->new_with_id = true; $index->name = "Index"; $index->location = "modules/AOD_Index/Index/Index"; $index->save(); return $index; } } /** * @param $revision * @return bool|Zend_Search_Lucene_Document */ private function getDocumentForRevision($revision){ $path = getDocumentRevisionPath($revision->id); if(!file_exists($path)){ return array("error"=>"File not found"); } //Convert the file to a lucene document $mime = $revision->file_mime_type; switch($mime){ case 'application/pdf': $document = createPDFDocument($path); break; case 'application/msword': $document = createDocDocument($path); break; case 'application/vnd.oasis.opendocument.text': $document = createOdtDocument($path); break; case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': $document = createDocXDocument($path); break; case 'text/html': $document = createHTMLDocument($path); break; case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': $document = createXLSXDocument($path); break; case 'application/rtf': $document = createRTFDocument($path); case 'text/csv': case 'text/plain': $document = createTextDocument($path); break; case 'application/vnd.openxmlformats-officedocument.presentationml.presentation': $document = createPPTXDocument($path); break; case 'application/vnd.oasis.opendocument.spreadsheet': case 'application/vnd.ms-powerpoint': case 'application/vnd.ms-excel': default: return array("error"=>"Mime type $mime not supported"); } if(!$document){ return array("error"=>"Failed to parse document"); } $document->addField(Zend_Search_Lucene_Field::text("filename",$revision->filename)); return array("error"=>false,"document"=>$document); } public function getDocumentForBean(SugarBean $bean){ if($bean->module_name == 'DocumentRevisions'){ $document = $this->getDocumentForRevision($bean); }else{ $document = array("error"=>false,"document"=>new Zend_Search_Lucene_Document()); } if($document["error"]){ return $document; } $document["document"]->addField(Zend_Search_Lucene_Field::Keyword("aod_id", $bean->module_name." ".$bean->id)); $document["document"]->addField(Zend_Search_Lucene_Field::UnIndexed("record_id", $bean->id)); $document["document"]->addField(Zend_Search_Lucene_Field::UnIndexed("record_module", $bean->module_name)); foreach($GLOBALS['dictionary'][$bean->getObjectName()]['fields'] as $key => $field){ switch($field['type']){ case "enum": if(property_exists($bean, $key)) { $document["document"]->addField(Zend_Search_Lucene_Field::Keyword($key, strtolower($bean->$key),'UTF-8')); } break; case "multienum": if(property_exists($bean, $key)) { $vals = unencodeMultienum($bean->$key); $document["document"]->addField(Zend_Search_Lucene_Field::unStored($key, strtolower(implode(" ",$vals)),'UTF-8')); } break; case "name": case "phone": case "html": case "text": case "url": case "varchar": if(property_exists($bean,$key)){ $val = strtolower($bean->$key); }else{ $val = ''; } $field = Zend_Search_Lucene_Field::unStored($key, $val,'UTF-8'); $field->boost = $this->getBoost($bean->module_name,$key); $document["document"]->addField($field); break; case "address": case "bool": case "currency": case "date": case "datetimecombo": case "decimal": case "float": case "iframe": case "int": case "radioenum": case "relate": default: break; } } return $document; } private function getBoost($module, $field){ $fieldBoosts = array('name' =>0.5, 'first_name' => 0.5, 'last_name' => 0.5); $moduleBoosts = array('Accounts' => 0.5, 'Contacts' => 0.5, 'Leads' => 0.5, 'Opportunities' => 0.5); $boost = 1; if(!empty($fieldBoosts[$field])){ $boost += $fieldBoosts[$field]; } if(!empty($moduleBoosts[$module])){ $boost += $moduleBoosts[$module]; } return $boost; } private function getIndexEvent($module, $beanId){ global $timedate; $indexEventBean = BeanFactory::getBean("AOD_IndexEvent"); $indexEvents = $indexEventBean->get_full_list('',"aod_indexevent.record_id = '".$beanId."' AND aod_indexevent.record_module = '".$module."'"); if($indexEvents){ $indexEvent = $indexEvents[0]; if(count($indexEvents) > 1){ for($x = 1; $x < count($indexEvents); $x++){ $duplicateIE = $indexEvents[$x]; $duplicateIE->mark_deleted($duplicateIE->id); } } }else{ $indexEvent = BeanFactory::newBean("AOD_IndexEvent"); $indexEvent->record_id = $beanId; $indexEvent->record_module = $module; } /* * "Now" is cached in the SugarBean which means for long running processes (such as the indexing scheduler) that * the date_modified could be in the past. This caused issues when comparing the date modified of the event with that * of a bean. Here we explicitly set the date modified to be the current date. */ $indexEvent->update_date_modified = false; $indexEvent->date_modified = $timedate->asDb(new DateTime()); return $indexEvent; } public function commit(){ if(!$this->isEnabled()){ return; } $this->getLuceneIndex()->commit(); } public static function isModuleSearchable($module,$beanName){ $whiteList = array("DocumentRevisions","Cases"); if(in_array($module,$whiteList)){ return true; } $blackList = array("AOD_IndexEvent","AOD_Index","AOW_Actions","AOW_Conditions","AOW_Processed","SchedulersJobs"); if(in_array($module,$blackList)){ return false; } $manager = new VardefManager(); $manager->loadVardef($module, $beanName); if(empty($GLOBALS['dictionary'][$beanName]['unified_search'])){ return false; } return true; } public function index($module, $beanId){ try{ if(!$this->isEnabled()){ return; } if(empty($GLOBALS['beanList'][$module])){ return false; } $bean_name = $GLOBALS['beanList'][$module]; $bean = new $bean_name(); if(!$bean || ! $bean instanceof SugarBean){ return false; } if(!self::isModuleSearchable($module,BeanFactory::getObjectName($module))){ return false; } $bean = $bean->retrieve($beanId); if(!$bean){ return false; } $indexEvent = $this->getIndexEvent($module,$beanId); $indexEvent->name = $bean->get_summary_text(); $document = $this->getDocumentForBean($bean); //Index name, id, date, filename if(!$document['error']){ $this->remove($module,$beanId); $this->getLuceneIndex()->addDocument($document['document']); $indexEvent->success = true; }else{ $indexEvent->success = false; $indexEvent->error = $document['error']; } $indexEvent->save(); }catch(Exception $ex){ $GLOBALS['log']->error($ex->getMessage()); return false; } return true; } private function getIdForDoc($module, $beanId){ return $module . " " . $beanId; } public function remove($module, $beanId){ $term = new Zend_Search_Lucene_Index_Term($module.' '.$beanId, 'aod_id'); $query = new Zend_Search_Lucene_Search_Query_Term($term); $hits = $this->getLuceneIndex()->find($query); foreach ($hits as $hit) { $this->getLuceneIndex()->delete($hit->id); } } /** * Returns a handle on the actual lucene index. * @return Zend_Search_Lucene_Interface */ private function getLuceneIndex(){ if(file_exists($this->location)){ $this->index = new Zend_Search_Lucene($this->location); }else{ $this->index = Zend_Search_Lucene::create($this->location); } $this->index->setMaxBufferedDocs(64); //$this->index->setMaxMergeDocs(50); Zend_Search_Lucene_Search_Query_Fuzzy::setDefaultPrefixLength(1); $this->index->setMergeFactor(5); return $this->index; } public function getIndexableModules(){ $modules = array(); $beanList = $GLOBALS['beanList']; ksort($beanList); foreach($beanList as $beanModule => $beanName){ if(self::isModuleSearchable($beanModule,$beanName)){ $modules[$beanModule] = $beanName; } } return $modules; } } ?> Save