示例#1
0
 protected function define_execution()
 {
     global $DB;
     // Although there is some sort of auto-recovery of missing sections
     // present in course/formats... here we check that all the sections
     // from 0 to MAX(section->section) exist, creating them if necessary
     $maxsection = $DB->get_field('course_sections', 'MAX(section)', array('course' => $this->get_courseid()));
     // Iterate over all sections
     for ($i = 0; $i <= $maxsection; $i++) {
         // If the section $i doesn't exist, create it
         if (!$DB->record_exists('course_sections', array('course' => $this->get_courseid(), 'section' => $i))) {
             $sectionrec = array('course' => $this->get_courseid(), 'section' => $i);
             $DB->insert_record('course_sections', $sectionrec);
             // missing section created
         }
     }
     // Rebuild cache now that all sections are in place
     rebuild_course_cache($this->get_courseid());
     cache_helper::purge_by_event('changesincourse');
     cache_helper::purge_by_event('changesincoursecat');
 }
示例#2
0
/**
 * Changes the sort order of courses in a category so that the first course appears after the second.
 *
 * @param int|stdClass $courseorid The course to focus on.
 * @param int $moveaftercourseid The course to shifter after or 0 if you want it to be the first course in the category.
 * @return bool
 */
function course_change_sortorder_after_course($courseorid, $moveaftercourseid)
{
    global $DB;
    if (!is_object($courseorid)) {
        $course = get_course($courseorid);
    } else {
        $course = $courseorid;
    }
    if ((int) $moveaftercourseid === 0) {
        // We've moving the course to the start of the queue.
        $sql = 'SELECT sortorder
                      FROM {course}
                     WHERE category = :categoryid
                  ORDER BY sortorder';
        $params = array('categoryid' => $course->category);
        $sortorder = $DB->get_field_sql($sql, $params, IGNORE_MULTIPLE);
        $sql = 'UPDATE {course}
                   SET sortorder = sortorder + 1
                 WHERE category = :categoryid
                   AND id <> :id';
        $params = array('categoryid' => $course->category, 'id' => $course->id);
        $DB->execute($sql, $params);
        $DB->set_field('course', 'sortorder', $sortorder, array('id' => $course->id));
    } else {
        if ($course->id === $moveaftercourseid) {
            // They're the same - moronic.
            debugging("Invalid move after course given.", DEBUG_DEVELOPER);
            return false;
        } else {
            // Moving this course after the given course. It could be before it could be after.
            $moveaftercourse = get_course($moveaftercourseid);
            if ($course->category !== $moveaftercourse->category) {
                debugging("Cannot re-order courses. The given courses do not belong to the same category.", DEBUG_DEVELOPER);
                return false;
            }
            // Increment all courses in the same category that are ordered after the moveafter course.
            // This makes a space for the course we're moving.
            $sql = 'UPDATE {course}
                       SET sortorder = sortorder + 1
                     WHERE category = :categoryid
                       AND sortorder > :sortorder';
            $params = array('categoryid' => $moveaftercourse->category, 'sortorder' => $moveaftercourse->sortorder);
            $DB->execute($sql, $params);
            $DB->set_field('course', 'sortorder', $moveaftercourse->sortorder + 1, array('id' => $course->id));
        }
    }
    fix_course_sortorder();
    cache_helper::purge_by_event('changesincourse');
    return true;
}
 /**
  * Changes the sort order of this categories parent shifting this category up or down one.
  *
  * @global \moodle_database $DB
  * @param bool $up If set to true the category is shifted up one spot, else its moved down.
  * @return bool True on success, false otherwise.
  */
 public function change_sortorder_by_one($up)
 {
     global $DB;
     $params = array($this->sortorder, $this->parent);
     if ($up) {
         $select = 'sortorder < ? AND parent = ?';
         $sort = 'sortorder DESC';
     } else {
         $select = 'sortorder > ? AND parent = ?';
         $sort = 'sortorder ASC';
     }
     fix_course_sortorder();
     $swapcategory = $DB->get_records_select('course_categories', $select, $params, $sort, '*', 0, 1);
     $swapcategory = reset($swapcategory);
     if ($swapcategory) {
         $DB->set_field('course_categories', 'sortorder', $swapcategory->sortorder, array('id' => $this->id));
         $DB->set_field('course_categories', 'sortorder', $this->sortorder, array('id' => $swapcategory->id));
         $this->sortorder = $swapcategory->sortorder;
         $event = \core\event\course_category_updated::create(array('objectid' => $this->id, 'context' => $this->get_context()));
         $event->set_legacy_logdata(array(SITEID, 'category', 'move', 'management.php?categoryid=' . $this->id, $this->id));
         $event->trigger();
         // Finally reorder courses.
         fix_course_sortorder();
         cache_helper::purge_by_event('changesincoursecat');
         return true;
     }
     return false;
 }
