function forumng_delete_instance($id, $ociskip = true) { require_once dirname(__FILE__) . '/forum.php'; try { $forum = forum::get_from_id($id, forum::CLONE_DIRECT); // avoid deleting OCI specific forum if running in upload block if ($ociskip) { global $restore; if (isset($restore) && $restore->restoreto == 0 && strpos($_SERVER['HTTP_REFERER'], 'blocks/versions/upload.php') !== false) { if ($forum->get_name() == get_string('newunitforumname', 'createcourse')) { //Unit forum echo ' found forumng ' . $forum->get_id() . ' ' . $forum->get_name(); return true; } } } $forum->delete_all_data(); if (forum::search_installed()) { $cm = $forum->get_course_module(); ousearch_document::delete_module_instance_data($cm); } } catch (Exception $e) { return false; } return delete_records('forumng', 'id', $id); }
function test_split_words() { // Standard usage and caps $this->assertEqual(ousearch_document::split_words('Hello I AM a basic test'), array('hello', 'i', 'am', 'a', 'basic', 'test')); // Numbers $this->assertEqual(ousearch_document::split_words('13 2by2'), array('13', '2by2')); // Ignored and accepted punctuation and whitespace $this->assertEqual(ousearch_document::split_words(' hello,testing!what\'s&up there-by '), array('hello', 'testing', 'what\'s', 'up', 'there', 'by')); // Unicode letters and nonletter $this->assertEqual(ousearch_document::split_words('café ßåřĉĕļÅ?ņä※tonight'), array('café', 'ßåřĉĕļÅ?ņä', 'tonight')); // Unicode caps $this->assertEqual(ousearch_document::split_words('ĀĒĪŌŪ'), array('Ä?Ä“Ä«Å?Å«')); // Query mode (keeps " + -) $this->assertEqual(ousearch_document::split_words('"hello there" +frog -doughnut extra-special', true), array('"hello', 'there"', '+frog', '-doughnut', 'extra-special')); // Position mode: normal $this->assertEqual(ousearch_document::split_words('hello test', false, true), array(array('hello', 'test'), array(0, 6, 10))); // Position mode: whitespace $this->assertEqual(ousearch_document::split_words(' hello test ', false, true), array(array('hello', 'test'), array(4, 13, 21))); // Position mode: unicode $this->assertEqual(ousearch_document::split_words('hÄ•llo tÄ•st', false, true), array(array('hÄ•llo', 'tÄ•st'), array(0, 7, 12))); // Positions are in bytes }
/** * 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) { // Set up 'search document' to refer to this post $doc = new ousearch_document(); $doc->init_module_instance('oublog', $cm); if (!isset($post->userid) || !isset($post->groupid)) { global $CFG; $results = get_record_sql("\nSELECT\n p.groupid,i.userid\nFROM\n{$CFG->prefix}oublog_posts p\n INNER JOIN {$CFG->prefix}oublog_instances i ON p.oubloginstancesid=i.id\nWHERE\n p.id={$post->id}"); if (!$results) { error("Can't find details for blog post {$post->id}"); } $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; }
/** * 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 = false, $courseid = 0) { global $CFG; // Get list of all wikis. We need the coursemodule data plus // the type of subwikis $coursecriteria = $courseid === 0 ? '' : 'cm.course=' . $courseid . ' AND'; $coursemodules = get_records_sql("\nSELECT\n cm.id,cm.course,cm.instance,w.subwikis\nFROM\n {$CFG->prefix}modules m\n INNER JOIN {$CFG->prefix}course_modules cm ON cm.module=m.id\n INNER JOIN {$CFG->prefix}ouwiki w ON cm.instance=w.id\nWHERE\n {$coursecriteria}\n m.name='ouwiki'"); 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 $rs = get_recordset_sql("\nSELECT\n p.id,p.title,v.xhtml,v.timecreated,sw.groupid,sw.userid\nFROM\n {$CFG->prefix}ouwiki_subwikis sw\n INNER JOIN {$CFG->prefix}ouwiki_pages p ON p.subwikiid=sw.id\n INNER JOIN {$CFG->prefix}ouwiki_versions v ON v.id=p.currentversionid\nWHERE\n sw.wikiid={$coursemodule->instance} AND {$where}"); while ($page = rs_fetch_next_record($rs)) { // Update the page for search $doc = new ousearch_document(); $doc->init_module_instance('ouwiki', $coursemodule); if ($page->groupid) { $doc->set_group_id($page->groupid); } if ($page->title) { $doc->set_string_ref($page->title); } if ($page->userid) { $doc->set_user_id($page->userid); } $title = $page->title ? $page->title : ''; if (!$doc->update($title, $page->xhtml, $page->timecreated)) { ouwiki_error('Failed to update database record for page ID ' . $page->id); } } rs_close($rs); $count++; if ($feedback) { if ($dotcount == 0) { print '<li>'; } print '.'; $dotcount++; if ($dotcount == 20 || $count == count($coursemodules)) { print 'done ' . $count . '</li>'; $dotcount = 0; } flush(); } } }
/** * 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) { if (!($oublog = get_record('oublog', 'id', $oublogid))) { return false; } if ($oublog->global) { error('You can\'t delete the global blog'); } if ($instances = get_records('oublog_instances', 'oublogid', $oublog->id)) { foreach ($instances as $oubloginstancesid => $bloginstance) { // tags delete_records('oublog_taginstances', 'oubloginstancesid', $oubloginstancesid); if ($posts = get_records('oublog_posts', 'oubloginstancesid', $oubloginstancesid)) { foreach ($posts as $postid => $post) { // comments delete_records('oublog_comments', 'postid', $postid); // edits delete_records('oublog_edits', 'postid', $postid); } // posts delete_records('oublog_posts', 'oubloginstancesid', $oubloginstancesid); } } } // links delete_records('oublog_links', 'oublogid', $oublog->id); // instances delete_records('oublog_instances', 'oublogid', $oublog->id); // Fulltext search data require_once dirname(__FILE__) . '/locallib.php'; if (oublog_search_installed()) { $moduleid = get_field('modules', 'id', 'name', 'oublog'); $cm = get_record('course_modules', 'module', $moduleid, 'instance', $oublog->id); if (!$cm) { error('Can\'t find coursemodule'); } ousearch_document::delete_module_instance_data($cm); } // oublog return delete_records('oublog', 'id', $oublog->id); }
$lastreport = 0; $pos = 0; $type_none = 0; $type_user = 0; $type_group = 0; $totaltime = 0; $documents = 0; while ($pos < $numlines) { // Pick how many lines to use for one 'document' $doclines = rand(5, 200); if ($pos + $doclines > $numlines) { $doclines = $numlines - $pos; } $start = $pos; // Set up document $document = new ousearch_document(); $document->init_test('test2'); // Refs define document and place in it $document->set_string_ref($filename); $document->set_int_refs($start, $doclines); // Type (group.user) $type = rand(0, 99); if ($type < 50) { // 50% None $type_none++; } else { if ($type < 90) { // 40% Group $document->set_group_id($groups[rand(0, count($groups) - 1)]); $type_group++; } else {
/** * 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 */ function ouwiki_save_new_version($course, $cm, $ouwiki, $subwiki, $pagename, $content, $changestart = -1, $changesize = -1, $changeprevsize = -1, $nouser = false) { $tw = new transaction_wrapper(); // Find page if it exists $pageversion = ouwiki_get_current_page($subwiki, $pagename, OUWIKI_GETPAGE_CREATE); // 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 = count_records('ouwiki_versions', '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 = get_field('ouwiki_sections', 'title', 'xhtmlid', addslashes($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 global $ouwiki_count; // Nasty but I can't think of a better way! $ouwiki_count = 0; global $ouwiki_internal_re; $ouwiki_internal_re->version = $versionnumber; $ouwiki_internal_re->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'; $content = preg_replace($regex, '[[]]', $content); // Create version $version = new StdClass(); $version->pageid = $pageversion->pageid; $version->xhtml = addslashes($content); $version->timecreated = time(); if (!$nouser) { global $USER; $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; } if (!($versionid = insert_record('ouwiki_versions', $version))) { $tw->rollback(); ouwiki_dberror(); } // Update latest version if (!set_field('ouwiki_pages', 'currentversionid', $versionid, 'id', $pageversion->pageid)) { $tw->rollback(); ouwiki_dberror(); } // 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 = get_record_select('ouwiki_pages', "subwikiid='{$subwiki->id}' AND UPPER(title)=UPPER('" . addslashes($targetpage) . "')"); if ($pagerecord) { $pageid = $pagerecord->id; } else { $pageid = false; } } else { $pageid = get_field_select('ouwiki_pages', 'id', "subwikiid={$subwiki->id} AND title IS NULL"); } if ($pageid) { $link->topageid = $pageid; $link->tomissingpage = null; } else { $link->topageid = null; $link->tomissingpage = addslashes(strtoupper($targetpage)); } if (!($link->id = insert_record('ouwiki_links', $link))) { $tw->rollback(); ouwiki_dberror(); } } $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 = addslashes($url); if (!($link->id = insert_record('ouwiki_links', $link))) { $tw->rollback(); ouwiki_dberror(); } } // Inform search, if installed if (ouwiki_search_installed()) { $doc = new 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 = is_null($pageversion->title) ? '' : $pageversion->title; if (!$doc->update($title, $content)) { $tw->rollback(); ouwiki_dberror(); } } // Inform completion system, if available if (class_exists('ouflags')) { if (completion_is_enabled($course, $cm) && ($ouwiki->completionedits || $ouwiki->completionpages)) { completion_update_state($course, $cm, COMPLETION_COMPLETE); } } $tw->commit(); }
/** * Update all documents for ousearch. * @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 */ static function search_update_all($feedback = false, $courseid = 0, $cmid = 0) { global $CFG; // If cmid is specified, only retrieve that one if ($cmid) { $cmrestrict = "cm.id = {$cmid} AND"; } else { $cmrestrict = ''; } // Get module-instances that need updating $cms = get_records_sql("\nSELECT\n cm.id, cm.course, cm.instance, f.name\nFROM\n {$CFG->prefix}forumng f\n INNER JOIN {$CFG->prefix}course_modules cm ON cm.instance=f.id\nWHERE\n {$cmrestrict}\n cm.module = (SELECT id FROM {$CFG->prefix}modules m WHERE name='forumng')" . ($courseid ? " AND f.course={$courseid}" : '')); $cms = $cms ? $cms : 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 reset the time limit. // Store the existing limit; we will set this existing value again // each time around the loop. Note: Despite the name, ini_get returns // the most recently set time limit, not the one from php.ini. $timelimitbefore = ini_get('max_execution_time'); // Loop around updating foreach ($cms as $cm) { forum_utils::start_transaction(); // Wipe existing search data, if any ousearch_document::delete_module_instance_data($cm); // Get all discussions for this forum $discussions = get_records('forumng_discussions', 'forumid', $cm->instance, '', 'id, postid'); $discussions = $discussions ? $discussions : array(); 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 = forum_discussion::get_from_id($discussionrec->id, forum::CLONE_DIRECT, -1); $root = $discussion->get_root_post(); $root->search_update(); $root->search_update_children(); print '. '; flush(); } forum_utils::finish_transaction(); if ($feedback) { print '</li>'; } } }
/** * Obtains search document representing this post. * @return ousearch_document Document object */ function search_get_document() { $doc = new ousearch_document(); $doc->init_module_instance('forumng', $this->get_forum()->get_course_module()); if ($groupid = $this->discussion->get_group_id()) { $doc->set_group_id($groupid); } $doc->set_int_refs($this->get_id()); return $doc; }
/** * 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. */ function internal_filter($results, $desired) { global $CFG; $required = array(); $accepted = array(); $count = 0; $return = new StdClass(); $return->dbnext = 0; $tl = textlib_get_instance(); 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)) { error('Missing module search support ' . $function); } } } 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 error('Unsupported search plugin type ' . $result->plugin); } } // 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) { debugging('Module ' . $result->plugin . ' can\'t find search document'); ousearch_document::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 = 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) = ousearch_document::split_words($textcontent, false, true); list($titlewords, $titlepositions) = 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 - OUSEARCH_SUMMARYLENGTH, $highlights)) { unset($highlights[$pos - OUSEARCH_SUMMARYLENGTH]); $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 - OUSEARCH_SUMMARYLENGTH; if ($start < 0) { $start = 0; } $end = $start + OUSEARCH_SUMMARYLENGTH; if ($end > count($contentwords)) { $end = count($contentwords); } // $contentpositions is in characters $result->summary = $tl->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; }