2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4 * SugarCRM Community Edition is a customer relationship management program developed by
5 * SugarCRM, Inc. Copyright (C) 2004-2011 SugarCRM Inc.
7 * This program is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU Affero General Public License version 3 as published by the
9 * Free Software Foundation with the addition of the following permission added
10 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
19 * You should have received a copy of the GNU Affero General Public License along with
20 * this program; if not, see http://www.gnu.org/licenses or write to the Free
21 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25 * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
27 * The interactive user interfaces in modified source and object code versions
28 * of this program must display Appropriate Legal Notices, as required under
29 * Section 5 of the GNU Affero General Public License version 3.
31 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32 * these Appropriate Legal Notices must retain the display of the "Powered by
33 * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34 * technical reasons, the Appropriate Legal Notices must display the words
35 * "Powered by SugarCRM".
36 ********************************************************************************/
42 * Performs the search and returns the HTML widget containing the results
44 * @param $query string what we are searching for
45 * @param $modules array modules we are searching in
46 * @param $offset int search result offset
47 * @return string HTML widget
49 public function searchAndDisplay(
55 $query_encoded = urlencode($query);
56 $results = $this->_performSearch($query, $modules, $offset);
58 $str = '<div id="SpotResults">';
62 foreach($results as $m=>$data){
63 if(empty($data['data'])){
69 $countRemaining = $data['pageData']['offsets']['total'] - count($data['data']);
70 if($offset > 0) $countRemaining -= $offset;
72 $data['pageData']['offsets']['next']++;
73 if($countRemaining > 0){
75 <small class='more' onclick="DCMenu.spotZoom('$query', '$m','{$data['pageData']['offsets']['next']}' )">($countRemaining {$GLOBALS['app_strings']['LBL_SEARCH_MORE']})</small>
79 $modDisplayString = $m;
80 if(isset($GLOBALS['app_list_strings']['moduleList'][$m]))
81 $modDisplayString = $GLOBALS['app_list_strings']['moduleList'][$m];
83 $str.= "<div>{$modDisplayString} $more</div>";
85 foreach($data['data'] as $row){
88 if(!empty($row['NAME'])){
90 } else if(!empty($row['DOCUMENT_NAME'])) {
91 $name = $row['DOCUMENT_NAME'];
93 foreach($row as $k=>$v){
94 if(strpos($k, 'NAME') !== false && !empty($row[$k])){
102 foreach($row as $k=>$v){
103 if(strpos($k, 'NAME') !== false){
112 <li><a href="index.php?module={$data['pageData']['bean']['moduleDir']}&action=DetailView&record={$row['ID']}">$name</a></li>
121 <button onclick="document.location.href='index.php?module=Home&action=UnifiedSearch&search_form=false&advanced=false&query_string={$query_encoded}'">{$GLOBALS['app_strings']['LBL_EMAIL_SHOW_READ']}</button>
126 <button onclick="document.location.href='index.php?module=Home&action=UnifiedSearch&search_form=false&advanced=false&query_string={$query_encoded}'">{$GLOBALS['app_strings']['LBL_EMAIL_SHOW_READ']}</button>
129 $str .= $GLOBALS['app_strings']['LBL_EMAIL_SEARCH_NO_RESULTS'];
133 <button onclick="document.location.href='index.php?module=Home&action=UnifiedSearch&search_form=false&advanced=false&query_string={$query_encoded}'">{$GLOBALS['app_strings']['LBL_EMAIL_SHOW_READ']}</button>
142 * Returns the array containing the $searchFields for a module. This function
143 * first checks the default installation directories for the SearchFields.php file and then
144 * loads any custom definition (if found)
146 * @param $moduleName String name of module to retrieve SearchFields entries for
147 * @return array of SearchFields
149 protected static function getSearchFields(
153 $searchFields = array();
155 if(file_exists("modules/{$moduleName}/metadata/SearchFields.php"))
157 require("modules/{$moduleName}/metadata/SearchFields.php");
160 if(file_exists("custom/modules/{$moduleName}/metadata/SearchFields.php"))
162 require("custom/modules/{$moduleName}/metadata/SearchFields.php");
165 return $searchFields;
170 * Get count from query
171 * @param SugarBean $seed
172 * @param string $main_query
174 protected function _getCount($seed, $main_query)
176 // $count_query = $seed->create_list_count_query($main_query);
177 $result = $seed->db->query("SELECT COUNT(*) as c FROM ($main_query) main");
178 $row = $seed->db->fetchByAssoc($result);
179 return isset($row['c'])?$row['c']:0;
183 * Performs the search
185 * @param $query string what we are searching for
186 * @param $modules array modules we are searching in
187 * @param $offset int search result offset
190 protected function _performSearch(
196 if(empty($query)) return array();
199 require_once 'include/SearchForm/SearchForm2.php' ;
201 $searchEmail = preg_match('/^([^%]|%)*@([^%]|%)*$/', $query);
203 $limit = ( !empty($GLOBALS['sugar_config']['max_spotresults_initial']) ? $GLOBALS['sugar_config']['max_spotresults_initial'] : 5 );
205 $limit = ( !empty($GLOBALS['sugar_config']['max_spotresults_more']) ? $GLOBALS['sugar_config']['max_spotresults_more'] : 20 );
207 $totalCounted = empty($GLOBALS['sugar_config']['disable_count_query']);
210 foreach($modules as $moduleName){
211 if (empty($primary_module))
213 $primary_module=$moduleName;
216 $searchFields = SugarSpot::getSearchFields($moduleName);
218 if (empty($searchFields[$moduleName]))
223 $class = $GLOBALS['beanList'][$moduleName];
224 $return_fields = array();
225 $seed = new $class();
226 if(!$seed->ACLAccess('ListView')) continue;
228 if ($class == 'aCase') {
232 foreach($searchFields[$moduleName] as $k=>$v){
234 $searchFields[$moduleName][$k]['value'] = $query;
236 if(!empty($GLOBALS['dictionary'][$class]['unified_search'])){
237 if(empty($GLOBALS['dictionary'][$class]['fields'][$k]['unified_search'])){
239 if(isset($searchFields[$moduleName][$k]['db_field'])){
240 foreach($searchFields[$moduleName][$k]['db_field'] as $field){
241 if(!empty($GLOBALS['dictionary'][$class]['fields'][$field]['unified_search'])){
247 if(strpos($k,'email') === false || !$searchEmail) {
248 unset($searchFields[$moduleName][$k]);
252 if($GLOBALS['dictionary'][$class]['fields'][$k]['type'] == 'int' && !is_numeric($query)) {
253 unset($searchFields[$moduleName][$k]);
256 }else if(empty($GLOBALS['dictionary'][$class]['fields'][$k]) ){
257 //If module did not have unified_search defined, then check the exception for an email search before we unset
258 if(strpos($k,'email') === false || !$searchEmail)
260 unset($searchFields[$moduleName][$k]);
263 switch($GLOBALS['dictionary'][$class]['fields'][$k]['type']){
268 unset($searchFields[$moduleName][$k]);
271 if(!is_numeric($query)) {
272 unset($searchFields[$moduleName][$k]);
279 if (empty($searchFields[$moduleName])) continue;
281 if(isset($seed->field_defs['name'])) {
282 $return_fields['name'] = $seed->field_defs['name'];
285 foreach($seed->field_defs as $k => $v) {
286 if(isset($seed->field_defs[$k]['type']) && ($seed->field_defs[$k]['type'] == 'name') && !isset($return_fields[$k])) {
287 $return_fields[$k] = $seed->field_defs[$k];
291 if(!isset($return_fields['name'])) {
292 // if we couldn't find any name fields, try search fields that have name in it
293 foreach($searchFields[$moduleName] as $k => $v) {
294 if(strpos($k, 'name') != -1 && isset($seed->field_defs[$k])
295 && !isset($seed->field_defs[$k]['source'])) {
296 $return_fields[$k] = $seed->field_defs[$k];
302 if(!isset($return_fields['name'])) {
303 // last resort - any fields that have 'name' in their name
304 foreach($seed->field_defs as $k => $v) {
305 if(strpos($k, 'name') != -1 && isset($seed->field_defs[$k])
306 && !isset($seed->field_defs[$k]['source'])) {
307 $return_fields[$k] = $seed->field_defs[$k];
313 if(!isset($return_fields['name'])) {
314 // FAIL: couldn't find id & name for the module
315 $GLOBALS['log']->error("Unable to find name for module $moduleName");
319 if(isset($return_fields['name']['fields'])) {
320 // some names are composite
321 foreach($return_fields['name']['fields'] as $field) {
322 $return_fields[$field] = $seed->field_defs[$field];
327 $searchForm = new SearchForm ( $seed, $moduleName ) ;
328 $searchForm->setup (array ( $moduleName => array() ) , $searchFields , '' , 'saved_views' /* hack to avoid setup doing further unwanted processing */ ) ;
329 $where_clauses = $searchForm->generateSearchWhere() ;
331 if(empty($where_clauses)) {
334 if(count($where_clauses) > 1) {
335 $query_parts = array();
337 $ret_array_start = $seed->create_new_list_query('', '', $return_fields, array(), 0, '', true, $seed, true);
338 $search_keys = array_keys($searchFields[$moduleName]);
340 foreach($where_clauses as $n => $clause) {
341 $allfields = $return_fields;
342 $skey = $search_keys[$n];
343 if(isset($seed->field_defs[$skey])) {
344 // Joins for foreign fields aren't produced unless the field is in result, hence the merge
345 $allfields[$skey] = $seed->field_defs[$skey];
347 $ret_array = $seed->create_new_list_query('', $clause, $allfields, array(), 0, '', true, $seed, true);
348 $query_parts[] = $ret_array_start['select'] . $ret_array['from'] . $ret_array['where'] . $ret_array['order_by'];
350 $main_query = "(".join(") UNION (", $query_parts).")";
352 foreach($searchFields[$moduleName] as $k=>$v){
353 if(isset($seed->field_defs[$k])) {
354 $return_fields[$k] = $seed->field_defs[$k];
357 $ret_array = $seed->create_new_list_query('', $where_clauses[0], $return_fields, array(), 0, '', true, $seed, true);
358 $main_query = $ret_array['select'] . $ret_array['from'] . $ret_array['where'] . $ret_array['order_by'];
363 $result = $seed->db->query($main_query);
366 $limit = $GLOBALS['sugar_config']['list_max_entries_per_page'];
369 if($offset == 'end') {
370 $totalCount = $this->_getCount($seed, $main_query);
372 $offset = (floor(($totalCount -1) / $limit)) * $limit;
377 $result = $seed->db->limitQuery($main_query, $offset, $limit + 1);
382 while($count < $limit && ($row = $seed->db->fetchByAssoc($result))) {
384 $temp->setupCustomFields($temp->module_dir);
385 $temp->loadFromRow($row);
386 $data[] = $temp->get_list_view_data($return_fields);
394 if($count >= $limit) {
395 $nextOffset = $offset + $limit;
399 $prevOffset = $offset - $limit;
400 if($prevOffset < 0) $prevOffset = 0;
403 if( $count >= $limit && $totalCounted){
404 if(!isset($totalCount)) {
405 $totalCount = $this->_getCount($seed, $main_query);
408 $totalCount = $count + $offset;
411 $pageData['offsets'] = array( 'current'=>$offset, 'next'=>$nextOffset, 'prev'=>$prevOffset, 'end'=>$endOffset, 'total'=>$totalCount, 'totalCounted'=>$totalCounted);
412 $pageData['bean'] = array('objectName' => $seed->object_name, 'moduleDir' => $seed->module_dir);
414 $results[$moduleName] = array("data" => $data, "pageData" => $pageData);
420 * Function used to walk the array and find keys that map the queried string.
421 * if both the pattern and module name is found the promote the string to thet top.
423 protected function _searchKeys(
429 //make the module name singular....
430 if ($patterns[1][strlen($patterns[1])-1] == 's') {
431 $patterns[1]=substr($patterns[1],0,(strlen($patterns[1])-1));
434 $module_exists = stripos($key,$patterns[1]); //primary module name.
435 $pattern_exists = stripos($key,$patterns[0]); //pattern provided by the user.
436 if ($module_exists !== false and $pattern_exists !== false) {
437 $GLOBALS['matching_keys']= array_merge(array(array('NAME'=>$key, 'ID'=>$key, 'VALUE'=>$item1)),$GLOBALS['matching_keys']);
440 if ($pattern_exists !== false) {
441 $GLOBALS['matching_keys'][]=array('NAME'=>$key, 'ID'=>$key, 'VALUE'=>$item1);