示例#4
0
        if ($movecourse = $DB->get_record('course', array('id' => $moveup))) {
            $swapcourse = $DB->get_record('course', array('sortorder' => $movecourse->sortorder - 1));
        }
    } else {
        if ($movecourse = $DB->get_record('course', array('id' => $movedown))) {
            $swapcourse = $DB->get_record('course', array('sortorder' => $movecourse->sortorder + 1));
        }
    }
    if ($swapcourse and $movecourse) {
        // Check course's category.
        if ($movecourse->category != $id) {
            print_error('coursedoesnotbelongtocategory');
        }
        $DB->set_field('course', 'sortorder', $swapcourse->sortorder, array('id' => $movecourse->id));
        $DB->set_field('course', 'sortorder', $movecourse->sortorder, array('id' => $swapcourse->id));
        cache_helper::purge_by_event('changesincourse');
        add_to_log($movecourse->id, "course", "move", "edit.php?id=$movecourse->id", $movecourse->id);
    }
}

// Prepare the standard URL params for this page. We'll need them later.
$urlparams = array('categoryid' => $id);
if ($page) {
    $urlparams['page'] = $page;
}
if ($perpage) {
    $urlparams['perpage'] = $perpage;
}
$urlparams += $searchcriteria;

$PAGE->set_pagelayout('coursecategory');
示例#5
0
/**
 * Fixes course category and course sortorder, also verifies category and course parents and paths.
 * (circular references are not fixed)
 *
 * @global object
 * @global object
 * @uses MAX_COURSES_IN_CATEGORY
 * @uses MAX_COURSE_CATEGORIES
 * @uses SITEID
 * @uses CONTEXT_COURSE
 * @return void
 */
