/** * @param $exerciseId * @param bool $addQs * @param bool $rmQs */ public function search_engine_edit($exerciseId, $addQs = false, $rmQs = false) { // update search engine and its values table if enabled if (api_get_setting('search_enabled') == 'true' && extension_loaded('xapian')) { $course_id = api_get_course_id(); // get search_did $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF); if ($addQs || $rmQs) { //there's only one row per question on normal db and one document per question on search engine db $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_second_level=%s LIMIT 1'; $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $this->id); } else { $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s AND ref_id_second_level=%s LIMIT 1'; $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $exerciseId, $this->id); } $res = Database::query($sql); if (Database::num_rows($res) > 0 || $addQs) { require_once api_get_path(LIBRARY_PATH) . 'search/ChamiloIndexer.class.php'; require_once api_get_path(LIBRARY_PATH) . 'search/IndexableChunk.class.php'; $di = new ChamiloIndexer(); if ($addQs) { $question_exercises = array((int) $exerciseId); } else { $question_exercises = array(); } isset($_POST['language']) ? $lang = Database::escape_string($_POST['language']) : ($lang = 'english'); $di->connectDb(null, null, $lang); // retrieve others exercise ids $se_ref = Database::fetch_array($res); $se_doc = $di->get_document((int) $se_ref['search_did']); if ($se_doc !== false) { if (($se_doc_data = $di->get_document_data($se_doc)) !== false) { $se_doc_data = unserialize($se_doc_data); if (isset($se_doc_data[SE_DATA]['type']) && $se_doc_data[SE_DATA]['type'] == SE_DOCTYPE_EXERCISE_QUESTION) { if (isset($se_doc_data[SE_DATA]['exercise_ids']) && is_array($se_doc_data[SE_DATA]['exercise_ids'])) { foreach ($se_doc_data[SE_DATA]['exercise_ids'] as $old_value) { if (!in_array($old_value, $question_exercises)) { $question_exercises[] = $old_value; } } } } } } if ($rmQs) { while (($key = array_search($exerciseId, $question_exercises)) !== false) { unset($question_exercises[$key]); } } // build the chunk to index $ic_slide = new IndexableChunk(); $ic_slide->addValue("title", $this->question); $ic_slide->addCourseId($course_id); $ic_slide->addToolId(TOOL_QUIZ); $xapian_data = array(SE_COURSE_ID => $course_id, SE_TOOL_ID => TOOL_QUIZ, SE_DATA => array('type' => SE_DOCTYPE_EXERCISE_QUESTION, 'exercise_ids' => $question_exercises, 'question_id' => (int) $this->id), SE_USER => (int) api_get_user_id()); $ic_slide->xapian_data = serialize($xapian_data); $ic_slide->addValue("content", $this->description); //TODO: index answers, see also form validation on question_admin.inc.php $di->remove_document((int) $se_ref['search_did']); $di->addChunk($ic_slide); //index and return search engine document id if (!empty($question_exercises)) { // if empty there is nothing to index $did = $di->index(); unset($di); } if ($did || $rmQs) { // save it to db if ($addQs || $rmQs) { $sql = 'DELETE FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_second_level=\'%s\''; $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $this->id); } else { $sql = 'DELETE FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=\'%s\' AND ref_id_second_level=\'%s\''; $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $exerciseId, $this->id); } Database::query($sql); if ($rmQs) { if (!empty($question_exercises)) { $sql = 'INSERT INTO %s (id, course_code, tool_id, ref_id_high_level, ref_id_second_level, search_did) VALUES (NULL , \'%s\', \'%s\', %s, %s, %s)'; $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, array_shift($question_exercises), $this->id, $did); Database::query($sql); } } else { $sql = 'INSERT INTO %s (id, course_code, tool_id, ref_id_high_level, ref_id_second_level, search_did) VALUES (NULL , \'%s\', \'%s\', %s, %s, %s)'; $sql = sprintf($sql, $tbl_se_ref, $course_id, TOOL_QUIZ, $exerciseId, $this->id, $did); Database::query($sql); } } } } }
/** * Set index specified prefix terms for all items in this path * @param string Comma-separated list of terms * @param char Xapian term prefix * @return boolean False on error, true otherwise */ public function set_terms_by_prefix($terms_string, $prefix) { $em = Database::getManager(); $course = $em->find('ChamiloCoreBundle:Course', api_get_course_int_id()); if (api_get_setting('search.search_enabled') !== 'true') { return false; } if (!extension_loaded('xapian')) { return false; } $terms_string = trim($terms_string); $terms = explode(',', $terms_string); array_walk($terms, 'trim_value'); $stored_terms = $this->get_common_index_terms_by_prefix($prefix); // Don't do anything if no change, verify only at DB, not the search engine. if (count(array_diff($terms, $stored_terms)) == 0 && count(array_diff($stored_terms, $terms)) == 0) { return false; } require_once 'xapian.php'; // TODO: Try catch every xapian use or make wrappers on API. require_once api_get_path(LIBRARY_PATH) . 'search/ChamiloIndexer.class.php'; require_once api_get_path(LIBRARY_PATH) . 'search/xapian/XapianQuery.php'; require_once api_get_path(LIBRARY_PATH) . 'search/IndexableChunk.class.php'; $items_table = Database::get_course_table(TABLE_LP_ITEM); // TODO: Make query secure agains XSS : use member attr instead of post var. $lp_id = intval($_POST['lp_id']); $sql = "SELECT * FROM {$items_table} WHERE c_id = {$course->getId()} AND lp_id = {$lp_id}"; $result = Database::query($sql); $di = new ChamiloIndexer(); while ($lp_item = Database::fetch_array($result)) { $lpCourse = $em->getRepository('ChamiloCoreBundle:Course')->findOneBy(['code' => $this->cc]); // Get search_did. $se_ref = $em->getRepository('ChamiloCoreBundle:SearchEngineRef')->findOneBy(['course' => $lpCourse, 'toolId' => TOOL_LEARNPATH, 'refIdHighLevel' => $lp_id, 'refIdSecondLevel' => $lp_item['id']]); if ($se_ref) { // Compare terms. $doc = $di->get_document($se_ref->getSearchDid()); $xapian_terms = xapian_get_doc_terms($doc, $prefix); $xterms = array(); foreach ($xapian_terms as $xapian_term) { $xterms[] = substr($xapian_term['name'], 1); } $dterms = $terms; $missing_terms = array_diff($dterms, $xterms); $deprecated_terms = array_diff($xterms, $dterms); // Save it to search engine. foreach ($missing_terms as $term) { $doc->add_term($prefix . $term, 1); } foreach ($deprecated_terms as $term) { $doc->remove_term($prefix . $term); } $di->getDb()->replace_document($se_ref->getSearchDid(), $doc); $di->getDb()->flush(); } else { //@todo What we should do here? } } return true; }
/** * Set index specified prefix terms for all items in this path * @param string Comma-separated list of terms * @param char Xapian term prefix * @return boolean False on error, true otherwise */ public function set_terms_by_prefix($terms_string, $prefix) { $course_id = api_get_course_int_id(); if (api_get_setting('search_enabled') !== 'true') { return false; } if (!extension_loaded('xapian')) { return false; } $terms_string = trim($terms_string); $terms = explode(',', $terms_string); array_walk($terms, 'trim_value'); $stored_terms = $this->get_common_index_terms_by_prefix($prefix); // Don't do anything if no change, verify only at DB, not the search engine. if (count(array_diff($terms, $stored_terms)) == 0 && count(array_diff($stored_terms, $terms)) == 0) { return false; } require_once 'xapian.php'; // TODO: Try catch every xapian use or make wrappers on API. require_once api_get_path(LIBRARY_PATH) . 'search/ChamiloIndexer.class.php'; require_once api_get_path(LIBRARY_PATH) . 'search/xapian/XapianQuery.php'; require_once api_get_path(LIBRARY_PATH) . 'search/IndexableChunk.class.php'; $items_table = Database::get_course_table(TABLE_LP_ITEM); // TODO: Make query secure agains XSS : use member attr instead of post var. $lp_id = intval($_POST['lp_id']); $sql = "SELECT * FROM {$items_table} WHERE c_id = {$course_id} AND lp_id = {$lp_id}"; $result = Database::query($sql); $di = new ChamiloIndexer(); while ($lp_item = Database::fetch_array($result)) { // Get search_did. $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF); $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s AND ref_id_second_level=%d LIMIT 1'; $sql = sprintf($sql, $tbl_se_ref, $this->cc, TOOL_LEARNPATH, $lp_id, $lp_item['id']); //echo $sql; echo '<br>'; $res = Database::query($sql); if (Database::num_rows($res) > 0) { $se_ref = Database::fetch_array($res); // Compare terms. $doc = $di->get_document($se_ref['search_did']); $xapian_terms = xapian_get_doc_terms($doc, $prefix); $xterms = array(); foreach ($xapian_terms as $xapian_term) { $xterms[] = substr($xapian_term['name'], 1); } $dterms = $terms; $missing_terms = array_diff($dterms, $xterms); $deprecated_terms = array_diff($xterms, $dterms); // Save it to search engine. foreach ($missing_terms as $term) { $doc->add_term($prefix . $term, 1); } foreach ($deprecated_terms as $term) { $doc->remove_term($prefix . $term); } $di->getDb()->replace_document((int) $se_ref['search_did'], $doc); $di->getDb()->flush(); } else { //@todo What we should do here? } } return true; }
public function search_engine_edit($exerciseId, $addQs = false, $rmQs = false) { // update search engine and its values table if enabled if (api_get_setting('search.search_enabled') == 'true' && extension_loaded('xapian')) { $em = Database::getManager(); $course = $em->find('ChamiloCoreBundle:Course', api_get_course_int_id()); // get search_did if ($addQs || $rmQs) { //there's only one row per question on normal db and one document per question on search engine db $se_ref = $em->getRepository('ChamiloCoreBundle:SearchEngineRef')->findOneBy(['course' => $course, 'toolId' => TOOL_QUIZ, 'refIdSecondLevel' => $this->id]); } else { $se_ref = $em->getRepository('ChamiloCoreBundle:SearchEngineRef')->findOneBy(['course' => $course, 'toolId' => TOOL_QUIZ, 'refIdHighLevel' => $exerciseId, 'refIdSecondLevel' => $this->id]); } if ($se_ref || $addQs) { require_once api_get_path(LIBRARY_PATH) . 'search/ChamiloIndexer.class.php'; require_once api_get_path(LIBRARY_PATH) . 'search/IndexableChunk.class.php'; $di = new ChamiloIndexer(); if ($addQs) { $question_exercises = array((int) $exerciseId); } else { $question_exercises = array(); } isset($_POST['language']) ? $lang = Database::escape_string($_POST['language']) : ($lang = 'english'); $di->connectDb(NULL, NULL, $lang); // retrieve others exercise ids $se_doc = $di->get_document($se_ref->getSearchDid()); if ($se_doc !== FALSE) { if (($se_doc_data = $di->get_document_data($se_doc)) !== FALSE) { $se_doc_data = unserialize($se_doc_data); if (isset($se_doc_data[SE_DATA]['type']) && $se_doc_data[SE_DATA]['type'] == SE_DOCTYPE_EXERCISE_QUESTION) { if (isset($se_doc_data[SE_DATA]['exercise_ids']) && is_array($se_doc_data[SE_DATA]['exercise_ids'])) { foreach ($se_doc_data[SE_DATA]['exercise_ids'] as $old_value) { if (!in_array($old_value, $question_exercises)) { $question_exercises[] = $old_value; } } } } } } if ($rmQs) { while (($key = array_search($exerciseId, $question_exercises)) !== FALSE) { unset($question_exercises[$key]); } } // build the chunk to index $ic_slide = new IndexableChunk(); $ic_slide->addValue("title", $this->question); $ic_slide->addCourseId($course->getCode()); $ic_slide->addToolId(TOOL_QUIZ); $xapian_data = array(SE_COURSE_ID => $course->getCode(), SE_TOOL_ID => TOOL_QUIZ, SE_DATA => array('type' => SE_DOCTYPE_EXERCISE_QUESTION, 'exercise_ids' => $question_exercises, 'question_id' => (int) $this->id), SE_USER => (int) api_get_user_id()); $ic_slide->xapian_data = serialize($xapian_data); $ic_slide->addValue("content", $this->description); //TODO: index answers, see also form validation on question_admin.inc.php $di->remove_document($se_ref->getSearchDid()); $di->addChunk($ic_slide); //index and return search engine document id if (!empty($question_exercises)) { // if empty there is nothing to index $did = $di->index(); unset($di); } if ($did || $rmQs) { // save it to db if ($addQs || $rmQs) { $em->createQuery(' DELETE FROM ChamiloCoreBundle:SearchEngineRef ser WHERE ser.course = ?1 AND ser.toolId = ?2 AND ser.refIdSecondLevel = ?3 ')->execute([1 => $course, 2 => TOOL_QUIZ, 3 => $this->id]); } else { $em->createQuery(' DELETE FROM ChamiloCoreBundle:SearchEngineRef ser WHERE ser.course = ?1 AND ser.toolId = ?2 AND ser.refIdHighLevel = ?3 AND ser.refIdSecondLevel = ?4 ')->execute([1 => $course, 2 => TOOL_QUIZ, 3 => $exerciseId, 4 => $this->id]); } if ($rmQs) { if (!empty($question_exercises)) { $searchEngineRef = new \Chamilo\CoreBundle\Entity\SearchEngineRef(); $searchEngineRef->setCourse($course)->setToolId(TOOL_QUIZ)->setRefIdHighLevel(array_shift($question_exercises))->setRefIdSecondLevel($this->id)->setSearchDid($did); $em->persist($searchEngineRef); $em->flush(); } } else { $searchEngineRef = new \Chamilo\CoreBundle\Entity\SearchEngineRef(); $searchEngineRef->setCourse($course)->setToolId(TOOL_QUIZ)->setRefIdHighLevel($exerciseId)->setRefIdSecondLevel($this->id)->setSearchDid($did); $em->persist($searchEngineRef); $em->flush(); } } } } }