Passed
Push — scrutinizer-code-quality ( 09f5a1...c4c5fb )
by Adam
56:05 queued 14:08
created

AOD_Index::AOD_Index()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6
Metric Value
cc 2
eloc 7
nc 2
nop 0
dl 0
loc 10
ccs 0
cts 7
cp 0
crap 6
rs 9.4285
1
<?PHP
2
/*********************************************************************************
3
 * SugarCRM Community Edition is a customer relationship management program developed by
4
 * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
5
 *
6
 * This program is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU Affero General Public License version 3 as published by the
8
 * Free Software Foundation with the addition of the following permission added
9
 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
10
 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
11
 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
12
 *
13
 * This program is distributed in the hope that it will be useful, but WITHOUT
14
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15
 * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
16
 * details.
17
 *
18
 * You should have received a copy of the GNU Affero General Public License along with
19
 * this program; if not, see http://www.gnu.org/licenses or write to the Free
20
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21
 * 02110-1301 USA.
22
 *
23
 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
24
 * SW2-130, Cupertino, CA 95014, USA. or at email address [email protected].
25
 *
26
 * The interactive user interfaces in modified source and object code versions
27
 * of this program must display Appropriate Legal Notices, as required under
28
 * Section 5 of the GNU Affero General Public License version 3.
29
 *
30
 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
31
 * these Appropriate Legal Notices must retain the display of the "Powered by
32
 * SugarCRM" logo. If the display of the logo is not reasonably feasible for
33
 * technical reasons, the Appropriate Legal Notices must display the words
34
 * "Powered by SugarCRM".
35
 ********************************************************************************/
36
37
/**
38
 * THIS CLASS IS FOR DEVELOPERS TO MAKE CUSTOMIZATIONS IN
39
 */