function fix_course_sortorder()
{
    global $DB, $SITE;
    //WARNING: this is PHP5 only code!
    // if there are any changes made to courses or categories we will trigger
    // the cache events to purge all cached courses/categories data
    $cacheevents = array();
    if ($unsorted = $DB->get_records('course_categories', array('sortorder' => 0))) {
        //move all categories that are not sorted yet to the end
        $DB->set_field('course_categories', 'sortorder', MAX_COURSES_IN_CATEGORY * MAX_COURSE_CATEGORIES, array('sortorder' => 0));
        $cacheevents['changesincoursecat'] = true;
    }
    $allcats = $DB->get_records('course_categories', null, 'sortorder, id', 'id, sortorder, parent, depth, path');
    $topcats = array();
    $brokencats = array();
    foreach ($allcats as $cat) {
        $sortorder = (int) $cat->sortorder;
        if (!$cat->parent) {
            while (isset($topcats[$sortorder])) {
                $sortorder++;
            }
            $topcats[$sortorder] = $cat;
            continue;
        }
        if (!isset($allcats[$cat->parent])) {
            $brokencats[] = $cat;
            continue;
        }
        if (!isset($allcats[$cat->parent]->children)) {
            $allcats[$cat->parent]->children = array();
        }
        while (isset($allcats[$cat->parent]->children[$sortorder])) {
            $sortorder++;
        }
        $allcats[$cat->parent]->children[$sortorder] = $cat;
    }
    unset($allcats);
    // add broken cats to category tree
    if ($brokencats) {
        $defaultcat = reset($topcats);
        foreach ($brokencats as $cat) {
            $topcats[] = $cat;
        }
    }
    // now walk recursively the tree and fix any problems found
    $sortorder = 0;
    $fixcontexts = array();
    if (_fix_course_cats($topcats, $sortorder, 0, 0, '', $fixcontexts)) {
        $cacheevents['changesincoursecat'] = true;
    }
    // detect if there are "multiple" frontpage courses and fix them if needed
    $frontcourses = $DB->get_records('course', array('category' => 0), 'id');
    if (count($frontcourses) > 1) {
        if (isset($frontcourses[SITEID])) {
            $frontcourse = $frontcourses[SITEID];
            unset($frontcourses[SITEID]);
        } else {
            $frontcourse = array_shift($frontcourses);
        }
        $defaultcat = reset($topcats);
        foreach ($frontcourses as $course) {
            $DB->set_field('course', 'category', $defaultcat->id, array('id' => $course->id));
            $context = context_course::instance($course->id);
            $fixcontexts[$context->id] = $context;
            $cacheevents['changesincourse'] = true;
        }
        unset($frontcourses);
    } else {
        $frontcourse = reset($frontcourses);
    }
    // now fix the paths and depths in context table if needed
    if ($fixcontexts) {
        foreach ($fixcontexts as $fixcontext) {
            $fixcontext->reset_paths(false);
        }
        context_helper::build_all_paths(false);
        unset($fixcontexts);
        $cacheevents['changesincourse'] = true;
        $cacheevents['changesincoursecat'] = true;
    }
    // release memory
    unset($topcats);
    unset($brokencats);
    unset($fixcontexts);
    // fix frontpage course sortorder
    if ($frontcourse->sortorder != 1) {
        $DB->set_field('course', 'sortorder', 1, array('id' => $frontcourse->id));
        $cacheevents['changesincourse'] = true;
    }
    // now fix the course counts in category records if needed
    $sql = "SELECT cc.id, cc.coursecount, COUNT(c.id) AS newcount\n              FROM {course_categories} cc\n              LEFT JOIN {course} c ON c.category = cc.id\n          GROUP BY cc.id, cc.coursecount\n            HAVING cc.coursecount <> COUNT(c.id)";
    if ($updatecounts = $DB->get_records_sql($sql)) {
        // categories with more courses than MAX_COURSES_IN_CATEGORY
        $categories = array();
        foreach ($updatecounts as $cat) {
            $cat->coursecount = $cat->newcount;
            if ($cat->coursecount >= MAX_COURSES_IN_CATEGORY) {
                $categories[] = $cat->id;
            }
            unset($cat->newcount);
            $DB->update_record_raw('course_categories', $cat, true);
        }
        if (!empty($categories)) {
            $str = implode(', ', $categories);
            debugging("The number of courses (category id: {$str}) has reached MAX_COURSES_IN_CATEGORY (" . MAX_COURSES_IN_CATEGORY . "), it will cause a sorting performance issue, please increase the value of MAX_COURSES_IN_CATEGORY in lib/datalib.php file. See tracker issue: MDL-25669", DEBUG_DEVELOPER);
        }
        $cacheevents['changesincoursecat'] = true;
    }
    // now make sure that sortorders in course table are withing the category sortorder ranges
    $sql = "SELECT DISTINCT cc.id, cc.sortorder\n              FROM {course_categories} cc\n              JOIN {course} c ON c.category = cc.id\n             WHERE c.sortorder < cc.sortorder OR c.sortorder > cc.sortorder + " . MAX_COURSES_IN_CATEGORY;
    if ($fixcategories = $DB->get_records_sql($sql)) {
        //fix the course sortorder ranges
        foreach ($fixcategories as $cat) {
            $sql = "UPDATE {course}\n                       SET sortorder = " . $DB->sql_modulo('sortorder', MAX_COURSES_IN_CATEGORY) . " + ?\n                     WHERE category = ?";
            $DB->execute($sql, array($cat->sortorder, $cat->id));
        }
        $cacheevents['changesincoursecat'] = true;
    }
    unset($fixcategories);
    // categories having courses with sortorder duplicates or having gaps in sortorder
    $sql = "SELECT DISTINCT c1.category AS id , cc.sortorder\n              FROM {course} c1\n              JOIN {course} c2 ON c1.sortorder = c2.sortorder\n              JOIN {course_categories} cc ON (c1.category = cc.id)\n             WHERE c1.id <> c2.id";
    $fixcategories = $DB->get_records_sql($sql);
    $sql = "SELECT cc.id, cc.sortorder, cc.coursecount, MAX(c.sortorder) AS maxsort, MIN(c.sortorder) AS minsort\n              FROM {course_categories} cc\n              JOIN {course} c ON c.category = cc.id\n          GROUP BY cc.id, cc.sortorder, cc.coursecount\n            HAVING (MAX(c.sortorder) <>  cc.sortorder + cc.coursecount) OR (MIN(c.sortorder) <>  cc.sortorder + 1)";
    $gapcategories = $DB->get_records_sql($sql);
    foreach ($gapcategories as $cat) {
        if (isset($fixcategories[$cat->id])) {
            // duplicates detected already
        } else {
            if ($cat->minsort == $cat->sortorder and $cat->maxsort == $cat->sortorder + $cat->coursecount - 1) {
                // easy - new course inserted with sortorder 0, the rest is ok
                $sql = "UPDATE {course}\n                       SET sortorder = sortorder + 1\n                     WHERE category = ?";
                $DB->execute($sql, array($cat->id));
            } else {
                // it needs full resorting
                $fixcategories[$cat->id] = $cat;
            }
        }
        $cacheevents['changesincourse'] = true;
    }
    unset($gapcategories);
    // fix course sortorders in problematic categories only
    foreach ($fixcategories as $cat) {
        $i = 1;
        $courses = $DB->get_records('course', array('category' => $cat->id), 'sortorder ASC, id DESC', 'id, sortorder');
        foreach ($courses as $course) {
            if ($course->sortorder != $cat->sortorder + $i) {
                $course->sortorder = $cat->sortorder + $i;
                $DB->update_record_raw('course', $course, true);
                $cacheevents['changesincourse'] = true;
            }
            $i++;
        }
    }
    // advise all caches that need to be rebuilt
    foreach (array_keys($cacheevents) as $event) {
        cache_helper::purge_by_event($event);
    }
}
 /**
  * Test the countall function
  */
 public function test_count_all()
 {
     global $DB;
     // Dont assume there is just one. An add-on might create a category as part of the install.
     $numcategories = $DB->count_records('course_categories');
     $this->assertEquals($numcategories, coursecat::count_all());
     $category1 = coursecat::create(array('name' => 'Cat1'));
     $category2 = coursecat::create(array('name' => 'Cat2', 'parent' => $category1->id));
     $category3 = coursecat::create(array('name' => 'Cat3', 'parent' => $category2->id, 'visible' => 0));
     // Now we've got three more.
     $this->assertEquals($numcategories + 3, coursecat::count_all());
     cache_helper::purge_by_event('changesincoursecat');
     // We should still have 4.
     $this->assertEquals($numcategories + 3, coursecat::count_all());
 }
