diff --git a/backup/moodle2/restore_stepslib.php b/backup/moodle2/restore_stepslib.php index a41292dffdf..88e118eed11 100644 --- a/backup/moodle2/restore_stepslib.php +++ b/backup/moodle2/restore_stepslib.php @@ -1778,22 +1778,8 @@ class restore_course_structure_step extends restore_structure_step { $data = (object)$data; - if (!empty($CFG->usetags)) { // if enabled in server - // TODO: This is highly inneficient. Each time we add one tag - // we fetch all the existing because tag_set() deletes them - // so everything must be reinserted on each call - $tags = array(); - $existingtags = tag_get_tags('course', $this->get_courseid()); - // Re-add all the existitng tags - foreach ($existingtags as $existingtag) { - $tags[] = $existingtag->rawname; - } - // Add the one being restored - $tags[] = $data->rawname; - // Send all the tags back to the course - tag_set('course', $this->get_courseid(), $tags, 'core', - context_course::instance($this->get_courseid())->id); - } + core_tag_tag::add_item_tag('core', 'course', $this->get_courseid(), + context_course::instance($this->get_courseid()), $data->rawname); } public function process_allowed_module($data) { diff --git a/course/edit.php b/course/edit.php index 0189fae101b..d79c0d6ae6f 100644 --- a/course/edit.php +++ b/course/edit.php @@ -123,10 +123,7 @@ if (!empty($course)) { } // Populate course tags. - if (!empty($CFG->usetags)) { - include_once($CFG->dirroot.'/tag/lib.php'); - $course->tags = tag_get_tags_array('course', $course->id); - } + $course->tags = core_tag_tag::get_item_tags_array('core', 'course', $course->id); } else { // Editor should respect category context if course context is not set. diff --git a/course/edit_form.php b/course/edit_form.php index 94d260c2ab3..7c9f43edf99 100644 --- a/course/edit_form.php +++ b/course/edit_form.php @@ -304,11 +304,12 @@ class course_edit_form extends moodleform { } } - if (!empty($CFG->usetags) && + if (core_tag_tag::is_enabled('core', 'course') && ((empty($course->id) && guess_if_creator_will_have_course_capability('moodle/course:tag', $categorycontext)) || (!empty($course->id) && has_capability('moodle/course:tag', $coursecontext)))) { $mform->addElement('header', 'tagshdr', get_string('tags', 'tag')); - $mform->addElement('tags', 'tags', get_string('tags')); + $mform->addElement('tags', 'tags', get_string('tags'), + array('itemtype' => 'course', 'component' => 'core')); } // When two elements we need a group. diff --git a/course/lib.php b/course/lib.php index 5412f248ff8..0c2acd3601b 100644 --- a/course/lib.php +++ b/course/lib.php @@ -1716,7 +1716,7 @@ function course_delete_module($cmid) { 'criteriatype' => COMPLETION_CRITERIA_TYPE_ACTIVITY)); // Delete all tag instances associated with the instance of this module. - core_tag::delete_instances('mod_' . $modulename, null, $modcontext->id); + core_tag_tag::delete_instances('mod_' . $modulename, null, $modcontext->id); // Delete the context. context_helper::delete_instance(CONTEXT_MODULE, $cm->id); @@ -2582,7 +2582,6 @@ function course_overviewfiles_options($course) { */ function create_course($data, $editoroptions = NULL) { global $DB, $CFG; - require_once($CFG->dirroot.'/tag/lib.php'); //check the categoryid - must be given for all new courses $category = $DB->get_record('course_categories', array('id'=>$data->category), '*', MUST_EXIST); @@ -2668,8 +2667,8 @@ function create_course($data, $editoroptions = NULL) { enrol_course_updated(true, $course, $data); // Update course tags. - if ($CFG->usetags && isset($data->tags)) { - tag_set('course', $course->id, $data->tags, 'core', context_course::instance($course->id)->id); + if (isset($data->tags)) { + core_tag_tag::set_item_tags('core', 'course', $course->id, context_course::instance($course->id), $data->tags); } return $course; @@ -2687,7 +2686,6 @@ function create_course($data, $editoroptions = NULL) { */ function update_course($data, $editoroptions = NULL) { global $DB, $CFG; - require_once($CFG->dirroot.'/tag/lib.php'); $data->timemodified = time(); @@ -2775,8 +2773,8 @@ function update_course($data, $editoroptions = NULL) { enrol_course_updated(false, $course, $data); // Update course tags. - if ($CFG->usetags && isset($data->tags)) { - tag_set('course', $course->id, $data->tags, 'core', context_course::instance($course->id)->id); + if (isset($data->tags)) { + core_tag_tag::set_item_tags('core', 'course', $course->id, context_course::instance($course->id), $data->tags); } // Trigger a course updated event. @@ -3828,3 +3826,36 @@ function course_view($context, $sectionnumber = 0) { $event = \core\event\course_viewed::create($eventdata); $event->trigger(); } + +/** + * Returns courses tagged with a specified tag. + * + * @param core_tag_tag $tag + * @param bool $exclusivemode if set to true it means that no other entities tagged with this tag + * are displayed on the page and the per-page limit may be bigger + * @param int $fromctx context id where the link was displayed, may be used by callbacks + * to display items in the same context first + * @param int $ctx context id where to search for records + * @param bool $rec search in subcontexts as well + * @param int $page 0-based number of page being displayed + * @return \core_tag\output\tagindex + */ +function course_get_tagged_courses($tag, $exclusivemode = false, $fromctx = 0, $ctx = 0, $rec = 1, $page = 0) { + global $CFG, $PAGE; + require_once($CFG->libdir . '/coursecatlib.php'); + + $perpage = $exclusivemode ? $CFG->coursesperpage : 5; + $displayoptions = array( + 'limit' => $perpage, + 'offset' => $page * $perpage, + 'viewmoreurl' => null, + ); + + $courserenderer = $PAGE->get_renderer('core', 'course'); + $totalcount = coursecat::search_courses_count(array('tagid' => $tag->id, 'ctx' => $ctx, 'rec' => $rec)); + $content = $courserenderer->tagged_courses($tag->id, $exclusivemode, $ctx, $rec, $displayoptions); + $totalpages = ceil($totalcount / $perpage); + + return new core_tag\output\tagindex($tag, 'core', 'course', $content, + $exclusivemode, $fromctx, $ctx, $rec, $page, $totalpages); +} diff --git a/course/renderer.php b/course/renderer.php index ec26762998e..b71d4882d41 100644 --- a/course/renderer.php +++ b/course/renderer.php @@ -1896,29 +1896,52 @@ class core_course_renderer extends plugin_renderer_base { * Renders html to print list of courses tagged with particular tag * * @param int $tagid id of the tag + * @param bool $exclusivemode if set to true it means that no other entities tagged with this tag + * are displayed on the page and the per-page limit may be bigger + * @param int $fromctx context id where the link was displayed, may be used by callbacks + * to display items in the same context first + * @param int $ctx context id where to search for records + * @param bool $rec search in subcontexts as well + * @param array $displayoptions * @return string empty string if no courses are marked with this tag or rendered list of courses */ - public function tagged_courses($tagid) { + public function tagged_courses($tagid, $exclusivemode = true, $ctx = 0, $rec = true, $displayoptions = null) { global $CFG; - require_once($CFG->libdir. '/coursecatlib.php'); - $displayoptions = array('limit' => $CFG->coursesperpage); - $displayoptions['viewmoreurl'] = new moodle_url('/course/search.php', - array('tagid' => $tagid, 'page' => 1, 'perpage' => $CFG->coursesperpage)); - $displayoptions['viewmoretext'] = new lang_string('findmorecourses'); + require_once($CFG->libdir . '/coursecatlib.php'); + if (empty($displayoptions)) { + $displayoptions = array(); + } + $showcategories = coursecat::count_all() > 1; + $displayoptions += array('limit' => $CFG->coursesperpage, 'offset' => 0); $chelper = new coursecat_helper(); - $searchcriteria = array('tagid' => $tagid); - $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED_WITH_CAT)-> - set_search_criteria(array('tagid' => $tagid))-> + $searchcriteria = array('tagid' => $tagid, 'ctx' => $ctx, 'rec' => $rec); + $chelper->set_show_courses($showcategories ? self::COURSECAT_SHOW_COURSES_EXPANDED_WITH_CAT : + self::COURSECAT_SHOW_COURSES_EXPANDED)-> + set_search_criteria($searchcriteria)-> set_courses_display_options($displayoptions)-> set_attributes(array('class' => 'course-search-result course-search-result-tagid')); // (we set the same css class as in search results by tagid) - $courses = coursecat::search_courses($searchcriteria, $chelper->get_courses_display_options()); - $totalcount = coursecat::search_courses_count($searchcriteria); - $content = $this->coursecat_courses($chelper, $courses, $totalcount); - if ($totalcount) { - require_once $CFG->dirroot.'/tag/lib.php'; - $heading = get_string('courses') . ' ' . get_string('taggedwith', 'tag', tag_get_name($tagid)) .': '. $totalcount; - return $this->heading($heading, 3). $content; + if ($totalcount = coursecat::search_courses_count($searchcriteria)) { + $courses = coursecat::search_courses($searchcriteria, $chelper->get_courses_display_options()); + if ($exclusivemode) { + return $this->coursecat_courses($chelper, $courses, $totalcount); + } else { + $tagfeed = new core_tag\output\tagfeed(); + $img = $this->output->pix_icon('i/course', ''); + foreach ($courses as $course) { + $url = course_get_url($course); + $imgwithlink = html_writer::link($url, $img); + $coursename = html_writer::link($url, $course->get_formatted_name()); + $details = ''; + if ($showcategories && ($cat = coursecat::get($course->category, IGNORE_MISSING))) { + $details = get_string('category').': '. + html_writer::link(new moodle_url('/course/index.php', array('categoryid' => $cat->id)), + $cat->get_formatted_name(), array('class' => $cat->visible ? '' : 'dimmed')); + } + $tagfeed->add($imgwithlink, $coursename, $details); + } + return $this->output->render_from_template('core_tag/tagfeed', $tagfeed->export_for_template($this->output)); + } } return ''; } diff --git a/course/tags.php b/course/tags.php index 8e81fb1e79e..1c7ad66aedd 100644 --- a/course/tags.php +++ b/course/tags.php @@ -23,7 +23,6 @@ */ require_once("../config.php"); -require_once($CFG->dirroot . '/tag/lib.php'); require_once($CFG->dirroot . '/course/tags_form.php'); $id = required_param('id', PARAM_INT); // Course id. @@ -38,7 +37,7 @@ if (!$course->visible and !has_capability('moodle/course:viewhiddencourses', $co print_error('coursehidden', '', $CFG->wwwroot .'/'); } require_capability('moodle/course:tag', $context); -if (empty($CFG->usetags)) { +if (!core_tag_tag::is_enabled('core', 'course')) { print_error('tagsaredisabled', 'tag'); } @@ -49,14 +48,14 @@ $PAGE->set_title(get_string('coursetags', 'tag')); $PAGE->set_heading($course->fullname); $form = new coursetags_form(); -$data = array('id' => $course->id, 'tags' => tag_get_tags_array('course', $course->id)); +$data = array('id' => $course->id, 'tags' => core_tag_tag::get_item_tags_array('core', 'course', $course->id)); $form->set_data($data); $redirecturl = $returnurl ? new moodle_url($returnurl) : course_get_url($course); if ($form->is_cancelled()) { redirect($redirecturl); } else if ($data = $form->get_data()) { - tag_set('course', $course->id, $data->tags, 'core', context_course::instance($course->id)->id); + core_tag_tag::set_item_tags('core', 'course', $course->id, context_course::instance($course->id), $data->tags); redirect($redirecturl); } diff --git a/course/tags_form.php b/course/tags_form.php index 08cacc02e4a..fc54c03f11e 100644 --- a/course/tags_form.php +++ b/course/tags_form.php @@ -41,7 +41,8 @@ class coursetags_form extends moodleform { public function definition() { $mform = $this->_form; - $mform->addElement('tags', 'tags', get_string('tags')); + $mform->addElement('tags', 'tags', get_string('tags'), + array('itemtype' => 'course', 'component' => 'core')); $mform->addElement('hidden', 'id', null); $mform->setType('id', PARAM_INT); diff --git a/course/tests/courselib_test.php b/course/tests/courselib_test.php index 421b1e4c2eb..de740656f85 100644 --- a/course/tests/courselib_test.php +++ b/course/tests/courselib_test.php @@ -29,7 +29,6 @@ global $CFG; require_once($CFG->dirroot . '/course/lib.php'); require_once($CFG->dirroot . '/course/tests/fixtures/course_capability_assignment.php'); require_once($CFG->dirroot . '/enrol/imsenterprise/tests/imsenterprise_test.php'); -require_once($CFG->dirroot . '/tag/lib.php'); class core_course_courselib_testcase extends advanced_testcase { @@ -1501,6 +1500,7 @@ class core_course_courselib_testcase extends advanced_testcase { */ public function test_course_delete_module($type, $options) { global $DB; + $this->resetAfterTest(true); $this->setAdminUser(); @@ -1521,7 +1521,7 @@ class core_course_courselib_testcase extends advanced_testcase { switch ($type) { case 'assign': // Add some tags to this assignment. - tag_set('assign', $module->id, array('Tag 1', 'Tag 2', 'Tag 3'), 'mod_assign', $modcontext->id); + core_tag_tag::set_item_tags('mod_assign', 'assign', $module->id, $modcontext, array('Tag 1', 'Tag 2', 'Tag 3')); // Confirm the tag instances were added. $criteria = array('component' => 'mod_assign', 'contextid' => $modcontext->id); diff --git a/course/tests/externallib_test.php b/course/tests/externallib_test.php index c5a6464edf3..10a84ba332d 100644 --- a/course/tests/externallib_test.php +++ b/course/tests/externallib_test.php @@ -605,8 +605,7 @@ class core_course_externallib_testcase extends externallib_advanced_testcase { */ public function test_search_courses () { - global $DB, $CFG; - require_once($CFG->dirroot . '/tag/lib.php'); + global $DB; $this->resetAfterTest(true); $this->setAdminUser(); @@ -636,8 +635,10 @@ class core_course_externallib_testcase extends externallib_advanced_testcase { // Enable coursetag option. set_config('block_tags_showcoursetags', true); // Add tag 'TAG-LABEL ON SECOND COURSE' to Course2. - tag_set('course', $course2->id, array('TAG-LABEL ON SECOND COURSE'), 'core', context_course::instance($course2->id)->id); - $taginstance = $DB->get_record('tag_instance', array('itemtype' => 'course', 'itemid' => $course2->id), '*', MUST_EXIST); + core_tag_tag::set_item_tags('core', 'course', $course2->id, context_course::instance($course2->id), + array('TAG-LABEL ON SECOND COURSE')); + $taginstance = $DB->get_record('tag_instance', + array('itemtype' => 'course', 'itemid' => $course2->id), '*', MUST_EXIST); // Search by tagid. $results = core_course_external::search_courses('tagid', $taginstance->tagid); $results = external_api::clean_returnvalue(core_course_external::search_courses_returns(), $results); diff --git a/lib/coursecatlib.php b/lib/coursecatlib.php index df83db66717..fc8811f8b54 100644 --- a/lib/coursecatlib.php +++ b/lib/coursecatlib.php @@ -1339,8 +1339,27 @@ class coursecat implements renderable, cacheable_object, IteratorAggregate { } else if (!empty($search['tagid'])) { // Search courses that are tagged with the specified tag. $where = "c.id IN (SELECT t.itemid ". - "FROM {tag_instance} t WHERE t.tagid = :tagid AND t.itemtype = :itemtype)"; - $params = array('tagid' => $search['tagid'], 'itemtype' => 'course'); + "FROM {tag_instance} t WHERE t.tagid = :tagid AND t.itemtype = :itemtype AND t.component = :component)"; + $params = array('tagid' => $search['tagid'], 'itemtype' => 'course', 'component' => 'core'); + if (!empty($search['ctx'])) { + $rec = isset($search['rec']) ? $search['rec'] : true; + $parentcontext = context::instance_by_id($search['ctx']); + if ($parentcontext->contextlevel == CONTEXT_SYSTEM && $rec) { + // Parent context is system context and recursive is set to yes. + // Nothing to filter - all courses fall into this condition. + } else if ($rec) { + // Filter all courses in the parent context at any level. + $where .= ' AND ctx.path LIKE :contextpath'; + $params['contextpath'] = $parentcontext->path . '%'; + } else if ($parentcontext->contextlevel == CONTEXT_COURSECAT) { + // All courses in the given course category. + $where .= ' AND c.category = :category'; + $params['category'] = $parentcontext->instanceid; + } else { + // No courses will satisfy the context criterion, do not bother searching. + $where = '1=0'; + } + } } else { debugging('No criteria is specified while searching courses', DEBUG_DEVELOPER); return array(); diff --git a/lib/moodlelib.php b/lib/moodlelib.php index e9598eb71ca..9b0b8f9ebe7 100644 --- a/lib/moodlelib.php +++ b/lib/moodlelib.php @@ -4735,7 +4735,6 @@ function remove_course_contents($courseid, $showfeedback = true, array $options require_once($CFG->libdir.'/questionlib.php'); require_once($CFG->libdir.'/gradelib.php'); require_once($CFG->dirroot.'/group/lib.php'); - require_once($CFG->dirroot.'/tag/lib.php'); require_once($CFG->dirroot.'/comment/lib.php'); require_once($CFG->dirroot.'/rating/lib.php'); require_once($CFG->dirroot.'/notes/lib.php'); @@ -4910,7 +4909,7 @@ function remove_course_contents($courseid, $showfeedback = true, array $options $rm->delete_ratings($delopt); // Delete course tags. - tag_set('course', $course->id, array(), 'core', $coursecontext->id); + core_tag_tag::remove_all_item_tags('core', 'course', $course->id); // Delete calendar events. $DB->delete_records('event', array('courseid' => $course->id));