/** * Get all collections and cache them * * @access public * @param int Channel ID * @return array */ public function get_all() { static $all; // Get all from parent class if (is_null($all)) { ee()->db->order_by('collection_label', 'asc'); $all = parent::get_all(); foreach ($all as &$row) { $row['settings'] = low_search_decode($row['settings'], FALSE); } $all = low_associate_results($all, 'collection_id'); } return $all; }
/** * View search log * * @access public * @return string */ public function search_log() { // Keep track of this URL $log_url = $this->mcp_url('search_log'); $filtered = FALSE; // -------------------------------------- // Check if filter form was posted // -------------------------------------- if ($filter = ee()->input->post('filter')) { if ($filter = array_filter($filter, 'low_not_empty')) { $log_url .= AMP . 'filter=' . low_search_encode($filter); } // Go to same url with encoded filter in GET ee()->functions->redirect($log_url); } // -------------------------------------- // Check if there's a GET filter // -------------------------------------- if ($filter = ee()->input->get('filter')) { $log_url .= AMP . 'filter=' . $filter; $filter = low_search_decode($filter); $filtered = TRUE; } if (!is_array($filter)) { $filter = array(); } // Add site id to it $filter['site_id'] = $this->site_id; // -------------------------------------- // Populate filters // -------------------------------------- // Get all unique members $members = ee()->db->select('m.member_id, m.screen_name')->from('members m')->join('low_search_log l', 'm.member_id = l.member_id', '')->where('l.site_id', $this->site_id)->group_by('m.member_id')->order_by('m.screen_name', 'asc')->get()->result_array(); $members = low_flatten_results($members, 'screen_name', 'member_id'); $this->data['members'] = $members; // Get all days of searching $dates = ee()->db->select("DISTINCT(FROM_UNIXTIME(search_date, '%Y-%m-%d')) AS search_date", FALSE)->from('low_search_log')->where('site_id', $this->site_id)->order_by('search_date', 'desc')->get()->result_array(); $dates = low_flatten_results($dates, 'search_date'); $this->data['dates'] = $dates; // -------------------------------------- // Get total rows of log // -------------------------------------- $total = ee()->low_search_log_model->get_filtered_count($filter); // Prune now? if (($search_log_size = ee()->low_search_settings->get('search_log_size')) !== '0' and $total > $search_log_size) { ee()->low_search_log_model->prune($this->site_id, $search_log_size); $total = $search_log_size; } // -------------------------------------- // Get start row // -------------------------------------- if (($start = ee()->input->get('start')) === FALSE) { $start = 0; } // -------------------------------------- // Load pagination class, if necessary // -------------------------------------- if ($total > self::VIEW_LOG_LIMIT) { ee()->load->library('pagination'); // Pagination link template $pagi_link = '<img src="' . ee()->cp->cp_theme_url . 'images/pagination_%s_button.gif" width="13" height="13" alt="%s" />'; // Set pagination parameters ee()->pagination->initialize(array('base_url' => $log_url, 'total_rows' => $total, 'per_page' => self::VIEW_LOG_LIMIT, 'page_query_string' => TRUE, 'query_string_segment' => 'start', 'full_tag_open' => '<span>', 'full_tag_close' => '</span>', 'prev_link' => sprintf($pagi_link, 'prev', '←'), 'next_link' => sprintf($pagi_link, 'next', '→'), 'first_link' => sprintf($pagi_link, 'first', '&ldarr;'), 'last_link' => sprintf($pagi_link, 'last', '&rdarr;'))); // Create the links $this->data['pagination'] = ee()->pagination->create_links(); } else { $this->data['pagination'] = FALSE; } // -------------------------------------- // Get search log // -------------------------------------- // pagination ee()->db->order_by('search_date', 'desc'); ee()->db->limit(self::VIEW_LOG_LIMIT, $start); $log = ee()->low_search_log_model->get_filtered_rows($filter); if ($log) { // -------------------------------------- // Set pagination details // -------------------------------------- $this->data['viewing_rows'] = sprintf(lang('viewing_rows'), $start + 1, ($to = $start + self::VIEW_LOG_LIMIT) > $total ? $total : $to, $total); // -------------------------------------- // Shortcut URL // -------------------------------------- $shortcut_url = $this->mcp_url('edit_shortcut', 'shortcut_id=new&log_id=%s'); // -------------------------------------- // Modify rows // -------------------------------------- foreach ($log as &$row) { // Display a nice date $row['search_date'] = $this->_human_time($row['search_date']); // Account for guests $row['member_id'] = isset($members[$row['member_id']]) ? $members[$row['member_id']] : ''; // Parameters $row['parameters'] = low_search_decode($row['parameters'], FALSE); // Shortcut URL $row['shortcut_url'] = sprintf($shortcut_url, $row['log_id']); } } // -------------------------------------- // Add log to data array // -------------------------------------- $this->data['log'] = $log; $this->data['is_admin'] = $this->member_group == 1; $this->data['filter'] = $filter; $this->data['filtered'] = $filtered; // -------------------------------------- // Set title and breadcrumb and view page // -------------------------------------- $this->_set_cp_var('cp_page_title', lang('view_search_log')); ee()->cp->set_breadcrumb($this->mcp_url(), lang('low_search_module_name')); return $this->view('mcp_list_search_log'); }
/** * Builds an index for a given collection and entry * * @access public * @param array * @param array * @return void */ public function build($collection = array(), $entry = array()) { // -------------------------------------- // Check parameters // -------------------------------------- $collection_id = $collection['collection_id']; $settings = $collection['settings']; if (!is_array($settings)) { $settings = low_search_decode($settings, FALSE); } $settings = array_filter($settings); $entry_id = $entry['entry_id']; // -------------------------------------- // Load addon/fieldtype files // -------------------------------------- ee()->load->library('addons'); // Include EE Fieldtype class if (!class_exists('EE_Fieldtype')) { include_once APPPATH . 'fieldtypes/EE_Fieldtype' . EXT; } // -------------------------------------- // Initiate fieldtypes var // -------------------------------------- static $fieldtypes; // Init field ids $field_ids = array(); // Set fieldtypes if ($fieldtypes === NULL) { $fieldtypes = ee()->addons->get_installed('fieldtypes'); } // -------------------------------------- // Get the field ids for these settings, minus title // -------------------------------------- foreach (array_filter(array_keys($settings)) as $field_id) { if (is_numeric($field_id)) { $field_ids[] = $field_id; } } // -------------------------------------- // Check for ids we haven't dealt with yet // -------------------------------------- if ($not_encountered = array_diff($field_ids, array_keys($this->fields))) { // Get the details for given fields $query = ee()->db->select()->from('channel_fields')->where_in('field_id', $not_encountered)->get(); foreach ($query->result_array() as $row) { // Shortcut to fieldtype $ftype = $fieldtypes[$row['field_type']]; // Include the file if it doesn't yet exist if (!class_exists($ftype['class'])) { require $ftype['path'] . $ftype['file']; } // Record it so we don't query again $this->fields[$row['field_id']] = TRUE; // Only initiate the fieldtypes that have the necessary method // Either low_search_index or third_party_search_index if (method_exists($ftype['class'], 'low_search_index') || method_exists($ftype['class'], 'third_party_search_index')) { // Initiate this fieldtype $this->fields[$row['field_id']] = new $ftype['class'](); // Decode settings if ($field_settings = @unserialize(base64_decode($row['field_settings']))) { $row = array_merge($row, $field_settings); } // Add details to settings $row['low_search_collection_id'] = $collection_id; $row['field_name'] = 'field_id_' . $row['field_id']; $row['search_index_method'] = (method_exists($ftype['class'], 'low_search_index') ? 'low' : 'third_party') . '_search_index'; // Set this instance's settings $this->fields[$row['field_id']]->settings = $row; } } } // -------------------------------------- // Init text array which will contain the index // -------------------------------------- $text = array(); // -------------------------------------- // Loop through settings and add weight to field by repeating string // -------------------------------------- foreach ($settings as $field_id => $field_weight) { // -------------------------------------- // Determine proper field id name // -------------------------------------- $field_name = is_numeric($field_id) ? 'field_id_' . $field_id : $field_id; // -------------------------------------- // Check fieldtype // -------------------------------------- if (array_key_exists($field_name, $entry)) { if (array_key_exists($field_id, $this->fields) && is_object($this->fields[$field_id])) { // Update entry id for this fieldtype $this->fields[$field_id]->settings['entry_id'] = $entry_id; // Which method should we use $method = $this->fields[$field_id]->settings['search_index_method']; // If fieldtype exists, it will have the correct method, so call that $str = $this->fields[$field_id]->{$method}($entry[$field_name]); } else { // If it doesn't, just use the raw data $str = $entry[$field_name]; } } // -------------------------------------- // If we have a string or array of strings, // add it to the text array // -------------------------------------- if (!empty($str)) { // Force output into an array if (!is_array($str)) { $str = array($str); } // And clean/filter it $str = array_filter(array_map('low_clean_string', $str)); // Then add each line to text foreach ($str as $s) { // Create pipe-separated weighted string $text[] = trim(self::WEIGHT_SEPARATOR . str_repeat($s . self::WEIGHT_SEPARATOR, $field_weight)); } } } // -------------------------------------- // Keep track of collection and their site ids // -------------------------------------- if (!isset($this->collections[$collection_id])) { $this->collections[$collection_id] = isset($collection['site_id']) ? $collection['site_id'] : $this->site_id; } // -------------------------------------- // Data to insert // -------------------------------------- $data = array('collection_id' => $collection_id, 'entry_id' => $entry_id, 'site_id' => $this->collections[$collection_id], 'index_text' => implode("\n", $text), 'index_date' => ee()->localize->now); // -------------------------------------- // 'low_search_update_index' hook // - Add additional attributes to the index // -------------------------------------- if (ee()->extensions->active_hook('low_search_update_index') === TRUE) { $ext_data = ee()->extensions->call('low_search_update_index', $data, $entry); if (is_array($ext_data) && !empty($ext_data)) { $data = array_merge($data, $ext_data); } } // -------------------------------------- // Get insert sql // -------------------------------------- $sql = ee()->db->insert_string($this->table(), $data); // -------------------------------------- // Change insert to replace to update existing entry // -------------------------------------- ee()->db->query(preg_replace('/^INSERT/', 'REPLACE', $sql)); }
/** * Update routines for version 2.1.0 * * @access private * @return void */ private function _v210() { // Fields to add to the DB $fields = array('modifier' => 'decimal(2,1) unsigned NOT NULL default 1.0', 'excerpt' => 'int(6) unsigned NOT NULL default 0'); // Template query $tmpl = 'ALTER TABLE `%s` ADD `%s` %s AFTER `collection_label`'; $tbl = ee()->low_search_collection_model->table(); // Add fields foreach ($fields as $field => $properties) { ee()->db->query(sprintf($tmpl, $tbl, $field, $properties)); } // Get the collections and re-do the settings foreach (ee()->low_search_collection_model->get_all() as $row) { // Initiate data array $data = array(); // Decode the settings $settings = low_search_decode($row['settings'], FALSE); // Set new property values $data['modifier'] = (double) (isset($settings['modifier']) ? $settings['modifier'] : 1.0); $data['excerpt'] = (int) (isset($settings['excerpt']) ? $settings['excerpt'] : 0); // Remove these properties from settings unset($settings['modifier'], $settings['excerpt']); // filter it $settings = array_filter($settings); // Encode the new settings for DB usage $data['settings'] = low_search_encode($settings, FALSE); // Update row ee()->low_search_collection_model->update($row['collection_id'], $data); } }
/** * Validate given array * * @access public * @param array * @return mixed */ public function validate($data) { // Reset errors $this->_errors = array(); // -------------------------------------- // Trim input // -------------------------------------- $data = array_map('trim', $data); // -------------------------------------- // Validate saved_id // -------------------------------------- if (empty($data['shortcut_id']) || !is_numeric($data['shortcut_id'])) { $data['shortcut_id'] = NULL; } // -------------------------------------- // Validate group_id // -------------------------------------- if (empty($data['group_id']) || !is_numeric($data['group_id'])) { $this->_errors['shortcut_invalid_group']; } // -------------------------------------- // Validate parameters // -------------------------------------- if (!empty($data['parameters'])) { // String, but not json if (is_string($data['parameters']) && substr($data['parameters'], 0, 1) != '{') { $data['parameters'] = low_search_decode($data['parameters']); } // Convert array to json if (is_array($data['parameters'])) { $data['parameters'] = low_search_encode($data['parameters'], FALSE); } // If something went wrong, skip it if (empty($data['parameters'])) { $this->_errors[] = 'shortcut_invalid_params'; } } else { $this->_errors[] = 'shortcut_no_params'; } // -------------------------------------- // Validate name // -------------------------------------- if (!empty($data['shortcut_name'])) { // shortcut_name should be url-safe if (preg_match('/^[\\w-]+$/', $data['shortcut_name'])) { // shortcut_name should be unique $query = ee()->db->from($this->table())->where('shortcut_name', $data['shortcut_name']); // Exclude this row if ($data['shortcut_id']) { ee()->db->where('shortcut_id !=', $data['shortcut_id']); } // Check it if ($query->count_all_results()) { $this->_errors[] = 'shortcut_name_not_available'; } } else { $this->_errors[] = 'shortcut_invalid_name'; } } else { $this->_errors[] = 'shortcut_no_name'; } // -------------------------------------- // Validate Label; fall back to name // -------------------------------------- if (empty($data['shortcut_label'])) { $data['shortcut_label'] = $data['shortcut_name']; } // -------------------------------------- // Return modified data if valid; FALSE if invalid // -------------------------------------- return empty($this->_errors) ? $data : FALSE; }
/** * Catch search form submission * * @access public * @return void */ public function catch_search() { // -------------------------------------- // Initiate data array; will be encrypted // and put in the URI later // -------------------------------------- $data = array(); if ($params = ee()->input->post('params')) { $data = low_search_decode($params); } // -------------------------------------- // Check other data // -------------------------------------- foreach (array_merge($_GET, $_POST) as $key => $val) { // Keys to skip if (in_array($key, array('ACT', 'XID', 'csrf_token', 'params', 'site_id'))) { continue; } // Add post var to data $data[$key] = is_array($val) ? implode('|', array_filter($val, 'low_not_empty')) : $val; } // -------------------------------------- // Clean up the data array // -------------------------------------- $data = array_filter($data, 'low_not_empty'); // -------------------------------------- // 'low_search_catch_search' extension hook // - Check incoming data and optionally change it // -------------------------------------- if (ee()->extensions->active_hook('low_search_catch_search') === TRUE) { $data = ee()->extensions->call('low_search_catch_search', $data); if (ee()->extensions->end_script === TRUE) { return; } // Clean again to be sure $data = array_filter($data, 'low_not_empty'); } // -------------------------------------- // Check for required parameter // -------------------------------------- if (isset($data['required'])) { // Init errors $errors = array(); // Get required as array list($required, $in) = low_explode_param($data['required']); foreach ($required as $req) { // Break out when empty // @TODO: enhance for multiple fields if (empty($data[$req])) { $errors[] = $req; } } // Go back if ($errors) { ee()->session->set_flashdata('errors', $errors); $this->_go_back('fields_missing'); } // remove from data unset($data['required']); } // -------------------------------------- // Optionally log search query // -------------------------------------- $this->_log_search($data); // -------------------------------------- // Result URI: result page & cleaned up data, encoded // -------------------------------------- $url = $this->_create_url($data, '&'); // -------------------------------------- // Redirect to result page // -------------------------------------- // Empty out flashdata to avoid serving of JSON for ajax request if (AJAX_REQUEST && count(ee()->session->flashdata)) { ee()->session->flashdata = array(); } ee()->functions->redirect($url); }
/** * Read all parameters from query param or GET * * @access private * @return void */ private function _set_all() { // -------------------------------------- // Reset // -------------------------------------- $this->_params = array(); $this->_query = NULL; // -------------------------------------- // Check for given query // -------------------------------------- if (ee()->low_search_settings->get('encode_query') == 'y') { // Read the query parameter $query_param = ee()->TMPL->fetch_param('query'); // If query is given (not FALSE or empty string), try and decode it // Also ignore pagination segment if (!empty($query_param) && !preg_match('/^P\\d+$/', $query_param)) { $query_val = low_search_decode($query_param); $this->_query = empty($query_val) ? FALSE : $query_val; } } else { // Or else get it from the GET vars foreach ($_GET as $key => $val) { // Skip arrays if (is_array($val)) { continue; } // Strip slashes if < PHP 5.4 if (version_compare(PHP_VERSION, '5.4', '<')) { $val = stripslashes($val); } $this->_query[$key] = $val; } } // -------------------------------------- // Combine query with default custom parameters // -------------------------------------- if (is_array($this->_query)) { $this->_params = $this->_query; } // -------------------------------------- // Remember current tagparams // -------------------------------------- if (is_array(ee()->TMPL->tagparams)) { $this->_tagparams = ee()->TMPL->tagparams; // but not the query param unset($this->_tagparams['query']); } // -------------------------------------- // Also set the site IDs // -------------------------------------- $this->_set_site_ids(); }
/** * Build collection index * * @access protected * @return array */ public function build($collection_id = FALSE, $entry_ids = FALSE, $start = FALSE) { // -------------------------------------- // Check for collection_id or entry_id // -------------------------------------- $collection_id = $collection_id !== FALSE ? $collection_id : ee()->input->get_post('collection_id'); $entry_ids = $entry_ids !== FALSE ? $entry_ids : ee()->input->get_post('entry_id'); // -------------------------------------- // Either collection_id or entry_id or both must be given // -------------------------------------- if (!($collection_id || $entry_ids)) { show_error(ee()->lang->line('not_authorized')); } // -------------------------------------- // Start building query to get collection details // -------------------------------------- ee()->db->select('lsc.collection_id, lsc.channel_id, lsc.settings, lsc.site_id'); ee()->db->from('low_search_collections lsc'); // -------------------------------------- // If there's a collection id, limit query by that one // -------------------------------------- if ($collection_id) { ee()->db->where('lsc.collection_id', $collection_id); } // -------------------------------------- // If there's an entry_id, limit query by those // -------------------------------------- if ($entry_ids) { // Force array if (!is_array($entry_ids)) { $entry_ids = preg_split('/\\D+/', $entry_ids); } // Get collections for given entries ee()->db->select('GROUP_CONCAT(ct.entry_id) AS entries'); ee()->db->join('channel_titles ct', 'lsc.channel_id = ct.channel_id'); ee()->db->where_in('entry_id', $entry_ids); ee()->db->group_by('lsc.collection_id'); } // -------------------------------------- // Execute query and get results. Bail out if none // -------------------------------------- if (!($collections = ee()->db->get()->result_array())) { return FALSE; } $collections = low_associate_results($collections, 'collection_id'); $channel_ids = array_unique(low_flatten_results($collections, 'channel_id')); // -------------------------------------- // Get batch size // -------------------------------------- $batch_size = ee()->low_search_settings->get('batch_size'); // -------------------------------------- // Get total number of entries that need to be indexed // -------------------------------------- if ($entry_ids) { $num_entries = count($entry_ids); } else { ee()->db->where_in('channel_id', $channel_ids); $num_entries = ee()->db->count_all_results('channel_titles'); } // -------------------------------------- // Get weighted field settings only, keep track of field ids // -------------------------------------- $fields = array(); $entries = array(); foreach ($collections as &$col) { $col['settings'] = array_filter(low_search_decode($col['settings'], FALSE)); // Add field ids to fields array $fields = array_merge($fields, array_keys($col['settings'])); if (isset($col['entries'])) { foreach (explode(',', $col['entries']) as $eid) { $entries[$eid][] = $col['collection_id']; } } } // Get rid of duplicate field ids $fields = array_unique($fields); sort($fields); // -------------------------------------- // Let an extension take over? // -------------------------------------- if (ee()->extensions->active_hook('low_search_get_index_entries') === TRUE) { $index_entries = ee()->extensions->call('low_search_get_index_entries', $fields, $channel_ids, $entry_ids, $start, $batch_size); } else { // -------------------------------------- // Create select list // -------------------------------------- $select = array('t.entry_id', 't.channel_id'); foreach ($fields as $field_id) { // Skip non-numeric settings if (!is_numeric($field_id)) { continue; } $select[] = $field_id == '0' ? 't.title AS field_id_0' : 'd.field_id_' . $field_id; } // -------------------------------------- // Start building query // -------------------------------------- ee()->db->select($select)->from('channel_titles t')->join('channel_data d', 't.entry_id = d.entry_id', 'inner')->where_in('t.channel_id', $channel_ids)->order_by('entry_id', 'asc'); // -------------------------------------- // Optional: Limit to given entries // -------------------------------------- if ($entry_ids) { ee()->db->where_in('t.entry_id', $entry_ids); } // -------------------------------------- // Optional: Limit entries by batch size // -------------------------------------- if ($start !== FALSE && is_numeric($start)) { ee()->db->limit($batch_size, $start); } // -------------------------------------- // Query it! // -------------------------------------- $query = ee()->db->get(); // Make sure the rows are keyed by their entry_id $index_entries = low_associate_results($query->result_array(), 'entry_id'); // -------------------------------------- // Get category info for these entries // -------------------------------------- if ($entry_cats = $this->get_entry_categories(array_keys($index_entries))) { // add the categories to the index_entries rows foreach ($entry_cats as $entry_id => $cats) { $index_entries[$entry_id] += $cats; } } } // -------------------------------------- // Loop thru the entries to index // -------------------------------------- foreach ($index_entries as $row) { // If it's a given entry, loop thru its collections and rebuild index if (isset($entries[$row['entry_id']])) { foreach ($entries[$row['entry_id']] as $col_id) { // Collection details $col = $collections[$col_id]; // Build index for this entry/collection combo ee()->low_search_index_model->build($col, $row); } } else { foreach ($collections as $col_id => $col) { if ($row['channel_id'] == $col['channel_id']) { ee()->low_search_index_model->build($col, $row); } } } } // Determine new start $new_start = $start + $batch_size; // Are we done? $done = $new_start >= $num_entries; // -------------------------------------- // Prep response // -------------------------------------- $response = array('status' => $done ? 'done' : 'building', 'start' => (int) $new_start, 'total_entries' => (int) $num_entries, 'processed' => count($index_entries)); return $response; }