function forumng_delete_instance($id) { global $DB; require_once dirname(__FILE__) . '/mod_forumng.php'; $cm = get_coursemodule_from_instance('forumng', $id); $forum = mod_forumng::get_from_id($id, mod_forumng::CLONE_DIRECT, true, $cm); $forum->delete_all_data(); if (mod_forumng::search_installed()) { $cm = $forum->get_course_module(); local_ousearch_document::delete_module_instance_data($cm); } if ($forum->is_shared()) { // Find all the clone instances. $clones = $forum->get_clone_details(); $transaction = $DB->start_delegated_transaction(); foreach ($clones as $clone) { try { course_delete_module($clone->context->instanceid); } catch (moodle_exception $e) { notify("Could not delete the Clone\n forumng (coursemoduleid) {$clone->context}->instanceid "); return false; } rebuild_course_cache($clone->courseid, true); } $transaction->allow_commit(); } return $DB->delete_records('forumng', array('id' => $id)); }
/** * Adds a test document to the index and to the in-memory store. * * @param int $index Document index * @param stdClass $course Course object * @param string $content Text content * @param int $cmid If specified, cmid of document */ private static function add_test_document($index, $course, $content, $cmid = 0) { $doc = new local_ousearch_document(); $doc->courseid = $course->id; $doc->plugin = 'test_yeartablestest'; $doc->timemodified = $index; $doc->set_int_refs($index); if ($cmid) { $doc->coursemoduleid = $cmid; } $doc->update('Title', $content); self::$testdocuments[$index] = array('title' => 'Title', 'content' => $content); }
/** * Filters search results to pick out only the ones that match the query. * @param array $results Array of results from internal_query * @param int $desired Number of desired results * @return object ->results containing actual results and ->dbnext * containing database position of next set of results. * @throws coding_exception If the results contain unsupported plugin types */ private function internal_filter($results, $desired) { global $CFG; $required = array(); $accepted = array(); $count = 0; $return = new StdClass(); $return->dbnext = 0; foreach ($results as $result) { $return->dbnext++; if (substr($result->plugin, 0, 4) === 'mod_') { // Module plugins. $module = substr($result->plugin, 4); $function = $module . '_ousearch_get_document'; if (!array_key_exists($module, $required)) { require_once $CFG->dirroot . '/mod/' . $module . '/lib.php'; $required[$module] = true; if (!function_exists($function)) { throw new coding_exception('Missing module search support ' . $function, 'Module is not searchable. Needs function ' . $function . '. See local/ousearch/doc/usage.html.'); } } } else { if (substr($result->plugin, 0, 5) === 'test_') { // Testing code, assumed to already be included. $function = substr($result->plugin, 5) . '_ousearch_get_document'; } else { // Nothing else supported yet. throw new coding_exception('Unsupported search plugin type ' . $result->plugin, 'OU search only currently works for modules'); } } // Let's request the document. Note that the 'document' fields of // $result are those used by this function to find the right one. $page = $function($result); // Ignore if we can't find the document. if (!$page) { // Output debug warning. debugging('Module ' . $result->plugin . ' can\'t find search document, removing from results'); $searchdoc = new local_ousearch_document(); $searchdoc->wipe_document($result->id); continue; } // Page option can request that this result is not included. if (!empty($page->hide)) { continue; } // Strip XHTML from content (need this before phrase scan). $textcontent = local_ousearch_document::strip_xhtml($page->content); // Add extra strings to the content after a special don't-show-this // marker and with another special marker between each (to prevent // phrases). if (isset($page->extrastrings) && count($page->extrastrings) > 0) { $evilmarker = rand(); // This means people can't do it on purpose. $textcontent .= ' xxrealcontentends' . $evilmarker; foreach ($page->extrastrings as $string) { $textcontent .= ' ' . $string . ' xxsplit' . $evilmarker; } } // Do quick phrase scan that doesn't deal with Unicode, // or word-splitting but just discards results that // don't have the phrase words next to each other without // ASCII letters in between. This is intended to discard // results that (fairly) definitely don't have the phrase. // The further check below will make sure they really do // have it according to our standard (slow) word-splitting. $quickcheckcontent = $page->title . ' ' . $textcontent; $ok = true; foreach ($this->terms as $term) { if (count($term->words) < 2) { continue; } $gap = '[^A-Za-z0-9]+'; $pattern = '/(^|' . $gap . ')'; $first = true; foreach ($term->words as $word) { if ($first) { $first = false; } else { $pattern .= $gap; } $pattern .= $word; } $pattern .= '($|' . $gap . ')/i'; if (!preg_match($pattern, $quickcheckcontent)) { $ok = false; break; } } if (!$ok) { continue; } // OK, obtain document as linear text. list($contentwords, $contentpositions) = local_ousearch_document::split_words($textcontent, false, true); list($titlewords, $titlepositions) = local_ousearch_document::split_words($page->title, false, true); $allwords = array_merge($titlewords, $contentwords); // Check it for phrases. $positivewords = array(); $ok = true; $dnifound = -1; foreach ($this->terms as $term) { foreach ($term->words as $word) { $positivewords[$word] = true; } if (count($term->words) < 2) { continue; } $pos = 0; $found = false; foreach ($allwords as $word) { if ($word === $term->words[$pos]) { $pos++; if ($pos === count($term->words)) { $found = true; break; } } else { $pos = 0; } } if (!$found) { $ok = false; break; } } foreach ($this->negativeterms as $term) { if (count($term->words) < 2) { continue; } $pos = 0; $found = false; foreach ($allwords as $word) { if ($word === $term->words[$pos]) { $pos++; if ($pos === count($term->words)) { $found = true; break; } } else { $pos = 0; } } if ($found) { $ok = false; break; } } if (!$ok) { continue; } // Result passes! Make structure holding it... // We now have list of all positive words, let's mark these // in title and summary. $result->title = self::internal_highlight_words($page->title, $titlewords, $titlepositions, $positivewords); // Strip searchable-but-not-displayable content for summary. if (isset($evilmarker)) { $strippedwords = array(); foreach ($contentwords as $word) { // Do not include extra strings in summary. if ($word === 'xxrealcontentends' . $evilmarker) { break; } $strippedwords[] = $word; } $contentwords = $strippedwords; } // Pick a section to include in the summary. This algorithm works as follows: // * Compute the 'score' (number of highlight words in the previous 20 words // up to and including this one) at each position in the text // * Observe where the maximum score is reached and where it is lost. // * A nice range that contains the most highlight words in the middle of the // range will end at ($maxstart + $maxlength/2). $highlights = array(); $pos = 0; $currentscore = 0; $maxscore = -1; $maxstart = 0; $maxlength = 0; $run = true; foreach ($contentwords as $word) { if (array_key_exists($pos - self::SUMMARY_LENGTH, $highlights)) { unset($highlights[$pos - self::SUMMARY_LENGTH]); $currentscore--; } if (array_key_exists($word, $positivewords)) { $highlights[$pos] = true; $currentscore++; } if ($currentscore > $maxscore) { $maxscore = $currentscore; $maxstart = $pos; $maxlength = 1; $run = true; } else { if ($currentscore === $maxscore && $run) { $maxlength++; } else { $run = false; } } $pos++; } $start = $maxstart + $maxlength / 2 - self::SUMMARY_LENGTH; if ($start < 0) { $start = 0; } $end = $start + self::SUMMARY_LENGTH; if ($end > count($contentwords)) { $end = count($contentwords); } // The $contentpositions is in characters. $result->summary = core_text::substr($textcontent, $contentpositions[$start], $contentpositions[$end] - $contentpositions[$start]) . ($end < count($contentwords) ? '...' : ''); $offset = -$contentpositions[$start]; $result->summary = self::internal_highlight_words($result->summary, $contentwords, $contentpositions, $positivewords, $offset, $start, $end); if ($start !== 0) { $result->summary = '...' . $result->summary; } $result->summary = trim($result->summary); $result->activityname = $page->activityname; $result->activityurl = $page->activityurl; $result->url = $page->url; if (isset($page->data)) { $result->data = $page->data; } // Do user-specified filter if set. if ($this->filter) { $filter = $this->filter; if (!$filter($result)) { continue; } } $accepted[] = $result; $count++; if ($count == $desired) { break; } } $return->results = $accepted; return $return; }
/** * Does a chunk of work toward splitting the data into tables. * * @param bool $output If true, calls mtrace to display output * @param array $results Output array (number of documents moved per course) * @param int $chunksize Number of items to include in chunk (roughly) * @return bool True to stop processing for this cron, false to continue * @throws coding_exception If not currently doing the transfer */ public static function split_tables_chunk($output = true, $chunksize = self::SPLIT_TABLES_CHUNK_SIZE) { global $DB, $CFG; require_once $CFG->dirroot . '/local/ousearch/searchlib.php'; $enabled = get_config('local_ousearch', self::CONFIG_ENABLED); if ($enabled != self::ENABLED_TRANSFERRING) { throw new \coding_exception('Cannot call this except during transfer'); } // Get course being transferred and set up initial data. $transferringid = get_config('local_ousearch', self::CONFIG_TRANSFERRING_COURSE); $params = array(); if (!$transferringid) { // Do non-course stuff first. $where = 'courseid IS NULL'; $course = null; self::cron_log($output, ' [Non-course]'); $targetyear = self::NON_COURSE_YEAR; } else { $where = 'courseid = ?'; $params[] = $transferringid; $course = get_course($transferringid); self::cron_log($output, ' ' . $course->shortname); $targetyear = self::get_year_for_course($course); } // Work out table names. $targetdocs = 'local_ousearch_docs_' . $targetyear; $targetoccurs = 'local_ousearch_occurs_' . $targetyear; // Only get the records we haven't done yet. $doneupto = get_config('local_ousearch', self::CONFIG_TRANSFERRING_DONEUPTO); if ($doneupto) { $where .= ' AND timemodified > ?'; $params[] = $doneupto; } // Find all records that were added or modified since the last processed // time. Note we do not need to limit these results because it's a // recordset. $before = microtime(true); $transaction = $DB->start_delegated_transaction(); $rs = $DB->get_recordset_select('local_ousearch_documents', $where, $params, 'timemodified', '*'); self::cron_log($output, ' Select: ' . round(microtime(true) - $before, 1) . 's.'); $count = 0; $lasttime = 0; $complete = true; $updates = 0; $creates = 0; $before = microtime(true); foreach ($rs as $document) { // If we have already processed the requested amount, stop processing // as soon as the last time changes. (The reason to wait until then // is that we can record that we've done everything up until that // time, without missing anything.) if ($count >= $chunksize) { if ($document->timemodified != $lasttime) { $complete = false; break; } } // See if this document already exists. If so, delete and remake. $docobject = new \local_ousearch_document(); $docobject->init_from_record($document); unset($docobject->id); if ($docobject->find($targetdocs)) { $DB->delete_records($targetoccurs, array('documentid' => $docobject->id)); $DB->delete_records($targetdocs, array('id' => $docobject->id)); $updates++; } else { $creates++; } // Insert the document into the target table. $oldid = $document->id; unset($document->id); $newid = $DB->insert_record($targetdocs, $document); // Copy all the SQL occurrences from source table. $DB->execute('INSERT INTO {' . $targetoccurs . '} (wordid, documentid, score) SELECT wordid, ?, score FROM {local_ousearch_occurrences} WHERE documentid = ?', array($newid, $oldid)); $lasttime = $document->timemodified; $count++; } $rs->close(); self::cron_log($output, ' Copy: ' . $creates . ' creates, ' . $updates . ' updates, ' . round(microtime(true) - $before, 1) . 's.'); if ($complete) { // After completing a course, add it to the course years list. if ($course) { $DB->insert_record('local_ousearch_courseyears', array('courseid' => $course->id, 'year' => $targetyear)); } // Look for the next course. $nextparams = array(); if (!$transferringid) { $where = ''; } else { $where = 'WHERE id > ?'; $nextparams[] = $transferringid; } $nextcourseid = $DB->get_field_sql("SELECT MIN(id) FROM {course} {$where}", $nextparams, IGNORE_MISSING); if ($nextcourseid) { // Move to next course. unset_config(self::CONFIG_TRANSFERRING_DONEUPTO, 'local_ousearch'); set_config(self::CONFIG_TRANSFERRING_COURSE, $nextcourseid, 'local_ousearch'); $transaction->allow_commit(); self::cron_log($output, ' Complete, moving to next course.', true); return false; } else { // Finished all courses! unset_config(self::CONFIG_TRANSFERRING_DONEUPTO, 'local_ousearch'); unset_config(self::CONFIG_TRANSFERRING_COURSE, 'local_ousearch'); set_config(self::CONFIG_ENABLED, self::ENABLED_ON, 'local_ousearch'); $transaction->allow_commit(); // Delete all records from old tables. Doing this outside the // transaction allows it to use TRUNCATE to speed this up. $before = microtime(true); $DB->delete_records('local_ousearch_documents'); $DB->delete_records('local_ousearch_occurrences'); self::cron_log($output, ' All courses complete, tables deleted: ' . round(microtime(true) - $before, 1) . 's.', true); return true; } } else { // If not complete, update the time processed up to. set_config(self::CONFIG_TRANSFERRING_DONEUPTO, $lasttime, 'local_ousearch'); $transaction->allow_commit(); self::cron_log($output, ' Continuing...', true); return false; } }
/** * Update all wiki documents for ousearch. * * @param bool $feedback If true, prints feedback as HTML list items * @param int $courseid If specified, restricts to particular courseid */ function ouwiki_ousearch_update_all($feedback = null, $courseid = 0) { global $CFG, $DB; require_once $CFG->dirroot . '/mod/ouwiki/locallib.php'; // Get list of all wikis. We need the coursemodule data plus // the type of subwikis $coursecriteria = $courseid === 0 ? '' : 'AND cm.course = ' . $courseid; $sql = "SELECT cm.id, cm.course, cm.instance, w.subwikis\n FROM {modules} m\n INNER JOIN {course_modules} cm ON cm.module = m.id\n INNER JOIN {ouwiki} w ON cm.instance = w.id\n WHERE m.name = 'ouwiki' {$coursecriteria}"; $coursemodules = $DB->get_records_sql($sql, array()); if (!$coursemodules) { return; } if ($feedback) { print '<li><strong>' . count($coursemodules) . '</strong> wikis to process.</li>'; $dotcount = 0; } $count = 0; foreach ($coursemodules as $coursemodule) { // This condition is needed because if somebody creates some stuff // then changes the wiki type, it actually keeps the old bits // in the database. Maybe it shouldn't, not sure. switch ($coursemodule->subwikis) { case OUWIKI_SUBWIKIS_SINGLE: $where = "sw.userid IS NULL AND sw.groupid IS NULL"; break; case OUWIKI_SUBWIKIS_GROUPS: $where = "sw.userid IS NULL AND sw.groupid IS NOT NULL"; break; case OUWIKI_SUBWIKIS_INDIVIDUAL: $where = "sw.userid IS NOT NULL AND sw.groupid IS NULL"; break; } // Get all pages in that wiki $sql = "SELECT p.id, p.title, v.xhtml, v.timecreated, sw.groupid, sw.userid\n FROM {ouwiki_subwikis} sw\n INNER JOIN {ouwiki_pages} p ON p.subwikiid = sw.id\n INNER JOIN {ouwiki_versions} v ON v.id = p.currentversionid\n WHERE sw.wikiid = ? AND {$where}"; $rs = $DB->get_recordset_sql($sql, array($coursemodule->instance)); foreach ($rs as $result) { // Update the page for search $doc = new local_ousearch_document(); $doc->init_module_instance('ouwiki', $coursemodule); if ($result->groupid) { $doc->set_group_id($result->groupid); } if ($result->title) { $doc->set_string_ref($result->title); } if ($result->userid) { $doc->set_user_id($result->userid); } $title = $result->title ? $result->title : ''; $doc->update($title, $result->xhtml, $result->timecreated); } $rs->close(); $count++; if ($feedback) { if ($dotcount == 0) { print '<li>'; } print '.'; $dotcount++; if ($dotcount == 20 || $count == count($coursemodules)) { print 'done ' . $count . '</li>'; $dotcount = 0; } flush(); } } }
/** * Saves a new version of the given named page within a subwiki. Can create * a new page or just add a new version to an existing one. In case of * failure, ends up calling error() rather than returning something. * * @param object $course Course object * @param object $cm Course-module object * @param object $ouwiki OU wiki object * @param object $subwiki Subwiki object * @param string $pagename Name of page (NO SLASHES) * @param string $content XHTML Content (NO SLASHES) * @param int $changestart For section changes. Start position of change. (-1 if not section change) * @param int $changesize Size of changed section. * @param int $changeprevsize Previous size of changed section * @param bool $nouser If true, creates as system * @param object $formdata if coming from edit will have content embedded media and attachments * @param int revertversionid if coming from revert.php will have an older versionid */ function ouwiki_save_new_version($course, $cm, $ouwiki, $subwiki, $pagename, $content, $changestart = -1, $changesize = -1, $changeprevsize = -1, $nouser = null, $formdata = null, $revertversionid = null) { global $DB, $USER; global $ouwikiinternalre, $ouwiki_count; // Nasty but I can't think of a better way! $transaction = $DB->start_delegated_transaction(); // Find page if it exists $pageversion = ouwiki_get_current_page($subwiki, $pagename, OUWIKI_GETPAGE_CREATE); $previousversionid = null; if ($pageversion->currentversionid) { $previousversionid = $pageversion->currentversionid; } // Analyse content for HTML headings that don't already have an ID. // These are all assigned unique, fairly short IDs. // Get number of version [guarantees in-page uniqueness of generated IDs] $versionnumber = $DB->count_records('ouwiki_versions', array('pageid' => $pageversion->pageid)); // Remove any spaces from annotation tags that were added for editing or by users // and remove any duplicate annotation tags $pattern = '~<span\\b.id=\\"annotation(.+?)\\">.*?</span>~'; $replace = '<span id="annotation$1"></span>'; $content = preg_replace($pattern, $replace, $content); unset($pattern, $replace, $used); // Get rid of any heading tags that only contain whitespace $emptypatterns = array(); for ($i = 1; $i <= 6; $i++) { $emptypatterns[] = '~<h' . $i . '[^>]*>\\s*(<br[^>]*>\\s*)*</h' . $i . '>~'; } $content = preg_replace($emptypatterns, '', $content); // List all headings that already have IDs, to check for duplicates $matches = array(); preg_match_all('|<h[1-9] id="ouw_s(.*?)">(.*?)</h[1-9]>|', $content, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE); // Organise list by ID $byid = array(); foreach ($matches as $index => $data) { $id = $data[1][0]; if (!array_key_exists($id, $byid)) { $byid[$id] = array(); } $byid[$id][] = $index; } // Handle any duplicates $deletebits = array(); foreach ($byid as $id => $duplicates) { if (count($duplicates) > 1) { // We have a duplicate. By default, keep the first one $keep = $duplicates[0]; // See if there is a title entry in the database for it $knowntitle = $DB->get_field('ouwiki_sections', 'title', array('xhtmlid' => $id, 'pageid' => $pageversion->pageid)); if ($knowntitle) { foreach ($duplicates as $duplicate) { $title = ouwiki_get_section_title(null, null, $matches[$duplicate][2][0]); if ($title === $knowntitle) { $keep = $duplicate; break; } } } foreach ($duplicates as $duplicate) { if ($duplicate !== $keep) { $deletebits[] = (object) array('startbyte' => $matches[$duplicate][1][1] - 10, 'bytes' => strlen($matches[$duplicate][1][0]) + 11); } } } } // Were there any? if (count($deletebits) > 0) { // Sort in reverse order of starting position usort($deletebits, 'ouwiki_internal_sort_deletions'); // Delete each bit foreach ($deletebits as $deletebit) { $content = substr($content, 0, $deletebit->startbyte) . substr($content, $deletebit->startbyte + $deletebit->bytes); } } // Replace existing empty headings with an ID including version count plus another index $ouwiki_count = 0; $ouwikiinternalre->version = $versionnumber; $ouwikiinternalre->count = 0; $sizebefore = strlen($content); $content = preg_replace_callback('/<h([1-9])>/', 'ouwiki_internal_re_headings', $content); $sizeafter = strlen($content); // Replace wiki links to [[Start page]] with the correct (non // language-specific) format [[]] $regex = str_replace('.*?', preg_quote(get_string('startpage', 'ouwiki')), OUWIKI_LINKS_SQUAREBRACKETS) . 'ui'; $newcontent = @preg_replace($regex, '[[]]', $content); if ($newcontent === null) { // Unicode support not available! Change the regex and try again $regex = preg_replace('~ui$~', 'i', $regex); $newcontent = preg_replace($regex, '[[]]', $content); } $content = $newcontent; // Create version $version = new StdClass(); $version->pageid = $pageversion->pageid; $version->xhtml = $content; // May be altered later (see below) $version->xhtmlformat = FORMAT_MOODLE; // Using fixed value here is a bit rubbish $version->timecreated = time(); $version->wordcount = ouwiki_count_words($content); $version->previousversionid = $previousversionid; if (!$nouser) { $version->userid = $USER->id; } if ($changestart != -1) { $version->changestart = $changestart; // In tracking the new size, account for any added headings etc $version->changesize = $changesize + ($sizeafter - $sizebefore); $version->changeprevsize = $changeprevsize; } try { $versionid = $DB->insert_record('ouwiki_versions', $version); // if firstversionid is already set in the current page use that // else this is a new page and version entirely if (!$pageversion->firstversionid) { $DB->set_field('ouwiki_pages', 'firstversionid', $versionid, array('id' => $version->pageid)); } } catch (Exception $e) { ouwiki_dberror($e); } // information needed for attachments $fs = get_file_storage(); $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); $prevversion = $revertversionid ? $revertversionid : $pageversion->versionid; // save new files connected with the version from the formdata if set if ($formdata) { $formdata->content = file_save_draft_area_files($formdata->content['itemid'], $modcontext->id, 'mod_ouwiki', 'content', $versionid, array('subdirs' => 0), $content); if ($content !== $formdata->content) { $DB->set_field('ouwiki_versions', 'xhtml', $formdata->content, array('id' => $versionid)); } if (isset($formdata->attachments)) { file_save_draft_area_files($formdata->attachments, $modcontext->id, 'mod_ouwiki', 'attachment', $versionid, array('subdirs' => 0)); } } else { // need to copy over attached files from the previous version when // editing without using form if ($oldfiles = $fs->get_area_files($modcontext->id, 'mod_ouwiki', 'attachment', $prevversion)) { foreach ($oldfiles as $oldfile) { // copy this file to the version record. $fs->create_file_from_storedfile(array('contextid' => $modcontext->id, 'filearea' => 'attachment', 'itemid' => $versionid), $oldfile); } } if ($oldfiles = $fs->get_area_files($modcontext->id, 'mod_ouwiki', 'content', $prevversion)) { foreach ($oldfiles as $oldfile) { // copy this file to the version record. $fs->create_file_from_storedfile(array('contextid' => $modcontext->id, 'filearea' => 'content', 'itemid' => $versionid), $oldfile); } } } // Update latest version $DB->set_field('ouwiki_pages', 'currentversionid', $versionid, array('id' => $pageversion->pageid)); // Analyse for links $wikilinks = array(); $externallinks = array(); // Wiki links: ordinary [[links]] $matches = array(); preg_match_all(OUWIKI_LINKS_SQUAREBRACKETS, $content, $matches, PREG_PATTERN_ORDER); foreach ($matches[1] as $match) { // Convert to page name (this also removes HTML tags etc) $wikilinks[] = ouwiki_get_wiki_link_details($match)->page; } // Note that we used to support CamelCase links but have removed support because: // 1. Confusing: students type JavaScript or MySpace and don't expect it to become a link // 2. Not accessible: screenreaders cannot cope with run-together words, and // dyslexic students can have difficulty reading them // External links preg_match_all('/<a [^>]*href=(?:(?:\'(.*?)\')|(?:"(.*?))")/', $content, $matches, PREG_PATTERN_ORDER); foreach ($matches[1] as $match) { if ($match) { $externallinks[] = html_entity_decode($match); } } foreach ($matches[2] as $match) { if ($match) { $externallinks[] = html_entity_decode($match); } } // Add link records $link = new StdClass(); $link->fromversionid = $versionid; foreach ($wikilinks as $targetpage) { if (!empty($targetpage)) { $pagerecord = $DB->get_record_select('ouwiki_pages', 'subwikiid = ? AND UPPER(title) = UPPER(?)', array($subwiki->id, $targetpage)); if ($pagerecord) { $pageid = $pagerecord->id; } else { $pageid = false; } } else { $pageid = $DB->get_field_select('ouwiki_pages', 'id', 'subwikiid = ? AND title IS NULL', array($subwiki->id)); } if ($pageid) { $link->topageid = $pageid; $link->tomissingpage = null; } else { $link->topageid = null; $link->tomissingpage = strtoupper($targetpage); } try { $link->id = $DB->insert_record('ouwiki_links', $link); } catch (Exception $e) { ouwiki_dberror($e); } } $link->topageid = null; $link->tomissingpage = null; $tl = textlib_get_instance(); foreach ($externallinks as $url) { // Restrict length of URL if ($tl->strlen($url) > 255) { $url = $tl->substr($url, 0, 255); } $link->tourl = $url; try { $link->id = $DB->insert_record('ouwiki_links', $link); } catch (Exception $e) { ouwiki_dberror($e); } } // Inform search, if installed if (ouwiki_search_installed()) { $doc = new local_ousearch_document(); $doc->init_module_instance('ouwiki', $cm); if ($subwiki->groupid) { $doc->set_group_id($subwiki->groupid); } $doc->set_string_ref($pageversion->title === '' ? null : $pageversion->title); if ($subwiki->userid) { $doc->set_user_id($subwiki->userid); } $title = $pageversion->title; $doc->update($title, $content); } $transaction->allow_commit(); }
/** * Update all documents for ousearch. * * If specified, the progress object should be ready to receive indeterminate * progress calls. * * @param bool $feedback If true, prints feedback as HTML list items * @param int $courseid If specified, restricts to particular courseid * @param int $cmid If specified, restricts to particular cmid * @param \core\progress\base $progress Set to a progress object or null */ public static function search_update_all($feedback = false, $courseid = 0, $cmid = 0, \core\progress\base $progress = null) { global $DB; raise_memory_limit(MEMORY_EXTRA); // If cmid is specified, only retrieve that one if ($cmid) { $cmrestrict = "cm.id = ? AND"; $cmrestrictparams = array($cmid); } else { $cmrestrict = ''; $cmrestrictparams = array(); } // Get module-instances that need updating $cms = $DB->get_records_sql("\nSELECT\n cm.id, cm.course, cm.instance, f.name\nFROM\n {forumng} f\n INNER JOIN {course_modules} cm ON cm.instance = f.id\nWHERE\n {$cmrestrict}\n cm.module = (SELECT id FROM {modules} m WHERE name = 'forumng')" . ($courseid ? " AND f.course = ?" : ''), array_merge($cmrestrictparams, $courseid ? array($courseid) : array())); // Print count if ($feedback && !$cmid) { print '<li>' . get_string('search_update_count', 'forumng', '<strong>' . count($cms) . '</strong>') . '</li>'; } // This can take a while, so let's be sure to have a long time limit. $timelimitbefore = 300; // Loop around updating foreach ($cms as $cm) { $transaction = $DB->start_delegated_transaction(); // Wipe existing search data, if any local_ousearch_document::delete_module_instance_data($cm); // Get all discussions for this forum $discussions = $DB->get_records('forumng_discussions', array('forumngid' => $cm->instance), '', 'id, postid'); if ($feedback) { print '<li><strong>' . $cm->name . '</strong> (' . count($discussions) . '):'; } // Process each discussion foreach ($discussions as $discussionrec) { // Ignore discussion with no postid // (This should not happen, where ther is a $discussionrec->id // it also shopuld have a $discussionrec->postid. This if-statement // fixes bug 10497 and would not have any side-effect.) if (!$discussionrec->postid) { continue; } set_time_limit($timelimitbefore); $discussion = mod_forumng_discussion::get_from_id($discussionrec->id, self::CLONE_DIRECT, -1); $root = $discussion->get_root_post(); $root->search_update(); $root->search_update_children(); $root = null; if ($feedback) { echo '. '; flush(); } if ($progress) { $progress->progress(\core\progress\base::INDETERMINATE); } } $transaction->allow_commit(); if ($feedback) { print '</li>'; } } }
/** * Tests the split_words function. */ public function test_split_words() { // Standard usage and caps. $this->assertEquals(local_ousearch_document::split_words('Hello I AM a basic test'), array('hello', 'i', 'am', 'a', 'basic', 'test')); // Numbers. $this->assertEquals(local_ousearch_document::split_words('13 2by2'), array('13', '2by2')); // Ignored and accepted punctuation and whitespace. $this->assertEquals(local_ousearch_document::split_words(' hello,testing!what\'s&up there-by '), array('hello', 'testing', 'what\'s', 'up', 'there', 'by')); // Unicode letters and nonletter, including one capital for lower-casing. $this->assertEquals(local_ousearch_document::split_words(html_entity_decode('café Áêïõù tonight', ENT_QUOTES, 'UTF-8')), array(html_entity_decode('café', ENT_QUOTES, 'UTF-8'), html_entity_decode('áêïõù', ENT_QUOTES, 'UTF-8'), 'tonight')); // Query mode (keeps " + -). $this->assertEquals(local_ousearch_document::split_words('"hello there" +frog -doughnut extra-special', true), array('"hello', 'there"', '+frog', '-doughnut', 'extra-special')); // Position mode: normal. $this->assertEquals(local_ousearch_document::split_words('hello test', false, true), array(array('hello', 'test'), array(0, 6, 10))); // Position mode: whitespace. $this->assertEquals(local_ousearch_document::split_words(' hello test ', false, true), array(array('hello', 'test'), array(4, 13, 21))); // Position mode: unicode (positions in characters). $eacute = html_entity_decode('é', ENT_QUOTES, 'UTF-8'); $this->assertEquals(local_ousearch_document::split_words("h{$eacute}llo t{$eacute}st", false, true), array(array("h{$eacute}llo", "t{$eacute}st"), array(0, 6, 10))); }
/** * Obtains a search document relating to a particular blog post. * * @param object $post Post object. Required fields: id (optionally also * groupid, userid save a db query) * @param object $cm Course-module object. Required fields: id, course * @return ousearch_doument */ function oublog_get_search_document($post, $cm) { global $DB; // Set up 'search document' to refer to this post $doc = new local_ousearch_document(); $doc->init_module_instance('oublog', $cm); if (!isset($post->userid) || !isset($post->groupid)) { $results = $DB->get_record_sql("\nSELECT\n p.groupid,i.userid\nFROM\n{oublog_posts} p\n INNER JOIN {oublog_instances} i ON p.oubloginstancesid=i.id\nWHERE\n p.id= ?", array($post->id)); if (!$results) { print_error('invalidblogdetails', 'oublog'); } $post->userid = $results->userid; $post->groupid = $results->groupid; } if ($post->groupid) { $doc->set_group_id($post->groupid); } $doc->set_user_id($post->userid); $doc->set_int_refs($post->id); return $doc; }
/** * Obtains search document representing this post. * @return local_ousearch_document Document object */ public function search_get_document() { $doc = new local_ousearch_document(); $doc->init_module_instance('forumng', $this->get_forum()->get_course_module(true)); if ($groupid = $this->discussion->get_group_id()) { $doc->set_group_id($groupid); } $doc->set_int_refs($this->get_id()); return $doc; }
/** * Given an ID of an instance of this module, this function will * permanently delete the instance and any data that depends on it. * * @param int $id The ID of the module instance * @return boolena true on success, false on failure. */ function oublog_delete_instance($oublogid) { global $DB, $CFG; if (!($oublog = $DB->get_record('oublog', array('id' => $oublogid)))) { return false; } if ($oublog->global) { print_error('deleteglobalblog', 'oublog'); } if ($instances = $DB->get_records('oublog_instances', array('oublogid' => $oublog->id))) { foreach ($instances as $oubloginstancesid => $bloginstance) { // tags $DB->delete_records('oublog_taginstances', array('oubloginstancesid' => $oubloginstancesid)); if ($posts = $DB->get_records('oublog_posts', array('oubloginstancesid' => $oubloginstancesid))) { foreach ($posts as $postid => $post) { // comments $DB->delete_records('oublog_comments', array('postid' => $postid)); // edits $DB->delete_records('oublog_edits', array('postid' => $postid)); } // posts $DB->delete_records('oublog_posts', array('oubloginstancesid' => $oubloginstancesid)); } } } // links $DB->delete_records('oublog_links', array('oublogid' => $oublog->id)); // instances $DB->delete_records('oublog_instances', array('oublogid' => $oublog->id)); // Fulltext search data require_once dirname(__FILE__) . '/locallib.php'; if (oublog_search_installed()) { $moduleid = $DB->get_field('modules', 'id', array('name' => 'oublog')); $cm = $DB->get_record('course_modules', array('module' => $moduleid, 'instance' => $oublog->id)); if (!$cm) { print_error('invalidcoursemodule'); } local_ousearch_document::delete_module_instance_data($cm); } oublog_grade_item_delete($oublog); // oublog return $DB->delete_records('oublog', array('id' => $oublog->id)); }