Index: branches/5.0.x/core/units/categories/categories_event_handler.php =================================================================== diff -u -N -r12635 -r12666 --- branches/5.0.x/core/units/categories/categories_event_handler.php (.../categories_event_handler.php) (revision 12635) +++ branches/5.0.x/core/units/categories/categories_event_handler.php (.../categories_event_handler.php) (revision 12666) @@ -1,6 +1,6 @@ 'OnSimpleSearch', + 'subsearch' => 'OnSubSearch', + 'advanced' => 'OnAdvancedSearch' + ); + + $type = $this->Application->GetVar('search_type', 'simple'); + + if ($keywords = $event->getEventParam('keyword_string')) { + // processing keyword_string param of ListProducts tag + $this->Application->SetVar('keywords', $keywords); + $type = 'simple'; + } + + $search_event = $event_mapping[$type]; + $this->$search_event($event); + + $search_table = TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_'.TABLE_PREFIX.'Search'; + $sql = 'SHOW TABLES LIKE "'.$search_table.'"'; + + if ($this->Conn->Query($sql)) { + $search_res_ids = $this->Conn->GetCol('SELECT ResourceId FROM '.$search_table); + } + + if (isset($search_res_ids) && $search_res_ids) { + $type_clauses['search']['include'] = '%1$s.ResourceId IN ('.implode(',', $search_res_ids).')'; + $type_clauses['search']['except'] = '%1$s.ResourceId NOT IN ('.implode(',', $search_res_ids).')'; + } + else { + $type_clauses['search']['include'] = '0'; + $type_clauses['search']['except'] = '1'; + } + + $type_clauses['search']['having_filter'] = false; + } + $search_helper =& $this->Application->recallObject('SearchHelper'); /* @var $search_helper kSearchHelper */ @@ -1993,4 +2030,277 @@ $this->Application->StoreVar('_editor_preview_content_', $string); } + + /** + * Makes simple search for categories + * based on keywords string + * + * @param kEvent $event + */ + function OnSimpleSearch(&$event) + { + $event->redirect = false; + $search_table = TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_'.TABLE_PREFIX.'Search'; + + $keywords = unhtmlentities( trim($this->Application->GetVar('keywords')) ); + + $query_object =& $this->Application->recallObject('HTTPQuery'); + $sql = 'SHOW TABLES LIKE "'.$search_table.'"'; + + if(!isset($query_object->Get['keywords']) && + !isset($query_object->Post['keywords']) && + $this->Conn->Query($sql)) + { + return; // used when navigating by pages or changing sorting in search results + } + if(!$keywords || strlen($keywords) < $this->Application->ConfigValue('Search_MinKeyword_Length')) + { + $this->Conn->Query('DROP TABLE IF EXISTS '.$search_table); + $this->Application->SetVar('keywords_too_short', 1); + return; // if no or too short keyword entered, doing nothing + } + + $this->Application->StoreVar('keywords', $keywords); + + $this->saveToSearchLog($keywords, 0); // 0 - simple search, 1 - advanced search + + $keywords = strtr($keywords, Array('%' => '\\%', '_' => '\\_')); + + $event->setPseudoClass('_List'); + + $object =& $event->getObject(); + /* @var $object kDBList */ + + $this->Application->SetVar($event->getPrefixSpecial().'_Page', 1); + $lang = $this->Application->GetVar('m_lang'); + $items_table = $this->Application->getUnitOption($event->Prefix, 'TableName'); + $module_name = 'In-Portal'; + + $sql = 'SELECT * + FROM ' . $this->Application->getUnitOption('confs', 'TableName') . ' + WHERE ModuleName = ' . $this->Conn->qstr($module_name) . ' AND SimpleSearch = 1'; + $search_config = $this->Conn->Query($sql, 'FieldName'); + + $field_list = array_keys($search_config); + + $join_clauses = Array(); + + // field processing + $weight_sum = 0; + + $alias_counter = 0; + + $custom_fields = $this->Application->getUnitOption($event->Prefix, 'CustomFields'); + if ($custom_fields) { + $custom_table = $this->Application->getUnitOption($event->Prefix.'-cdata', 'TableName'); + $join_clauses[] = ' LEFT JOIN '.$custom_table.' custom_data ON '.$items_table.'.ResourceId = custom_data.ResourceId'; + } + + // what field in search config becomes what field in sql (key - new field, value - old field (from searchconfig table)) + $search_config_map = Array(); + + foreach ($field_list as $key => $field) { + $options = $object->getFieldOptions($field); + $local_table = TABLE_PREFIX.$search_config[$field]['TableName']; + $weight_sum += $search_config[$field]['Priority']; // counting weight sum; used when making relevance clause + + // processing multilingual fields + if (getArrayValue($options, 'formatter') == 'kMultiLanguage') { + $field_list[$key.'_primary'] = 'l'.$this->Application->GetDefaultLanguageId().'_'.$field; + $field_list[$key] = 'l'.$lang.'_'.$field; + + if (!isset($search_config[$field]['ForeignField'])) { + $field_list[$key.'_primary'] = $local_table.'.'.$field_list[$key.'_primary']; + $search_config_map[ $field_list[$key.'_primary'] ] = $field; + } + } + + // processing fields from other tables + if ($foreign_field = $search_config[$field]['ForeignField']) { + $exploded = explode(':', $foreign_field, 2); + if ($exploded[0] == 'CALC') { + // ignoring having type clauses in simple search + unset($field_list[$key]); + continue; + } + else { + $multi_lingual = false; + if ($exploded[0] == 'MULTI') { + $multi_lingual = true; + $foreign_field = $exploded[1]; + } + + $exploded = explode('.', $foreign_field); // format: table.field_name + $foreign_table = TABLE_PREFIX.$exploded[0]; + + $alias_counter++; + $alias = 't'.$alias_counter; + + if ($multi_lingual) { + $field_list[$key] = $alias.'.'.'l'.$lang.'_'.$exploded[1]; + $field_list[$key.'_primary'] = 'l'.$this->Application->GetDefaultLanguageId().'_'.$field; + $search_config_map[ $field_list[$key] ] = $field; + $search_config_map[ $field_list[$key.'_primary'] ] = $field; + } + else { + $field_list[$key] = $alias.'.'.$exploded[1]; + $search_config_map[ $field_list[$key] ] = $field; + } + + $join_clause = str_replace('{ForeignTable}', $alias, $search_config[$field]['JoinClause']); + $join_clause = str_replace('{LocalTable}', $items_table, $join_clause); + + $join_clauses[] = ' LEFT JOIN '.$foreign_table.' '.$alias.' + ON '.$join_clause; + } + } + else { + // processing fields from local table + if ($search_config[$field]['CustomFieldId']) { + $local_table = 'custom_data'; + + // search by custom field value on current language + $custom_field_id = array_search($field_list[$key], $custom_fields); + $field_list[$key] = 'l'.$lang.'_cust_'.$custom_field_id; + + // search by custom field value on primary language + $field_list[$key.'_primary'] = $local_table.'.l'.$this->Application->GetDefaultLanguageId().'_cust_'.$custom_field_id; + $search_config_map[ $field_list[$key.'_primary'] ] = $field; + } + + $field_list[$key] = $local_table.'.'.$field_list[$key]; + $search_config_map[ $field_list[$key] ] = $field; + } + } + + // keyword string processing + $search_helper =& $this->Application->recallObject('SearchHelper'); + /* @var $search_helper kSearchHelper */ + + $where_clause = Array (); + foreach ($field_list as $field) { + if (preg_match('/^' . preg_quote($items_table, '/') . '\.(.*)/', $field, $regs)) { + // local real field + $filter_data = $search_helper->getSearchClause($object, $regs[1], $keywords, false); + if ($filter_data) { + $where_clause[] = $filter_data['value']; + } + } + elseif (preg_match('/^custom_data\.(.*)/', $field, $regs)) { + $custom_field_name = 'cust_' . $search_config_map[$field]; + $filter_data = $search_helper->getSearchClause($object, $custom_field_name, $keywords, false); + if ($filter_data) { + $where_clause[] = str_replace('`' . $custom_field_name . '`', $field, $filter_data['value']); + } + } + else { + $where_clause[] = $search_helper->buildWhereClause($keywords, Array ($field)); + } + } + + $where_clause = '(' . implode(') OR (', $where_clause) . ')'; + + $where_clause = $where_clause.' AND '.$items_table.'.Status=1'; + + if ($event->MasterEvent && $event->MasterEvent->Name == 'OnListBuild') { + if ($event->MasterEvent->getEventParam('ResultIds')) { + $where_clause .= ' AND '.$items_table.'.ResourceId IN ('.implode(',', $event->MasterEvent->getEventParam('ResultIds')).')'; + } + } + + // making relevance clause + $positive_words = $search_helper->getPositiveKeywords($keywords); + $this->Application->StoreVar('highlight_keywords', serialize($positive_words)); + $revelance_parts = Array(); + reset($search_config); + + foreach ($positive_words as $keyword_index => $positive_word) { + $positive_words[$keyword_index] = mysql_real_escape_string($positive_word); + } + + foreach ($field_list as $field) { + + if (!array_key_exists($field, $search_config_map)) { + $map_key = $search_config_map[$items_table . '.' . $field]; + } + else { + $map_key = $search_config_map[$field]; + } + + $config_elem = $search_config[ $map_key ]; + $weight = $config_elem['Priority']; + $revelance_parts[] = 'IF('.$field.' LIKE "%'.implode(' ', $positive_words).'%", '.$weight_sum.', 0)'; + foreach ($positive_words as $keyword) { + $revelance_parts[] = 'IF('.$field.' LIKE "%'.$keyword.'%", '.$weight.', 0)'; + } + } + + $revelance_parts = array_unique($revelance_parts); + + $conf_postfix = $this->Application->getUnitOption($event->Prefix, 'SearchConfigPostfix'); + $rel_keywords = $this->Application->ConfigValue('SearchRel_Keyword_'.$conf_postfix) / 100; + $rel_pop = $this->Application->ConfigValue('SearchRel_Pop_'.$conf_postfix) / 100; + $rel_rating = $this->Application->ConfigValue('SearchRel_Rating_'.$conf_postfix) / 100; + $relevance_clause = '('.implode(' + ', $revelance_parts).') / '.$weight_sum.' * '.$rel_keywords; + if ($rel_pop && isset($object->Fields['Hits'])) { + $relevance_clause .= ' + (Hits + 1) / (MAX(Hits) + 1) * '.$rel_pop; + } + if ($rel_rating && isset($object->Fields['CachedRating'])) { + $relevance_clause .= ' + (CachedRating + 1) / (MAX(CachedRating) + 1) * '.$rel_rating; + } + + // building final search query + if (!$this->Application->GetVar('do_not_drop_search_table')) { + $this->Conn->Query('DROP TABLE IF EXISTS '.$search_table); // erase old search table if clean k4 event + $this->Application->SetVar('do_not_drop_search_table', true); + } + + + $search_table_exists = $this->Conn->Query('SHOW TABLES LIKE "'.$search_table.'"'); + if ($search_table_exists) { + $select_intro = 'INSERT INTO '.$search_table.' (Relevance, ItemId, ResourceId, ItemType, EdPick) '; + } + else { + $select_intro = 'CREATE TABLE '.$search_table.' AS '; + } + + $edpick_clause = $this->Application->getUnitOption($event->Prefix.'.EditorsPick', 'Fields') ? $items_table.'.EditorsPick' : '0'; + + + $sql = $select_intro.' SELECT '.$relevance_clause.' AS Relevance, + '.$items_table.'.'.$this->Application->getUnitOption($event->Prefix, 'IDField').' AS ItemId, + '.$items_table.'.ResourceId, + '.$this->Application->getUnitOption($event->Prefix, 'ItemType').' AS ItemType, + '.$edpick_clause.' AS EdPick + FROM '.$object->TableName.' + '.implode(' ', $join_clauses).' + WHERE '.$where_clause.' + GROUP BY '.$items_table.'.'.$this->Application->getUnitOption($event->Prefix, 'IDField'); + + $res = $this->Conn->Query($sql); + } + + /** + * Make record to search log + * + * @param string $keywords + * @param int $search_type 0 - simple search, 1 - advanced search + */ + function saveToSearchLog($keywords, $search_type = 0) + { + // don't save keywords for each module separately, just one time + // static variable can't help here, because each module uses it's own class instance ! + if (!$this->Application->GetVar('search_logged')) { + $sql = 'UPDATE '.TABLE_PREFIX.'SearchLog + SET Indices = Indices + 1 + WHERE Keyword = '.$this->Conn->qstr($keywords).' AND SearchType = '.$search_type; // 0 - simple search, 1 - advanced search + $this->Conn->Query($sql); + if ($this->Conn->getAffectedRows() == 0) { + $fields_hash = Array('Keyword' => $keywords, 'Indices' => 1, 'SearchType' => $search_type); + $this->Conn->doInsert($fields_hash, TABLE_PREFIX.'SearchLog'); + } + + $this->Application->SetVar('search_logged', 1); + } + } } \ No newline at end of file