/** * Create temporary test tables and entries in the database for these tests. * These tests have to work on a brand new site. */ function setUp() { global $CFG; parent::setup(); $this->switch_to_test_db(); // All operations until end of test method will happen in test DB // additional tables required if ousearch module is present if (ouwiki_search_installed()) { $this->tables['local/ousearch'] = array('local_ousearch_documents', 'local_ousearch_words', 'local_ousearch_occurrences'); } foreach ($this->tables as $dir => $tables) { $this->create_test_tables($tables, $dir); // Create tables foreach ($tables as $table) { // Fill them if load_xxx method is available $function = "load_{$table}"; if (method_exists($this, $function)) { $this->{$function}(); } } } }
function ouwiki_delete_instance($id) { global $DB, $CFG; require_once $CFG->dirroot . '/mod/ouwiki/locallib.php'; $cm = get_coursemodule_from_instance('ouwiki', $id, 0, false, MUST_EXIST); // Delete associated template data. $context = context_module::instance($cm->id); $fs = get_file_storage(); $fs->delete_area_files($context->id, 'mod_ouwiki', 'template', $id); // Delete search data if (ouwiki_search_installed()) { local_ousearch_document::delete_module_instance_data($cm); } // Delete grade $ouwiki = $DB->get_record('ouwiki', array('id' => $cm->instance)); ouwiki_grade_item_delete($ouwiki); // Subqueries that find all versions and pages associated with this wiki // and delete them all bottom up $versions = $DB->get_records_sql("SELECT DISTINCT v.id\n FROM {ouwiki_subwikis} s\n INNER JOIN {ouwiki_pages} p ON p.subwikiid = s.id\n INNER JOIN {ouwiki_versions} v ON v.pageid = p.id\n WHERE s.wikiid = ?", array($id)); if (!empty($versions)) { list($vsql, $vparams) = $DB->get_in_or_equal(array_keys($versions)); $DB->delete_records_select('ouwiki_links', "fromversionid {$vsql}", $vparams); } $pages = $DB->get_records_sql("SELECT p.id\n FROM {ouwiki_subwikis} s\n INNER JOIN {ouwiki_pages} p ON p.subwikiid = s.id\n WHERE s.wikiid = ?", array($id)); if (!empty($pages)) { list($psql, $pparams) = $DB->get_in_or_equal(array_keys($pages)); $DB->delete_records_select('ouwiki_versions', "pageid {$psql}", $pparams); $DB->delete_records_select('ouwiki_locks', "pageid {$psql}", $pparams); $DB->delete_records_select('ouwiki_sections', "pageid {$psql}", $pparams); } $subwikis = $DB->get_records_sql("SELECT s.id\n FROM {ouwiki_subwikis} s\n WHERE s.wikiid = ?", array($id)); if (!empty($subwikis)) { list($swsql, $swparams) = $DB->get_in_or_equal(array_keys($subwikis)); $DB->delete_records_select('ouwiki_pages', "subwikiid {$swsql}", $swparams); } $DB->delete_records_select('ouwiki_subwikis', 'wikiid = ?', array($id)); $DB->delete_records('ouwiki', array('id' => $id)); return true; }
protected function after_execute() { global $DB, $CFG; $transaction = $DB->start_delegated_transaction(); $ouwikiid = $this->get_task()->get_activityid(); // Flush out any unsaved versions. $this->flush_versions(); // Add ouwiki related files, no need to match by itemname (just internally handled context). $this->add_related_files('mod_ouwiki', 'intro', null); $this->add_related_files('mod_ouwiki', 'template', 'ouwiki'); // Add post related files. $this->add_related_files('mod_ouwiki', 'attachment', 'ouwiki_version'); $this->add_related_files('mod_ouwiki', 'content', 'ouwiki_version'); // Update firstversionid. $sql = 'SELECT v.pageid, (SELECT MIN(id) FROM {ouwiki_versions} v3 WHERE v3.pageid = p.id AND v3.deletedat IS NULL) AS firstversionid FROM {ouwiki} o JOIN {ouwiki_subwikis} s ON s.wikiid = o.id JOIN {ouwiki_pages} p ON p.subwikiid = s.id JOIN {ouwiki_versions} v ON v.pageid = p.id WHERE o.id = ? GROUP BY v.pageid, p.id ORDER BY v.pageid'; $rs = $DB->get_recordset_sql($sql, array($ouwikiid)); foreach ($rs as $entry) { if ($entry->firstversionid) { $DB->set_field('ouwiki_pages', 'firstversionid', $entry->firstversionid, array('id' => $entry->pageid)); } } $rs->close(); // Update previousversionid. $sql = 'SELECT v.id AS versionid, (SELECT MAX(v2.id) FROM {ouwiki_versions} v2 WHERE v2.pageid = p.id AND v2.id < v.id) AS previousversionid FROM {ouwiki} o JOIN {ouwiki_subwikis} s ON s.wikiid = o.id JOIN {ouwiki_pages} p ON p.subwikiid = s.id JOIN {ouwiki_versions} v ON v.pageid = p.id WHERE o.id = ?'; $rs = $DB->get_recordset_sql($sql, array($ouwikiid)); foreach ($rs as $entry) { if ($entry->previousversionid) { $DB->set_field('ouwiki_versions', 'previousversionid', $entry->previousversionid, array('id' => $entry->versionid)); } } $rs->close(); // Update all the page ids for links. $sql = 'SELECT l.id AS linkid, l.topageid FROM {ouwiki} o JOIN {ouwiki_subwikis} s ON s.wikiid = o.id JOIN {ouwiki_pages} p ON p.subwikiid = s.id JOIN {ouwiki_versions} v ON v.pageid = p.id JOIN {ouwiki_links} l ON l.fromversionid = v.id WHERE o.id = ? AND l.topageid IS NOT NULL'; $rs = $DB->get_recordset_sql($sql, array($ouwikiid)); $errors = array(); foreach ($rs as $entry) { $newpageid = $this->get_mappingid('ouwiki_page', $entry->topageid, null); if (!$newpageid && empty($errors[$entry->topageid])) { $errors[$entry->topageid] = true; $this->get_logger()->process('OU wiki: link to missing pageid ' . $entry->topageid . ' not restored properly.', backup::LOG_WARNING); } $DB->set_field('ouwiki_links', 'topageid', $newpageid, array('id' => $entry->linkid)); } $rs->close(); // Update xhtml field with correct annotations. // Get all table entries in ouwiki_versions table for this wiki. $sql = "SELECT v.id, v.xhtml\n FROM {ouwiki_subwikis} s\n JOIN {ouwiki_pages} p ON p.subwikiid = s.id\n JOIN {ouwiki_versions} v ON v.pageid = p.id\n WHERE s.wikiid = ?\n ORDER BY v.id"; $rs = $DB->get_recordset_sql($sql, array($ouwikiid)); // Go through annotation elements ids replacing old annotation ids with new annotation ids in xhtml field of result set. foreach ($rs as $entry) { $matches = array(); // Check to see whether this contains any annotations to be replaced. $found = preg_match_all('~(span id=")(annotation[0-9]+)(")~', $entry->xhtml, $matches, PREG_SET_ORDER); if ($found) { foreach ($matches as $arr) { // Check to see whether an old array key exist. if (array_key_exists($arr[2], $this->elementsids)) { // Do the replace. $replacestr = 'span id="' . $this->elementsids[$arr[2]] . '"'; $entry->xhtml = str_replace($arr[0], $replacestr, $entry->xhtml); } } // Set the xhtml field if any annotation ids replaced. $DB->set_field('ouwiki_versions', 'xhtml', $entry->xhtml, array('id' => $entry->id)); } } // Close the result set. $rs->close(); $transaction->allow_commit(); require_once $CFG->dirroot . '/mod/ouwiki/locallib.php'; // Create search index if user data restored. if ($this->get_setting_value('userinfo') && ouwiki_search_installed()) { ouwiki_ousearch_update_all(false, $this->get_courseid()); } }
/** * Returns html for a search form for the nav bar * @param object $subwiki wiki to be searched * @param int $cmid wiki to be searched * @return string html */ function ouwiki_get_search_form($subwiki, $cmid) { if (!ouwiki_search_installed()) { return ''; } global $OUTPUT, $CFG; $queryhtml = ($query = optional_param('query', '', PARAM_RAW)) ? htmlspecialchars($query) : ''; $out = html_writer::start_tag('form', array('action' => 'search.php', 'method' => 'get')); $out .= html_writer::start_tag('div'); $out .= html_writer::tag('label', get_string('search', 'ouwiki'), array('for' => 'ouwiki_searchquery')); $out .= $OUTPUT->help_icon('search', 'ouwiki'); $out .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'id', 'value' => $cmid)); if (!$subwiki->defaultwiki) { if ($subwiki->groupid) { $out .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'group', 'value' => $subwiki->groupid)); } if ($subwiki->userid) { $out .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'user', 'value' => $subwiki->userid)); } } $out .= html_writer::empty_tag('input', array('type' => 'text', 'name' => 'query', 'id' => 'ouwiki_searchquery', 'value' => $queryhtml)); $out .= html_writer::empty_tag('input', array('type' => 'submit', 'id' => 'ousearch_searchbutton', 'value' => '', 'alt' => get_string('search'), 'title' => get_string('search'))); $out .= html_writer::end_tag('div'); $out .= html_writer::end_tag('form'); return $out; }
function ouwiki_delete_instance($id) { if (ouwiki_search_installed()) { $moduleid = get_field('modules', 'id', 'name', 'ouwiki'); $cm = get_record('course_modules', 'module', $moduleid, 'instance', $id); if (!$cm) { error('Can\'t find coursemodule'); } ousearch_document::delete_module_instance_data($cm); } global $CFG; // Subqueries that find all versions and pages associated with this wiki $versionquery = "\nSELECT\n v.id\nFROM\n {$CFG->prefix}ouwiki_subwikis s\n INNER JOIN {$CFG->prefix}ouwiki_pages p ON p.subwikiid=s.id\n INNER JOIN {$CFG->prefix}ouwiki_versions v ON v.pageid=p.id\nWHERE\n s.wikiid={$id}"; $sectionquery = "\nSELECT\n sc.id\nFROM\n {$CFG->prefix}ouwiki_subwikis s\n INNER JOIN {$CFG->prefix}ouwiki_pages p ON p.subwikiid=s.id\n INNER JOIN {$CFG->prefix}ouwiki_sections sc ON sc.pageid=p.id\nWHERE\n s.wikiid={$id}"; $pagequery = "\nSELECT\n p.id\nFROM\n {$CFG->prefix}ouwiki_subwikis s\n INNER JOIN {$CFG->prefix}ouwiki_pages p ON p.subwikiid=s.id\nWHERE\n s.wikiid={$id}"; $subwikiquery = "\nSELECT\n s.id\nFROM\n {$CFG->prefix}ouwiki_subwikis s\nWHERE\n s.wikiid={$id}"; // Delete everything, bottom-up $ok = true; $ok = delete_records_select('ouwiki_links', "fromversionid IN ({$versionquery})") && $ok; $ok = delete_records_select('ouwiki_comments', "sectionid IN ({$sectionquery})") && $ok; $ok = delete_records_select('ouwiki_versions', "pageid IN ({$pagequery})") && $ok; $ok = delete_records_select('ouwiki_locks', "pageid IN ({$pagequery})") && $ok; $ok = delete_records_select('ouwiki_sections', "pageid IN ({$pagequery})") && $ok; $ok = delete_records_select('ouwiki_pages', "subwikiid IN ({$subwikiquery})") && $ok; $ok = delete_records_select('ouwiki_subwikis', "wikiid={$id}") && $ok; $ok = delete_records("ouwiki", "id", "{$id}") && $ok; return $ok; }
function ouwiki_decode_content_links_caller($restore) { // Get all the items that might have links in, from the relevant new course try { global $CFG, $db; // 1. Summaries if ($summaries = get_records_select('ouwiki', 'course=' . $restore->course_id . ' AND summary IS NOT NULL', '', 'id,summary')) { foreach ($summaries as $summary) { $newsummary = restore_decode_content_links_worker($summary->summary, $restore); if ($newsummary != $summary->summary) { if (!set_field('ouwiki', 'summary', addslashes($newsummary), 'id', $summary->id)) { throw new Exception("Failed to set summary for wiki {$summary->id}: " . $db->ErrorMsg()); } } } } // 2. Actual content $rs = get_recordset_sql("\nSELECT\n v.id,v.xhtml \nFROM\n {$CFG->prefix}ouwiki w\n INNER JOIN {$CFG->prefix}ouwiki_subwikis s ON w.id=s.wikiid\n INNER JOIN {$CFG->prefix}ouwiki_pages p ON s.id=p.subwikiid\n INNER JOIN {$CFG->prefix}ouwiki_versions v ON p.id=v.pageid \nWHERE\n w.course={$restore->course_id} \n"); if (!$rs) { throw new Exception("Failed to query for wiki data: " . $db->ErrorMsg()); } while (!$rs->EOF) { $newcontent = restore_decode_content_links_worker($rs->fields['xhtml'], $restore); if ($newcontent != $rs->fields['xhtml']) { if (!set_field('ouwiki_versions', 'xhtml', addslashes($newcontent), 'id', $rs->fields['id'])) { throw new Exception("Failed to update content {$rs->fields['id']}: " . $db->ErrorMsg()); } } $rs->MoveNext(); } // 3. This is a bit crappy, as it isn't directly to do with content links, but // we can't do it until we have a course-module so it can't happen earlier. if (ouwiki_search_installed()) { ouwiki_ousearch_update_all(false, $restore->course_id); } return true; } catch (Exception $e) { ouwiki_handle_backup_exception($e, 'restore'); return false; } }
/** * 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(); }