40 1
require_once('modules/AOD_Index/AOD_Index_sugar.php');
41 1
require_once('modules/AOD_Index/LuceneUtils.php');
42 1
requireLucene();
43
44
class AOD_Index extends AOD_Index_sugar {
45
46 84
	function __construct(){
47 84
		parent::__construct();
48 84
        Zend_Search_Lucene_Search_QueryParser::setDefaultEncoding('utf-8');
49 84
        Zend_Search_Lucene_Analysis_Analyzer::setDefault(new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8Num_CaseInsensitive());
50 84
    }
51
52
    /**
53
     * @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
54
     */
55
    function AOD_Index(){
56
        $deprecatedMessage = 'PHP4 Style Constructors are deprecated and will be remove in 7.8, please update your code';
57
        if(isset($GLOBALS['log'])) {
58
            $GLOBALS['log']->deprecated($deprecatedMessage);
59
        }
60
        else {
61
            trigger_error($deprecatedMessage, E_USER_DEPRECATED);
62
        }
63
        self::__construct();
64
    }
65
66
67 71
    function isEnabled(){
68 71
        global $sugar_config;
69 71
        return !empty($sugar_config['aod']['enable_aod']);
70
    }
71
72 1
    function find($queryString){
73 1
        $queryString = strtolower($queryString);
74 1
        $hits = $this->getLuceneIndex()->find($queryString);
75 1
        return $hits;
76
    }
77
78 1
    function optimise(){
79 1
        if(!$this->isEnabled()){
80
            return;
81
        }
82 1
        global $timedate;
83 1
        $this->getLuceneIndex()->optimize();
84 1
        $this->last_optimised = $timedate->getNow()->asDb();
85 1
        $this->save();
86 1
    }
87
88 71
    public function getIndex(){
89 71
        $index = BeanFactory::getBean('AOD_Index',1);
90 71
        if(!empty($index) && !empty($index->id)){
91 70
            return $index;
92
        }else{
93 1
            $index = new AOD_Index();
94 1
            $index->id = 1;
95 1
            $index->new_with_id = true;
96 1
            $index->name = "Index";
97 1
            $index->location = "modules/AOD_Index/Index/Index";
98 1
            $index->save();
99 1
            return $index;
100
        }
101
    }
102
103
    /**
104
     * @param $revision
105
     * @return bool|Zend_Search_Lucene_Document
106
     */
107 1
    private function getDocumentForRevision($revision){
108 1
        $path = getDocumentRevisionPath($revision->id);
109 1
        if(!file_exists($path)){
110 1
            return array("error"=>"File not found");
111
        }
112
        //Convert the file to a lucene document
113
        $mime = $revision->file_mime_type;
114
        switch($mime){
115
            case 'application/pdf':
116
                $document = createPDFDocument($path);
117
                break;
118
            case 'application/msword':
119
                $document = createDocDocument($path);
120
                break;
121
            case 'application/vnd.oasis.opendocument.text':
122
                $document = createOdtDocument($path);
123
                break;
124
            case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
125
                $document = createDocXDocument($path);
126
                break;
127
            case 'text/html':
128
                $document = createHTMLDocument($path);
129
                break;
130
            case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
131
                $document = createXLSXDocument($path);
132
                break;
133
            case 'application/rtf':
0 ignored issues
show
Coding Style introduced by
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
134
                $document = createRTFDocument($path);
135
            case 'text/csv':
136
            case 'text/plain':
137
                $document = createTextDocument($path);
138
                break;
139
            case 'application/vnd.openxmlformats-officedocument.presentationml.presentation':
140
                $document = createPPTXDocument($path);
141
                break;
142
            case 'application/vnd.oasis.opendocument.spreadsheet':
143
            case 'application/vnd.ms-powerpoint':
144
            case 'application/vnd.ms-excel':
145
            default:
146
                return array("error"=>"Mime type $mime not supported");
147
        }
148
        if(!$document){
149
            return array("error"=>"Failed to parse document");
150
        }
151
        $document->addField(Zend_Search_Lucene_Field::text("filename",$revision->filename));
152
        return array("error"=>false,"document"=>$document);
153
    }
154
155 21
    public function getDocumentForBean(SugarBean $bean){
156 21
        if($bean->module_name == 'DocumentRevisions'){
157 1
            $document = $this->getDocumentForRevision($bean);
158
        }else{
159 20
            $document = array("error"=>false,"document"=>new Zend_Search_Lucene_Document());
160
        }
161 21
        if($document["error"]){
162 1
            return $document;
163
        }
164 20
        $document["document"]->addField(Zend_Search_Lucene_Field::Keyword("aod_id", $bean->module_name." ".$bean->id));
165 20
        $document["document"]->addField(Zend_Search_Lucene_Field::UnIndexed("record_id", $bean->id));
166 20
        $document["document"]->addField(Zend_Search_Lucene_Field::UnIndexed("record_module", $bean->module_name));
167 20
        foreach($GLOBALS['dictionary'][$bean->getObjectName()]['fields'] as $key => $field){
168 20
            switch($field['type']){
169 20
                case "enum":
170 17
                	if(property_exists($bean, $key)) {
171 17
                		$document["document"]->addField(Zend_Search_Lucene_Field::Keyword($key, strtolower($bean->$key),'UTF-8'));
172
                	}
173 17
                    break;
174
175 20
                case "multienum":
176
                	if(property_exists($bean, $key)) {
177
                		$vals = unencodeMultienum($bean->$key);
178
                		$document["document"]->addField(Zend_Search_Lucene_Field::unStored($key, strtolower(implode(" ",$vals)),'UTF-8'));
179
                	}
180
                    break;
181 20
                case "name":
182 20
                case "phone":
183 20
                case "html":
184 20
                case "text":
185 20
                case "url":
186 20
                case "varchar":
187 20
                    if(property_exists($bean,$key)){
188 20
                        $val = strtolower($bean->$key);
189
                    }else{
190 1
                        $val = '';
191
                    }
192 20
                    $field = Zend_Search_Lucene_Field::unStored($key, $val,'UTF-8');
193 20
                    $field->boost = $this->getBoost($bean->module_name,$key);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getBoost($bean->module_name, $key) can also be of type integer. However, the property $boost is declared as type double. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
194 20
                    $document["document"]->addField($field);
195 20
                    break;
196 20
                case "address":
197 20
                case "bool":
198 20
                case "currency":
199 20
                case "date":
200 20
                case "datetimecombo":
201 20
                case "decimal":
202 20
                case "float":
203 20
                case "iframe":
204 20
                case "int":
205 20
                case "radioenum":
206 20
                case "relate":
207
                default:
208 20
                    break;
209
            }
210
        }
211
212 20
        return $document;
213
    }
214
215 20
    private function getBoost($module, $field){
216 20
        $fieldBoosts = array('name' =>0.5, 'first_name' => 0.5, 'last_name' => 0.5);
217 20
        $moduleBoosts = array('Accounts' => 0.5, 'Contacts' => 0.5, 'Leads' => 0.5, 'Opportunities' => 0.5);
218 20
        $boost = 1;
219 20
        if(!empty($fieldBoosts[$field])){
220 20
            $boost += $fieldBoosts[$field];
221
        }
222 20
        if(!empty($moduleBoosts[$module])){
223 1
            $boost += $moduleBoosts[$module];
224
        }
225 20
        return $boost;
226
    }
227
228 20
    private function getIndexEvent($module, $beanId){
229 20
    	global $timedate;
230 20
        $indexEventBean = BeanFactory::getBean("AOD_IndexEvent");
231 20
        $indexEvents = $indexEventBean->get_full_list('',"aod_indexevent.record_id = '".$beanId."' AND aod_indexevent.record_module = '".$module."'");
232 20
        if($indexEvents){
233 3
            $indexEvent = $indexEvents[0];
234 3
            if(count($indexEvents) > 1){
235 3
                for($x = 1; $x < count($indexEvents); $x++){
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
236
                    $duplicateIE = $indexEvents[$x];
237
                    $duplicateIE->mark_deleted($duplicateIE->id);
238
                }
239
            }
240
241
        }else{
242 20
            $indexEvent = BeanFactory::newBean("AOD_IndexEvent");
243 20
            $indexEvent->record_id = $beanId;
244 20
            $indexEvent->record_module = $module;
245
        }
246
        /*
247
         * "Now" is cached in the SugarBean which means for long running processes (such as the indexing scheduler) that
248
         * the date_modified could be in the past. This caused issues when comparing the date modified of the event with that
249
         * of a bean. Here we explicitly set the date modified to be the current date.
250
         */
251 20
        $indexEvent->update_date_modified = false;
252 20
        $indexEvent->date_modified = $timedate->asDb(new DateTime());
253 20
        return $indexEvent;
254
    }
255
256 1
    public function commit(){
257 1
        if(!$this->isEnabled()){
258
            return;
259
        }
260 1
        $this->getLuceneIndex()->commit();
261 1
    }
262
263 70
    public static function isModuleSearchable($module,$beanName){
264 70
        $whiteList = array("DocumentRevisions","Cases");
265 70
        if(in_array($module,$whiteList)){
266 4
            return true;
267
        }
268 70
        $blackList = array("AOD_IndexEvent","AOD_Index","AOW_Actions","AOW_Conditions","AOW_Processed","SchedulersJobs");
269 70
        if(in_array($module,$blackList)){
270 33
            return false;
271
        }
272 58
        $manager = new VardefManager();
273 58
        $manager->loadVardef($module, $beanName);
274 58
        if(empty($GLOBALS['dictionary'][$beanName]['unified_search'])){
275 43
            return false;
276
        }
277 23
        return true;
278
    }
279
280 68
    public function index($module, $beanId){
281
        try{
282 68
            if(!$this->isEnabled()){
283
                return;
284
            }
285 68
            if(empty($GLOBALS['beanList'][$module])){
286
                return false;
287
            }
288 68
            $bean_name = $GLOBALS['beanList'][$module];
289 68
            $bean = new $bean_name();
290 68
            if(!$bean || ! $bean instanceof SugarBean){
291
                return false;
292
            }
293
294 68
            if(!self::isModuleSearchable($module,BeanFactory::getObjectName($module))){
295 67
                return false;
296
            }
297
298 22
            $bean = $bean->retrieve($beanId);
299 22
            if(!$bean){
300 2
                return false;
301
            }
302
303 20
            $indexEvent = $this->getIndexEvent($module,$beanId);
304 20
            $indexEvent->name = $bean->get_summary_text();
305
306 20
            $document = $this->getDocumentForBean($bean);
307
            //Index name, id, date, filename
308 20
            if(!$document['error']){
309 19
                $this->remove($module,$beanId);
310 19
                $this->getLuceneIndex()->addDocument($document['document']);
311 19
                $indexEvent->success = true;
312
            }else{
313 1
                $indexEvent->success = false;
314 1
                $indexEvent->error = $document['error'];
315
            }
316 20
            $indexEvent->save();
317
        }catch(Exception $ex){
318
            $GLOBALS['log']->error($ex->getMessage());
319
            return false;
320
        }
321 20
        return true;
322
    }
323
    private function getIdForDoc($module, $beanId){
324
        return $module . " " . $beanId;
325
    }
326
327 45
    public function remove($module, $beanId){
328 45
        $term  = new Zend_Search_Lucene_Index_Term($module.' '.$beanId, 'aod_id');
329 45
        $query = new Zend_Search_Lucene_Search_Query_Term($term);
330 45
        $hits = $this->getLuceneIndex()->find($query);
331 45
        foreach ($hits as $hit) {
332 3
            $this->getLuceneIndex()->delete($hit->id);
333
        }
334 45
    }
335
336
    /**
337
     * Returns a handle on the actual lucene index.
338
     * @return Zend_Search_Lucene_Interface
339
     */
340 48
    private function getLuceneIndex(){
341 48
        if(file_exists($this->location)){
342 48
            $this->index = new Zend_Search_Lucene($this->location);
343
        }else{
344 1
            $this->index = Zend_Search_Lucene::create($this->location);
345
        }
346 48
        $this->index->setMaxBufferedDocs(64);
347
        //$this->index->setMaxMergeDocs(50);
348 48
        Zend_Search_Lucene_Search_Query_Fuzzy::setDefaultPrefixLength(1);
349 48
        $this->index->setMergeFactor(5);
350 48
        return $this->index;
351
    }
352
353
354 1
    public function getIndexableModules(){
355 1
        $modules = array();
356 1
        $beanList = $GLOBALS['beanList'];
357 1
        ksort($beanList);
358 1
        foreach($beanList as $beanModule => $beanName){
359 1
            if(self::isModuleSearchable($beanModule,$beanName)){
360 1
                $modules[$beanModule] = $beanName;
361
            }
362
        }
363 1
        return $modules;
364
    }
365
}
366
?>
367