* @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 * @category Stud.IP * @since 4.1 */ class GlobalSearchController extends AuthenticatedController { public function before_filter(&$action, &$args) { parent::before_filter($action, $args); if (in_array($action, ['settings', 'saveconfig'])) { $GLOBALS['perm']->check('root'); } } /** * Perform search in all registered modules for the given search term. */ public function find_action($limit) { $limit = min(100, (int)$limit); // Perform search by mysqli (=async) or by PDO (=sync)? $async = Config::get()->GLOBALSEARCH_ASYNC_QUERIES && extension_loaded('mysqli'); if ($async) { // throw exceptions on mysqli error $driver = new mysqli_driver(); $driver->report_mode = MYSQLI_REPORT_ERROR; } // Now load all modules $modules = GlobalSearchModule::getActiveSearchModules(); $search = trim(Request::get('search')); $filter = json_decode(Request::get('filters'), true); $result = []; foreach ($modules as $className) { $partSQL = $className::getSQL($search, $filter, $limit); // No valid sql? Leave. if (!$partSQL) { continue; } // Global config setting says to use mysqli if ($async) { $mysqli = new mysqli($GLOBALS['DB_STUDIP_HOST'], $GLOBALS['DB_STUDIP_USER'], $GLOBALS['DB_STUDIP_PASSWORD'], $GLOBALS['DB_STUDIP_DATABASE']); mysqli_set_charset($mysqli, 'UTF8'); if ($mysqli->multi_query($partSQL . '; SELECT FOUND_ROWS() as found_rows;')) { do { if ($res = $mysqli->store_result()) { $all_links[$className][] = $res->fetch_all(MYSQLI_ASSOC); $res->free(); } } while ($mysqli->more_results() && $mysqli->next_result()); } $entries = $all_links[$className][0]; $entries_count = (int)$all_links[$className][1][0]['found_rows']; // Global config setting calls for PDO } else { $entries = DBManager::get()->fetchAll($partSQL); $entries_count_array = DBManager::get()->fetchAll('SELECT FOUND_ROWS() as found_rows'); $entries_count = (int)$entries_count_array[0]['found_rows']; } // No results? Leave. if (!is_array($entries)) { continue; } // Walk through results $found = []; foreach ($entries as $one) { // Filter item and add to result if necessary. if ($item = $className::filter($one, $search)) { $found[] = $item; } } // Nothing found? Leave. if (count($found) === 0) { continue; } $result[$className] = [ 'name' => $className::getName(), 'fullsearch' => $className::getSearchURL($search), 'content' => $found, // If we found more results than needed, indicate a "more" link // for full search. 'more' => count($found) > Config::get()->GLOBALSEARCH_MAX_RESULT_OF_TYPE, // If there are more results than our arbitrary LIMIT, a plus // ('+') should be shown besides the category result count 'plus' => count($found) < $entries_count, ]; } GlobalSearchModule::clearCache(); // Sort $positions = array_flip($modules); uksort($result, function($a, $b) use ($positions) { return $positions[$a] - $positions[$b]; }); // Send me an answer $this->render_json($result); } /** * Provide a GUI for configuring the search module order and other settings. */ public function settings_action() { PageLayout::setTitle(_('Globale Suche: Einstellungen')); Navigation::activateItem('/admin/config/globalsearch'); $this->config = Config::get()->GLOBALSEARCH_MODULES; $this->modules = []; foreach ($this->config as $className => $config) { if (class_exists($className)) { $this->modules[$className] = new $className(); } } // Search declared classes for GlobalSearchModules foreach (get_declared_classes() as $className) { if (is_subclass_of($className, 'GlobalSearchModule')) { // Add new classes at module array end and not activated. if (!isset($this->modules[$className])) { $this->modules[$className] = new $className(); } } } } /** * Saves the set values to global configuration. */ public function saveconfig_action() { CSRFProtection::verifyUnsafeRequest(); $config = []; foreach (Request::getArray('modules') as $module) { $config[$module['class']] = [ 'active' => (bool)$module['active'], 'fulltext' => is_a($module['class'], 'GlobalSearchFulltext', true) && $module['fulltext'] ]; } Config::get()->store('GLOBALSEARCH_ASYNC_QUERIES', Request::int('async_queries', 0)); Config::get()->store('GLOBALSEARCH_MAX_RESULT_OF_TYPE', Request::int('entries_per_type', 3)); Config::get()->store('GLOBALSEARCH_MODULES', $config); PageLayout::postSuccess(_('Die Einstellungen wurden gespeichert.')); $this->redirect('globalsearch/settings'); } }