1<?php
2/* Copyright (c) 1998-2009 ILIAS open source, Extended GPL, see docs/LICENSE */
3
4
5include_once './Services/Search/classes/class.ilQueryParser.php';
6
7/**
8* search
9*
10* @author Stefan Meyer <meyer@leifos.com>
11* @version Id$
12*
13*/
14class ilSearch
15{
16    protected $qp = null;
17
18    /**
19    * ilias object
20    * @var object DB
21    * @access public
22    */
23    public $ilias;
24    public $lng;
25    public $rbacsystem;
26    public $user_id;				// INTEGER USED FOR SAVED RESULTS
27    public $search_string;			// INPUT FROM SEARCH FORM
28    public $parsed_str;			// PARSED INPUT
29    public $combination;			// STRING 'and' or 'or'
30    public $min_word_length = 3;	// Define minimum character length for queries
31    public $search_for;			// OBJECT TYPE 'usr','grp','lm','dbk'
32    public $search_in;				// STRING SEARCH IN 'content' OR 'meta'
33    public $search_type;			// STRING 'new' or 'result'
34    public $result;				// RESULT SET array['object_type']['counter']
35    public $perform_update;		// UPDATE USER SEARCH HISTORY default is true SEE function setPerformUpdate()
36    public $read_db_result;		// READ db result true/false
37
38    public $allow_empty_search;		// ALLOW EMPTY SEARCH TERM use setEmtySearch(true | false) TO SET THIS VALUE DEFAULT (FALSE)
39    /**
40    * Constructor
41    * @access	public
42    */
43    public function __construct($a_user_id = 0, $a_read = false)
44    {
45        global $DIC;
46
47        $ilias = $DIC['ilias'];
48        $rbacsystem = $DIC['rbacsystem'];
49        $lng = $DIC['lng'];
50
51        // Initiate variables
52        $this->ilias = &$ilias;
53        $this->lng = &$lng;
54        $this->lng->loadLanguageModule("search");
55        $this->rbacsystem = &$rbacsystem;
56        $this->user_id = $a_user_id;
57
58        $this->setPerformUpdate(true);
59        $this->setEmptySearch(false);
60        $this->read_db_result = $a_read;
61
62        // READ OLD SEARCH RESULTS FROM DATABASE
63        #$this->__readDBResult();
64    }
65
66    // SET METHODS
67    public function setSearchString($a_search_str)
68    {
69        $this->search_string = trim($a_search_str);
70    }
71    public function setCombination($a_combination)
72    {
73        // 'and' or 'or'
74        $this->combination = $a_combination;
75    }
76    public function setSearchFor($a_search_for)
77    {
78        $this->search_for = $a_search_for;
79    }
80    public function setSearchIn($a_search_in)
81    {
82        $this->search_in = $a_search_in;
83    }
84    public function setResult($a_result)
85    {
86        $this->result = $a_result;
87    }
88    public function setSearchType($a_type)
89    {
90        $this->search_type = $a_type;
91    }
92    public function setPerformUpdate($a_value)
93    {
94        $this->perform_update = $a_value;
95    }
96    public function setEmptySearch($a_value)
97    {
98        $this->allow_empty_search = $a_value;
99    }
100
101    public function setMinWordLength($a_min_word_length)
102    {
103        $this->min_word_length = $a_min_word_length;
104    }
105    public function getMinWordLength()
106    {
107        return $this->min_word_length;
108    }
109
110    // GET MEHODS
111    public function getUserId()
112    {
113        return $this->user_id;
114    }
115    public function getSearchString()
116    {
117        return $this->search_string;
118    }
119    public function getCombination()
120    {
121        return $this->combination ? $this->combination : "or";
122    }
123    public function getSearchFor()
124    {
125        return $this->search_for ? $this->search_for : array();
126    }
127    public function getSearchIn()
128    {
129        return $this->search_in ? $this->search_in : array();
130    }
131    public function getSearchInByType($a_type)
132    {
133        if ($a_type == 'lm' or $a_type == 'dbk') {
134            return $this->search_in[$a_type];
135        } else {
136            return false;
137        }
138    }
139    public function getResults()
140    {
141        return $this->result ? $this->result : array();
142    }
143    public function getResultByType($a_type)
144    {
145        return $this->result[$a_type] ? $this->result[$a_type] : array();
146    }
147    public function getSearchType()
148    {
149        return $this->search_type;
150    }
151    public function getPerformUpdate()
152    {
153        return $this->perform_update;
154    }
155    public function getEmptySearch()
156    {
157        return $this->allow_empty_search;
158    }
159
160
161    // PUBLIC
162    public function getNumberOfResults()
163    {
164        $number = count($this->getResultByType("usr")) + count($this->getResultByType("grp")) + count($this->getResultByType("role"));
165
166        $tmp_res = $this->getResultByType("dbk");
167        $number += count($tmp_res["meta"]) + count($tmp_res["content"]);
168
169        $tmp_res = $this->getResultByType("lm");
170        $number += count($tmp_res["meta"]) + count($tmp_res["content"]);
171
172        return $number;
173    }
174
175    public function validate(&$message)
176    {
177        $this->initQueryParser();
178
179        if (!$this->qp->validate()) {
180            $message = $this->qp->getMessage();
181            return false;
182        }
183        return true;
184    }
185
186    public function performSearch()
187    {
188        global $DIC;
189
190        $objDefinition = $DIC['objDefinition'];
191        $ilBench = $DIC['ilBench'];
192
193        $ilBench->start("Search", "performSearch");
194
195        $this->initQueryParser();
196
197        $result = array("usr" => array(),
198                        "grp" => array(),
199                        "lm" => array(),
200                        "dbk" => array(),
201                        "role" => array());
202
203        foreach ($this->getSearchFor() as $obj_type) {
204            switch ($obj_type) {
205                case "usr":
206                    $result['usr'] = $this->performUserSearch();
207                    break;
208
209                case "grp":
210                    $result['grp'] = $this->performObjectSearch('grp');
211                    break;
212
213                case "lm":
214                    $result['lm'] = $this->performObjectSearch('lm');
215                    break;
216
217                case "dbk":
218                    $result['dbk'] = $this->performObjectSearch('dbk');
219                    break;
220
221                case "role":
222                    $result['role'] = $this->performRoleSearch();
223                    break;
224            }
225        }
226
227        $this->setResult($result);
228        $this->__validateResults();
229
230        if ($this->getPerformUpdate()) {
231            $this->__updateDBResult();
232        }
233
234        $ilBench->stop("Search", "performSearch");
235
236        return true;
237    }
238
239
240    // PRIVATE METHODS
241
242    public function __parseSearchString()
243    {
244        $tmp_arr = explode(" ", $this->getSearchString());
245        $this->parsed_str["and"] = $this->parsed_str["or"] = $this->parsed_str["not"] = array();
246
247        foreach ($tmp_arr as $word) {
248            #$word = trim($word);
249            $word = $this->__prepareWord($word);
250            if ($word) {
251                if (substr($word, 0, 1) == '+') {
252                    $this->parsed_str["all"][] = substr($word, 1);
253                    $this->parsed_str["and"][] = substr($word, 1);
254                    continue;
255                }
256
257                if (substr($word, 0, 1) == '-') {
258                    // better parsed_str["allmost_all"] ;-)
259                    #$this->parsed_str["all"][] = substr($word,1);
260                    $this->parsed_str["not"][] = substr($word, 1);
261                    continue;
262                }
263
264                if ($this->getCombination() == 'and') {
265                    $this->parsed_str["all"][] = $word;
266                    $this->parsed_str["and"][] = $word;
267                    continue;
268                }
269
270                if ($this->getCombination() == 'or') {
271                    $this->parsed_str["all"][] = $word;
272                    $this->parsed_str["or"][] = $word;
273                    continue;
274                }
275            }
276        }
277    }
278
279    public function __updateDBResult()
280    {
281        global $DIC;
282
283        $ilDB = $DIC['ilDB'];
284
285        if ($this->getUserId() != 0 and $this->getUserId() != ANONYMOUS_USER_ID) {
286            $ilDB->manipulate("DELETE FROM usr_search " .
287                "WHERE usr_id = " . $ilDB->quote($this->getUserId(), 'integer') . " " .
288                "AND search_type = 0 ");
289
290            $ilDB->insert('usr_search', array(
291                'usr_id' => array('integer',$this->getUserId()),
292                'search_result' => array('clob',serialize($this->getResults())),
293                'checked' => array('clob',serialize(array())),
294                'failed' => array('clob',serialize(array())),
295                'page' => array('integer',0),
296                'search_type' => array('integer',0),
297                'query' => array('text',''),
298                'root' => array('integer',ROOT_FOLDER_ID)));
299
300            return true;
301        }
302
303        return false;
304    }
305
306    public function __readDBResult()
307    {
308        global $DIC;
309
310        $ilDB = $DIC['ilDB'];
311
312        if ($this->getUserId() != 0 and $this->getUserId() != ANONYMOUS_USER_ID and $this->read_db_result) {
313            $query = "SELECT search_result FROM usr_search " .
314                "WHERE usr_id = " . $ilDB->quote($this->getUserId(), 'integer');
315
316
317            $res = $ilDB->query($query);
318            if ($res->numRows()) {
319                $row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT);
320                $this->setResult(unserialize(stripslashes($row->search_result)));
321            } else {
322                $this->setResult(array("usr" => array(),
323                                       "grp" => array(),
324                                       "lm" => array(),
325                                       "dbk" => array()));
326            }
327        } else {
328            $this->setResult(array("usr" => array(),
329                                   "grp" => array(),
330                                   "lm" => array(),
331                                   "dbk" => array()));
332        }
333
334        $this->__validateResults();
335        $this->__updateDBResult();
336        return true;
337    }
338
339
340    public function __checkAccess($a_results, $a_type)
341    {
342        global $DIC;
343
344        $ilAccess = $DIC['ilAccess'];
345
346        if (is_array($a_results)) {
347            foreach ($a_results as $result) {
348                if ($ilAccess->checkAccess('read', '', $result['id'])) {
349                    $checked_result[] = $result;
350                    break;
351                }
352            }
353        }
354        return $checked_result ? $checked_result : array();
355    }
356
357
358    public function __validateResults()
359    {
360        global $DIC;
361
362        $tree = $DIC['tree'];
363
364        $new_result = array();
365
366
367        // check lm meta
368
369        $this->result['lm']['meta'] = $this->__checkAccess($this->result['lm']['meta'], 'lm');
370        if (is_array($this->result['lm']['meta'])) {
371            foreach ($this->result['lm']['meta'] as $data) {
372                if ($tree->isInTree($data['id'])) {
373                    $new_result['lm']['meta'][] = $data;
374                }
375            }
376        }
377        $this->result['lm']['content'] = $this->__checkAccess($this->result['lm']['content'], 'lm');
378        if (is_array($this->result['lm']['content'])) {
379            foreach ($this->result['lm']['content'] as $data) {
380                if ($tree->isInTree($data['id'])) {
381                    $new_result['lm']['content'][] = $data;
382                }
383            }
384        }
385        $this->result['dbk']['meta'] = $this->__checkAccess($this->result['dbk']['meta'], 'dbk');
386        if (is_array($this->result['dbk']['meta'])) {
387            foreach ($this->result['dbk']['meta'] as $data) {
388                if ($tree->isInTree($data['id'])) {
389                    $new_result['dbk']['meta'][] = $data;
390                }
391            }
392        }
393        $this->result['dbk']['content'] = $this->__checkAccess($this->result['dbk']['content'], 'dbk');
394        if (is_array($this->result['dbk']['content'])) {
395            foreach ($this->result['dbk']['content'] as $data) {
396                if ($tree->isInTree($data['id'])) {
397                    $new_result['dbk']['content'][] = $data;
398                }
399            }
400        }
401        $this->result['grp'] = $this->__checkAccess($this->result['grp'], 'grp');
402        if (is_array($this->result['grp'])) {
403            foreach ($this->result['grp'] as $data) {
404                if ($tree->isInTree($data['id'])) {
405                    $new_result['grp'][] = $data;
406                }
407            }
408        }
409        if (is_array($this->result['usr'])) {
410            foreach ($this->result['usr'] as $user) {
411                if ($tmp_obj = &ilObjectFactory::getInstanceByObjId($user['id'], false)) {
412                    $new_result['usr'][] = $user;
413                }
414            }
415        }
416        if (is_array($this->result['role'])) {
417            foreach ($this->result['role'] as $user) {
418                if ($tmp_obj = &ilObjectFactory::getInstanceByObjId($user['id'], false)) {
419                    $new_result['role'][] = $user;
420                }
421            }
422        }
423        $this->setResult($new_result);
424
425        return true;
426    }
427
428    public function __prepareWord($a_word)
429    {
430        $word = trim($a_word);
431
432        if (!preg_match('/\*/', $word)) {
433            return '%' . $word . '%';
434        }
435        if (preg_match('/^\*/', $word)) {
436            return str_replace('*', '%', $word);
437        } else {
438            return '% ' . str_replace('*', '%', $word);
439        }
440    }
441
442    /**
443     * perform a search for users
444     * @return
445     */
446    protected function performUserSearch()
447    {
448        include_once 'Services/Search/classes/class.ilObjectSearchFactory.php';
449
450        $user_search = ilObjectSearchFactory::_getUserSearchInstance($this->qp);
451        $res = new ilSearchResult($this->getUserId());
452
453        foreach (array("login","firstname","lastname","title",
454                "email","institution","street","city","zipcode","country","phone_home","fax") as $field) {
455            $user_search->setFields(array($field));
456            $tmp_res = $user_search->performSearch();
457
458            $res->mergeEntries($tmp_res);
459        }
460
461        foreach ($res->getEntries() as $id => $data) {
462            $tmp['id'] = $id;
463            $users[] = $tmp;
464        }
465        return $users ? $users : array();
466    }
467
468    /**
469     * perform object search
470     * @return
471     */
472    protected function performObjectSearch($a_type)
473    {
474        include_once 'Services/Search/classes/Like/class.ilLikeObjectSearch.php';
475        $object_search = new ilLikeObjectSearch($this->qp);
476        $object_search->setFilter(array($a_type));
477        $res = $object_search->performSearch();
478        $res->filter(ROOT_FOLDER_ID, $this->getCombination());
479
480        $counter = 0;
481        foreach ($res->getResultIds() as $id) {
482            $objs[$counter++]['id'] = $id;
483        }
484        return $objs ? $objs : array();
485    }
486
487    /**
488     *
489     * @param
490     * @return
491     */
492    protected function performRoleSearch()
493    {
494        // Perform like search
495        include_once 'Services/Search/classes/Like/class.ilLikeObjectSearch.php';
496        $object_search = new ilLikeObjectSearch($this->qp);
497        $object_search->setFilter(array('role'));
498
499        $res = $object_search->performSearch();
500        foreach ($res->getEntries() as $id => $data) {
501            $tmp['id'] = $id;
502            $roles[] = $tmp;
503        }
504        return $roles ? $roles : array();
505    }
506
507    /**
508     * init query parser
509     * @return
510     */
511    protected function initQueryParser()
512    {
513        if ($this->qp) {
514            return true;
515        }
516
517        $this->qp = new ilQueryParser($this->getSearchString());
518        $this->qp->setCombination($this->getCombination() == 'and' ? QP_COMBINATION_AND : QP_COMBINATION_OR);
519        $this->qp->setMinWordLength($this->getMinWordLength());
520        $this->qp->parse();
521    }
522} // END class.ilSearch
523