示例#7
0
 /**
  * Show course category and restores visibility for child course and subcategories
  *
  * Note that there is no capability check inside this function
  *
  * This function does not update field course_categories.timemodified
  * If you want to update timemodified, use
  * $coursecat->update(array('visible' => 1));
  */
 public function show()
 {
     if ($this->show_raw()) {
         cache_helper::purge_by_event('changesincoursecat');
         add_to_log(SITEID, "category", "show", "editcategory.php?id={$this->id}", $this->id);
     }
 }
示例#8
0
 /**
  * Tests application cache event purge
  */
 public function test_application_event_purge()
 {
     $instance = cache_config_phpunittest::instance();
     $instance->phpunit_add_definition('phpunit/eventpurgetest', array('mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'eventpurgetest', 'invalidationevents' => array('crazyevent')));
     $cache = cache::make('phpunit', 'eventpurgetest');
     $this->assertTrue($cache->set('testkey1', 'test data 1'));
     $this->assertEquals('test data 1', $cache->get('testkey1'));
     $this->assertTrue($cache->set('testkey2', 'test data 2'));
     $this->assertEquals('test data 2', $cache->get('testkey2'));
     // Purge the event.
     cache_helper::purge_by_event('crazyevent');
     // Check things have been removed.
     $this->assertFalse($cache->get('testkey1'));
     $this->assertFalse($cache->get('testkey2'));
 }
示例#9
0
/**
 * Update a course.
 *
 * Please note this functions does not verify any access control,
 * the calling code is responsible for all validation (usually it is the form definition).
 *
 * @param object $data  - all the data needed for an entry in the 'course' table
 * @param array $editoroptions course description editor options
 * @return void
 */
function update_course($data, $editoroptions = NULL)
{
    global $CFG, $DB;
    $data->timemodified = time();
    $oldcourse = course_get_format($data->id)->get_course();
    $context = context_course::instance($oldcourse->id);
    if ($editoroptions) {
        $data = file_postupdate_standard_editor($data, 'summary', $editoroptions, $context, 'course', 'summary', 0);
    }
    if ($overviewfilesoptions = course_overviewfiles_options($data->id)) {
        $data = file_postupdate_standard_filemanager($data, 'overviewfiles', $overviewfilesoptions, $context, 'course', 'overviewfiles', 0);
    }
    if (!isset($data->category) or empty($data->category)) {
        // prevent nulls and 0 in category field
        unset($data->category);
    }
    $changesincoursecat = $movecat = (isset($data->category) and $oldcourse->category != $data->category);
    if (!isset($data->visible)) {
        // data not from form, add missing visibility info
        $data->visible = $oldcourse->visible;
    }
    if ($data->visible != $oldcourse->visible) {
        // reset the visibleold flag when manually hiding/unhiding course
        $data->visibleold = $data->visible;
        $changesincoursecat = true;
    } else {
        if ($movecat) {
            $newcategory = $DB->get_record('course_categories', array('id' => $data->category));
            if (empty($newcategory->visible)) {
                // make sure when moving into hidden category the course is hidden automatically
                $data->visible = 0;
            }
        }
    }
    // Update with the new data
    $DB->update_record('course', $data);
    // make sure the modinfo cache is reset
    rebuild_course_cache($data->id);
    // update course format options with full course data
    course_get_format($data->id)->update_course_format_options($data, $oldcourse);
    $course = $DB->get_record('course', array('id' => $data->id));
    if ($movecat) {
        $newparent = context_coursecat::instance($course->category);
        context_moved($context, $newparent);
    }
    fix_course_sortorder();
    // purge appropriate caches in case fix_course_sortorder() did not change anything
    cache_helper::purge_by_event('changesincourse');
    if ($changesincoursecat) {
        cache_helper::purge_by_event('changesincoursecat');
    }
    // Test for and remove blocks which aren't appropriate anymore
    blocks_remove_inappropriate($course);
    // Save any custom role names.
    save_local_role_names($course->id, $data);
    // update enrol settings
    enrol_course_updated(false, $course, $data);
    add_to_log($course->id, "course", "update", "edit.php?id={$course->id}", $course->id);
    // Trigger events
    events_trigger('course_updated', $course);
    if ($oldcourse->format !== $course->format) {
        // Remove all options stored for the previous format
        // We assume that new course format migrated everything it needed watching trigger
        // 'course_updated' and in method format_XXX::update_course_format_options()
        $DB->delete_records('course_format_options', array('courseid' => $course->id, 'format' => $oldcourse->format));
    }
}
示例#10
0
 /**
  * Resets all course/items session caches - useful in unittests when we change users and enrolments.
  */
 public static function reset_caches()
 {
     cache_helper::purge_by_event('resettagindexbuilder');
 }
示例#11
0
    public function test_get_children() {
        $category1 = coursecat::create(array('name' => 'Cat1'));
        $category2 = coursecat::create(array('name' => 'Cat2', 'parent' => $category1->id));
        $category3 = coursecat::create(array('name' => 'Cat3', 'parent' => $category1->id, 'visible' => 0));
        $category4 = coursecat::create(array('name' => 'Cat4', 'idnumber' => '12', 'parent' => $category1->id));
        $category5 = coursecat::create(array('name' => 'Cat5', 'idnumber' => '11', 'parent' => $category1->id, 'visible' => 0));
        $category6 = coursecat::create(array('name' => 'Cat6', 'idnumber' => '10', 'parent' => $category1->id));
        $category7 = coursecat::create(array('name' => 'Cat0', 'parent' => $category1->id));

        $children = $category1->get_children();
        // user does not have the capability to view hidden categories, so the list should be
        // 2,4,6,7
        $this->assertEquals(array($category2->id, $category4->id, $category6->id, $category7->id), array_keys($children));
        $this->assertEquals(4, $category1->get_children_count());

        $children = $category1->get_children(array('offset' => 2));
        $this->assertEquals(array($category6->id, $category7->id), array_keys($children));
        $this->assertEquals(4, $category1->get_children_count());

        $children = $category1->get_children(array('limit' => 2));
        $this->assertEquals(array($category2->id, $category4->id), array_keys($children));

        $children = $category1->get_children(array('offset' => 1, 'limit' => 2));
        $this->assertEquals(array($category4->id, $category6->id), array_keys($children));

        $children = $category1->get_children(array('sort' => array('name' => 1)));
        // must be 7,2,4,6
        $this->assertEquals(array($category7->id, $category2->id, $category4->id, $category6->id), array_keys($children));

        $children = $category1->get_children(array('sort' => array('idnumber' => 1, 'name' => -1)));
        // must be 2,7,6,4
        $this->assertEquals(array($category2->id, $category7->id, $category6->id, $category4->id), array_keys($children));

        // check that everything is all right after purging the caches
        cache_helper::purge_by_event('changesincoursecat');
        $children = $category1->get_children();
        $this->assertEquals(array($category2->id, $category4->id, $category6->id, $category7->id), array_keys($children));
        $this->assertEquals(4, $category1->get_children_count());
    }
示例#12
0
 /**
  * Cleans the cache.
  *
  * Aggressive deletion to be conservative given the gradebook design.
  * The key is based on the requested params, not easy nor worth to purge selectively.
  *
  * @return void
  */
 public static function clean_record_set()
 {
     cache_helper::purge_by_event('changesingradecategories');
 }
示例#13
0
 /**
  * Changes the sort order of this categories parent shifting this category up or down one.
  *
  * @global \moodle_database $DB
  * @param bool $up If set to true the category is shifted up one spot, else its moved down.
  * @return bool True on success, false otherwise.
  */
 public function change_sortorder_by_one($up)
 {
     global $DB;
     $params = array($this->sortorder, $this->parent);
     if ($up) {
         $select = 'sortorder < ? AND parent = ?';
         $sort = 'sortorder DESC';
     } else {
         $select = 'sortorder > ? AND parent = ?';
         $sort = 'sortorder ASC';
     }
     fix_course_sortorder();
     $swapcategory = $DB->get_records_select('course_categories', $select, $params, $sort, '*', 0, 1);
     $swapcategory = reset($swapcategory);
     if ($swapcategory) {
         $DB->set_field('course_categories', 'sortorder', $swapcategory->sortorder, array('id' => $this->id));
         $DB->set_field('course_categories', 'sortorder', $this->sortorder, array('id' => $swapcategory->id));
         $this->sortorder = $swapcategory->sortorder;
         add_to_log(SITEID, "category", "move", "management.php?categoryid={$this->id}", $this->id);
         // Finally reorder courses.
         fix_course_sortorder();
         cache_helper::purge_by_event('changesincoursecat');
         return true;
     }
     return false;
 }
示例#14
0
 /**
  * Test the countall function
  */
 public function test_count_all()
 {
     // There should be just the default category.
     $this->assertEquals(1, coursecat::count_all());
     $category1 = coursecat::create(array('name' => 'Cat1'));
     $category2 = coursecat::create(array('name' => 'Cat2', 'parent' => $category1->id));
     $category3 = coursecat::create(array('name' => 'Cat3', 'parent' => $category2->id, 'visible' => 0));
     // Now we've got four.
     $this->assertEquals(4, coursecat::count_all());
     cache_helper::purge_by_event('changesincoursecat');
     // We should still have 4.
     $this->assertEquals(4, coursecat::count_all());
 }