From: defacer Date: Mon, 8 Nov 2004 19:36:07 +0000 (+0000) Subject: MAJOR CHANGES: X-Git-Url: http://git.mjollnir.org/gw?a=commitdiff_plain;h=f032aa7a5f918fd3041bb502f833596ef18c7579;p=moodle.git MAJOR CHANGES: -------------- Brand-new pagelib.php at your disposal! It slices, it dices, it makes your coffee. It still doesn't understand women... Read the comments in pagelib.php for a first impression. Feedback highly encouraged! Blocks code has undergone significant changes to work with Pages. Flexibility is its middle name, since I think (without having tried it) that adding e.g. a third placeholder for "center" blocks in courses, with full support for moving around etc, will now take about 15 lines of new or edited code. If you are not impressed yet, I suggest watching a documentary on solar physics. :P MINOR CHANGES: -------------- Added instance_allow_config() in the blocks class hierarchy to supplement per-instance configuration options. You can override it to return true and thus get configuration amenities without allowing multiple instances. Minor polishing to comments and documentation (whatever caught my eye was game). Tightened up some code here and there by utilizing all the new features. BUGFIXES: --------- A bug in restore_execute.html caused hidden blocks to not be restored at all. --- diff --git a/admin/site.php b/admin/site.php index ab18e5e9e8..b4d35cfbc5 100644 --- a/admin/site.php +++ b/admin/site.php @@ -35,19 +35,17 @@ error("Serious Error! Could not update the site record! (id = $form->id)"); } } else { - // [pj] We are about to create the site, so let's add some blocks... - // calendar_month is included as a Moodle feature advertisement ;-) + // We are about to create the site "course" require_once($CFG->dirroot.'/lib/blocklib.php'); - if ($newid = insert_record("course", $form)) { + if ($newid = insert_record('course', $form)) { + // Site created, add blocks for it - $page = new stdClass; - $page->type = MOODLE_PAGE_COURSE; - $page->id = $newid; + $page = MoodlePage::create_object(MOODLE_PAGE_COURSE, $newid); blocks_repopulate_page($page); // Return value not checked because you can always edit later - $cat->name = get_string("miscellaneous"); - if (insert_record("course_categories", $cat)) { + $cat->name = get_string('miscellaneous'); + if (insert_record('course_categories', $cat)) { redirect("$CFG->wwwroot/$CFG->admin/index.php", get_string("changessaved"), 1); } else { error("Serious Error! Could not set up a default course category!"); diff --git a/backup/backuplib.php b/backup/backuplib.php index fa2b98cc4f..7a15b65f54 100644 --- a/backup/backuplib.php +++ b/backup/backuplib.php @@ -616,9 +616,7 @@ // Read all of the block table $blocks = blocks_get_record(); - $page = new stdClass; - $page->id = $preferences->backup_course; - $page->type = MOODLE_PAGE_COURSE; + $page = MoodlePage::create_object(MOODLE_PAGE_COURSE, $preferences->backup_course); if ($instances = blocks_get_by_page($page)) { //Blocks open tag diff --git a/backup/restore_execute.html b/backup/restore_execute.html index 7983b23191..cb5e8fc7f0 100644 --- a/backup/restore_execute.html +++ b/backup/restore_execute.html @@ -117,9 +117,7 @@ if (empty($info->backup_block_format)) { // This is a backup from Moodle < 1.5 if (empty($course_header->blockinfo)) { // Looks like it's from Moodle < 1.3. Let's give the course default blocks... - $newpage = new stdClass; - $newpage->type = MOODLE_PAGE_COURSE; - $newpage->id = $course_header->course_id; + $newpage = MoodlePage::create_object(MOODLE_PAGE_COURSE, $course_header->course_id); blocks_repopulate_page($newpage); } else { @@ -137,11 +135,7 @@ continue; } $blockinstance = new stdClass; - $blockinstance->blockid = $blockrecords[$blockname]->id; - $blockinstance->pageid = $course_header->course_id; - $blockinstance->pagetype = MOODLE_PAGE_COURSE; - $blockinstance->position = $blockposition; - $blockinstance->weight = $blockweight; + // Remove any - prefix before doing the name-to-id mapping if(substr($blockname, 0, 1) == '-') { $blockname = substr($blockname, 1); $blockinstance->visible = 0; @@ -149,6 +143,11 @@ else { $blockinstance->visible = 1; } + $blockinstance->blockid = $blockrecords[$blockname]->id; + $blockinstance->pageid = $course_header->course_id; + $blockinstance->pagetype = MOODLE_PAGE_COURSE; + $blockinstance->position = $blockposition; + $blockinstance->weight = $blockweight; if(!$status = insert_record('block_instance', $blockinstance)) { notify('Error while creating the course blocks'); } @@ -163,7 +162,7 @@ } } } - //Otherwise we are bringing into a course which already has blocks + //Otherwise we are adding the backup into an existing course; do nothing else { } } diff --git a/blocks/moodleblock.class.php b/blocks/moodleblock.class.php index 0167c5309a..50cc758808 100644 --- a/blocks/moodleblock.class.php +++ b/blocks/moodleblock.class.php @@ -296,8 +296,6 @@ class MoodleBlock { $pixpath = $path .'/../theme/'. $CFG->theme .'/pix'; } - $sesskeystr = '&sesskey='. $USER->sesskey; - $movebuttons = '
'; if ($this->instance->visible) { @@ -308,36 +306,34 @@ class MoodleBlock { $title = $this->str->show; } - $page = new stdClass; - $page->id = $this->instance->pageid; - $page->type = $this->instance->pagetype; - $script = page_source_script($page); + $page = MoodlePage::create_object($this->instance->pagetype, $this->instance->pageid); + $script = $page->url_get_full(array('instanceid' => $this->instance->id, 'sesskey' => $USER->sesskey)); - $movebuttons .= '' . + $movebuttons .= '' . '\"\"'; if ($options & BLOCK_CONFIGURE) { - $movebuttons .= '' . + $movebuttons .= '' . '\"\"'; } - $movebuttons .= '' . + $movebuttons .= '' . '\"\" '; if ($options & BLOCK_MOVE_LEFT) { - $movebuttons .= '' . + $movebuttons .= '' . '\"\"'; } if ($options & BLOCK_MOVE_UP) { - $movebuttons .= '' . + $movebuttons .= '' . '\"\"'; } if ($options & BLOCK_MOVE_DOWN) { - $movebuttons .= '' . + $movebuttons .= '' . '\"\"'; } if ($options & BLOCK_MOVE_RIGHT) { - $movebuttons .= '' . + $movebuttons .= '' . '\"\"'; } @@ -404,7 +400,6 @@ class MoodleBlock { * You don't need to override this if you're satisfied with the above * * @uses $CFG - * @uses $USER * @uses $THEME * @return boolean */ @@ -414,7 +409,7 @@ class MoodleBlock { if (!$this->has_config()) { return false; } - global $CFG, $USER, $THEME; + global $CFG, $THEME; print_simple_box_start('center', '', $THEME->cellheading); include($CFG->dirroot.'/blocks/'. $this->name() .'/config_global.html'); print_simple_box_end(); @@ -505,7 +500,21 @@ class MoodleBlock { } /** - *Are you going to allow multiple instances of each block? + * Is each block of this type going to have instance-specific configuration? + * Normally, this setting is controlled by {@link instance_allow_multiple}: if multiple + * instances are allowed, then each will surely need its own configuration. However, in some + * cases it may be necessary to provide instance configuration to blocks that do not want to + * allow multiple instances. In that case, make this function return true. + * I stress again that this makes a difference ONLY if {@link instance_allow_multiple} returns false. + * @return boolean + * @todo finish documenting this function by explaining per-instance configuration further + */ + function instance_allow_config() { + return false; + } + + /** + * Are you going to allow multiple instances of each block? * If yes, then it is assumed that the block WILL USE per-instance configuration * @return boolean * @todo finish documenting this function by explaining per-instance configuration further @@ -522,14 +531,13 @@ class MoodleBlock { * * @uses $CFG * @uses $THEME - * @uses $USER * @return boolean * @todo finish documenting this function */ function instance_config_print() { // Default behavior: print the config_instance.html file // You don't need to override this if you're satisfied with the above - if (!$this->instance_allow_multiple()) { + if (!$this->instance_allow_multiple() && !$this->instance_allow_config()) { return false; } global $CFG, $USER, $THEME; diff --git a/course/edit.php b/course/edit.php index 345a94d41b..2ba4f4b776 100644 --- a/course/edit.php +++ b/course/edit.php @@ -32,8 +32,6 @@ } - - /// If data submitted, then process and store. if ($form = data_submitted() and confirm_sesskey()) { @@ -54,29 +52,24 @@ if (!empty($course)) { // Test for and remove blocks which aren't appropriate anymore - $page = new stdClass; - $page->id = $course->id; - $page->type = MOODLE_PAGE_COURSE; - + $page = MoodlePage::create_object(MOODLE_PAGE_COURSE, $course->id); blocks_remove_inappropriate($page); // Update with the new data - if (update_record("course", $form)) { + if (update_record('course', $form)) { add_to_log($course->id, "course", "update", "edit.php?id=$id", ""); fix_course_sortorder(); - redirect("view.php?id=$course->id", get_string("changessaved")); + redirect($page->url_get_full(), get_string('changessaved')); } else { error("Serious Error! Could not update the course record! (id = $form->id)"); } } else { $form->timecreated = time(); - if ($newcourseid = insert_record("course", $form)) { // Set up new course + if ($newcourseid = insert_record('course', $form)) { // Set up new course // Setup the blocks - $page = new stdClass; - $page->type = MOODLE_PAGE_COURSE; - $page->id = $newcourseid; + $page = MoodlePage::create_object(MOODLE_PAGE_COURSE, $newcourseid); blocks_repopulate_page($page); // Return value not checked because you can always edit later $section = NULL; diff --git a/course/format/social/format.php b/course/format/social/format.php index c10a254eb8..ca658c7619 100644 --- a/course/format/social/format.php +++ b/course/format/social/format.php @@ -27,7 +27,7 @@ if(blocks_have_content($pageblocks[BLOCK_POS_LEFT]) || $editing) { echo ''; - blocks_print_group($page, $pageblocks[BLOCK_POS_LEFT]); + blocks_print_group($PAGE, $pageblocks[BLOCK_POS_LEFT]); echo ''; } @@ -56,9 +56,9 @@ // The right column if(blocks_have_content($pageblocks[BLOCK_POS_RIGHT]) || $editing) { echo ''; - blocks_print_group($page, $pageblocks[BLOCK_POS_RIGHT]); + blocks_print_group($PAGE, $pageblocks[BLOCK_POS_RIGHT]); if ($editing && !empty($missingblocks)) { - blocks_print_adminblock($page, $missingblocks); + blocks_print_adminblock($PAGE, $missingblocks); } echo ''; } diff --git a/course/format/topics/format.php b/course/format/topics/format.php index 41316e7e53..a54cca31e8 100644 --- a/course/format/topics/format.php +++ b/course/format/topics/format.php @@ -65,7 +65,7 @@ if(blocks_have_content($pageblocks[BLOCK_POS_LEFT]) || $editing) { echo ''; - blocks_print_group($page, $pageblocks[BLOCK_POS_LEFT]); + blocks_print_group($PAGE, $pageblocks[BLOCK_POS_LEFT]); echo ''; } @@ -272,9 +272,9 @@ // The right column if(blocks_have_content($pageblocks[BLOCK_POS_RIGHT]) || $editing) { echo ''; - blocks_print_group($page, $pageblocks[BLOCK_POS_RIGHT]); + blocks_print_group($PAGE, $pageblocks[BLOCK_POS_RIGHT]); if ($editing && !empty($missingblocks)) { - blocks_print_adminblock($page, $missingblocks); + blocks_print_adminblock($PAGE, $missingblocks); } echo ''; } diff --git a/course/format/weeks/format.php b/course/format/weeks/format.php index ef339d29b9..3b54b5b722 100644 --- a/course/format/weeks/format.php +++ b/course/format/weeks/format.php @@ -56,7 +56,7 @@ if(blocks_have_content($pageblocks[BLOCK_POS_LEFT]) || $editing) { echo ''; - blocks_print_group($page, $pageblocks[BLOCK_POS_LEFT]); + blocks_print_group($PAGE, $pageblocks[BLOCK_POS_LEFT]); echo ''; } @@ -258,9 +258,9 @@ // The right column if(blocks_have_content($pageblocks[BLOCK_POS_RIGHT]) || $editing) { echo ''; - blocks_print_group($page, $pageblocks[BLOCK_POS_RIGHT]); + blocks_print_group($PAGE, $pageblocks[BLOCK_POS_RIGHT]); if ($editing && !empty($missingblocks)) { - blocks_print_adminblock($page, $missingblocks); + blocks_print_adminblock($PAGE, $missingblocks); } echo ''; } diff --git a/course/view.php b/course/view.php index 2f30c82d28..7ac1022be3 100644 --- a/course/view.php +++ b/course/view.php @@ -37,11 +37,8 @@ $course->format = 'weeks'; // Default format is weeks } - $page = new stdClass; - $page->id = $course->id; - $page->type = MOODLE_PAGE_COURSE; - - $pageblocks = blocks_get_by_page($page); + $PAGE = MoodlePage::create_object(MOODLE_PAGE_COURSE, $course->id); + $pageblocks = blocks_get_by_page($PAGE); if (!isset($USER->editing)) { $USER->editing = false; @@ -70,19 +67,19 @@ if (!empty($blockaction) && confirm_sesskey()) { if (!empty($blockid)) { - blocks_execute_action($page, $pageblocks, strtolower($blockaction), intval($blockid)); + blocks_execute_action($PAGE, $pageblocks, strtolower($blockaction), intval($blockid)); } else if (!empty($instanceid)) { $instance = blocks_find_instance($instanceid, $pageblocks); - blocks_execute_action($page, $pageblocks, strtolower($blockaction), $instance); + blocks_execute_action($PAGE, $pageblocks, strtolower($blockaction), $instance); } // This re-query could be eliminated by judicious programming in blocks_execute_action(), // but I 'm not sure if it's worth the complexity increase... - $pageblocks = blocks_get_by_page($page); + $pageblocks = blocks_get_by_page($PAGE); } - $missingblocks = blocks_get_missing($page, $pageblocks); + $missingblocks = blocks_get_missing($PAGE, $pageblocks); if (!empty($section)) { if (!empty($move) and confirm_sesskey()) { @@ -101,12 +98,7 @@ redirect("$CFG->wwwroot/"); } - $strcourse = get_string("course"); - - $loggedinas = "

".user_login_string($course, $USER)."

"; - - print_header("$strcourse: $course->fullname", "$course->fullname", "$course->shortname", - "", "", true, update_course_icon($course->id), $loggedinas); + $PAGE->print_header(get_string('course').': %fullname%'); get_all_mods($course->id, $mods, $modnames, $modnamesplural, $modnamesused); diff --git a/index.php b/index.php index d3bb6d1fec..a2d7d774af 100644 --- a/index.php +++ b/index.php @@ -55,34 +55,32 @@ $langmenu = popup_form ($CFG->wwwroot .'/index.php?lang=', $langs, 'chooselang', $currlang, '', '', '', true); } + $PAGE = MoodlePage::create_object(MOODLE_PAGE_COURSE, SITEID); + print_header(strip_tags($site->fullname), $site->fullname, 'home', '', '', true, '', $loginstring . $langmenu); $editing = isediting($site->id); - $page = new stdClass; - $page->id = SITEID; - $page->type = MOODLE_PAGE_COURSE; - - $pageblocks = blocks_get_by_page($page); + $pageblocks = blocks_get_by_page($PAGE); if($editing) { if (!empty($blockaction) && confirm_sesskey()) { if (!empty($blockid)) { - blocks_execute_action($page, $pageblocks, strtolower($blockaction), intval($blockid)); + blocks_execute_action($PAGE, $pageblocks, strtolower($blockaction), intval($blockid)); } else if (!empty($instanceid)) { $instance = blocks_find_instance($instanceid, $pageblocks); - blocks_execute_action($page, $pageblocks, strtolower($blockaction), $instance); + blocks_execute_action($PAGE, $pageblocks, strtolower($blockaction), $instance); } // This re-query could be eliminated by judicious programming in blocks_execute_action(), // but I'm not sure if it's worth the complexity increase... - $pageblocks = blocks_get_by_page($page); + $pageblocks = blocks_get_by_page($PAGE); } - $missingblocks = blocks_get_missing($page, $pageblocks); + $missingblocks = blocks_get_missing($PAGE, $pageblocks); } optional_variable($preferred_width_left, blocks_preferred_width($pageblocks[BLOCK_POS_LEFT])); @@ -101,7 +99,7 @@ if(blocks_have_content($pageblocks[BLOCK_POS_LEFT]) || $editing) { echo ''; - blocks_print_group($page, $pageblocks[BLOCK_POS_LEFT]); + blocks_print_group($PAGE, $pageblocks[BLOCK_POS_LEFT]); echo ''; } @@ -216,9 +214,9 @@ echo '
'.update_course_icon($site->id).'
'; echo '
'; } - blocks_print_group($page, $pageblocks[BLOCK_POS_RIGHT]); + blocks_print_group($PAGE, $pageblocks[BLOCK_POS_RIGHT]); if ($editing && !empty($missingblocks)) { - blocks_print_adminblock($page, $missingblocks); + blocks_print_adminblock($PAGE, $missingblocks); } echo ''; } diff --git a/lib/blocklib.php b/lib/blocklib.php index 76440ac1c2..6346572e1c 100644 --- a/lib/blocklib.php +++ b/lib/blocklib.php @@ -8,91 +8,10 @@ define('BLOCK_MOVE_UP', 0x04); define('BLOCK_MOVE_DOWN', 0x08); define('BLOCK_CONFIGURE', 0x10); -define('MOODLE_PAGE_COURSE', 'course'); define('BLOCK_POS_LEFT', 'l'); define('BLOCK_POS_RIGHT', 'r'); -function page_get_format($page) { - switch($page->type) { - case MOODLE_PAGE_COURSE: - if($page->id == SITEID) { - return 'site'; - } - else { - $course = get_record('course', 'id', $page->id); - return $course->format; - } - break; - } - return NULL; -} - -function blocks_get_missing($page, $pageblocks) { - $missingblocks = array(); - $allblocks = blocks_get_record(); - - if(!empty($allblocks)) { - foreach($allblocks as $block) { - if($block->visible && (!blocks_find_block($block->id, $pageblocks) || $block->multiple)) { - // And if it's applicable for display in this format... - $formats = block_method_result($block->name, 'applicable_formats'); - $pageformat = page_get_format($page); - if(isset($formats[$pageformat]) ? $formats[$pageformat] : !empty($formats['all'])) { - // Add it to the missing blocks - $missingblocks[] = $block->id; - } - } - } - } - return $missingblocks; -} - -function blocks_remove_inappropriate($page) { - $pageblocks = blocks_get_by_page($page); - - if(empty($pageblocks)) { - return; - } - - switch($page->type) { - case MOODLE_PAGE_COURSE: - $course = get_record('course', 'id', $page->id); - if($page->id == SITEID) { - $pageformat = 'site'; - } - else { - $pageformat = $course->format; - } - break; - default: - return; - break; - } - - foreach($pageblocks as $position) { - foreach($position as $instance) { - $block = blocks_get_record($instance->blockid); - $formats = block_method_result($block->name, 'applicable_formats'); - if(! (isset($formats[$pageformat]) ? $formats[$pageformat] : !empty($formats['all']))) { - // Translation: if the course format is explicitly accepted/rejected, use - // that setting. Otherwise, fallback to the 'all' format. The empty() test - // uses the trick that empty() fails if 'all' is either !isset() or false. - - blocks_delete_instance($instance); - } - } - } -} - -function blocks_delete_instance($instance) { - global $CFG; - - delete_records('block_instance', 'id', $instance->id); - // And now, decrement the weight of all blocks after this one - execute_sql('UPDATE '.$CFG->prefix.'block_instance SET weight = weight - 1 WHERE pagetype = \''.$instance->pagetype. - '\' AND pageid = '.$instance->pageid.' AND position = \''.$instance->position. - '\' AND weight > '.$instance->weight, false); -} +require_once($CFG->libdir.'/pagelib.php'); // Returns the case-sensitive name of the class' constructor function. This includes both // PHP5- and PHP4-style constructors. If no appropriate constructor can be found, returns NULL. @@ -151,7 +70,7 @@ function block_instance($blockname, $instance = NULL) { return false; } $classname = 'CourseBlock_'.$blockname; - $retval = New $classname; + $retval = new $classname; if($instance !== NULL) { $retval->load_instance($instance); } @@ -171,6 +90,63 @@ function block_load_class($blockname) { return class_exists($classname); } +function blocks_get_missing($page, $pageblocks) { + + $missingblocks = array(); + $allblocks = blocks_get_record(); + $pageformat = $page->get_format_name(); + + if(!empty($allblocks)) { + foreach($allblocks as $block) { + if($block->visible && (!blocks_find_block($block->id, $pageblocks) || $block->multiple)) { + // And if it's applicable for display in this format... + $formats = block_method_result($block->name, 'applicable_formats'); + if(isset($formats[$pageformat]) ? $formats[$pageformat] : !empty($formats['all'])) { + // Add it to the missing blocks + $missingblocks[] = $block->id; + } + } + } + } + return $missingblocks; +} + +function blocks_remove_inappropriate($page) { + $pageblocks = blocks_get_by_page($page); + + if(empty($pageblocks)) { + return; + } + + if(($pageformat = $page->get_format_name()) == NULL) { + return; + } + + foreach($pageblocks as $position) { + foreach($position as $instance) { + $block = blocks_get_record($instance->blockid); + $formats = block_method_result($block->name, 'applicable_formats'); + if(! (isset($formats[$pageformat]) ? $formats[$pageformat] : !empty($formats['all']))) { + // Translation: if the course format is explicitly accepted/rejected, use + // that setting. Otherwise, fallback to the 'all' format. The empty() test + // uses the trick that empty() fails if 'all' is either !isset() or false. + + blocks_delete_instance($instance); + } + } + } +} + +function blocks_delete_instance($instance) { + global $CFG; + + delete_records('block_instance', 'id', $instance->id); + // And now, decrement the weight of all blocks after this one + execute_sql('UPDATE '.$CFG->prefix.'block_instance SET weight = weight - 1 WHERE pagetype = \''.$instance->pagetype. + '\' AND pageid = '.$instance->pageid.' AND position = \''.$instance->position. + '\' AND weight > '.$instance->weight, false); +} + function blocks_have_content($instances) { foreach($instances as $instance) { if(!$instance->visible) { @@ -198,26 +174,14 @@ function blocks_have_content($instances) { return false; } -//This function print the one side of blocks in course main page +//This function prints one side of the blocks in the course main page function blocks_print_group($page, $instances) { if(empty($instances)) { return; } - switch($page->type) { - case MOODLE_PAGE_COURSE: - $isediting = isediting($page->id); - $ismoving = ismoving($page->id); - $isteacheredit = isteacheredit($page->id); - break; - } - - // Include the base class - @include_once($CFG->dirroot.'/blocks/moodleblock.class.php'); - if(!class_exists('moodleblock')) { - error('Class MoodleBlock is not defined or file not found for /course/blocks/moodleblock.class.php'); - } + $isediting = $page->user_is_editing(); $maxweight = max(array_keys($instances)); @@ -230,14 +194,20 @@ function blocks_print_group($page, $instances) { $obj = block_instance($block->name, $instance); - if ($isediting && !$ismoving && $isteacheredit) { + if ($isediting) { $options = 0; - $options |= BLOCK_MOVE_UP * ($instance->weight != 0); - $options |= BLOCK_MOVE_DOWN * ($instance->weight != $maxweight); - $options |= BLOCK_MOVE_RIGHT * ($instance->position != BLOCK_POS_RIGHT); - $options |= BLOCK_MOVE_LEFT * ($instance->position != BLOCK_POS_LEFT); - // DH - users can configure this instance if the block class allows multiple instances, not just if the administrator has allowed this block class to display multiple for the given site as would be found in $block->multiple - $options |= BLOCK_CONFIGURE * ( $obj->instance_allow_multiple() ); + // The block can be moved up if it's NOT the first one in its position. If it is, we look at the OR clause: + // the first block might still be able to move up if the page says so (i.e., it will change position) + $options |= BLOCK_MOVE_UP * ($instance->weight != 0 || ($page->blocks_move_position($instance, BLOCK_MOVE_UP) != $instance->position)); + // Same thing for downward movement + $options |= BLOCK_MOVE_DOWN * ($instance->weight != $maxweight || ($page->blocks_move_position($instance, BLOCK_MOVE_DOWN) != $instance->position)); + // For left and right movements, it's up to the page to tell us whether they are allowed + $options |= BLOCK_MOVE_RIGHT * ($page->blocks_move_position($instance, BLOCK_MOVE_RIGHT) != $instance->position); + $options |= BLOCK_MOVE_LEFT * ($page->blocks_move_position($instance, BLOCK_MOVE_LEFT ) != $instance->position); + // Finally, the block can be configured if the block class either allows multiple instances, or if it specifically + // allows instance configuration (multiple instances override that one). It doesn't have anything to do with what the + // administrator has allowed for this block in the site admin options. + $options |= BLOCK_CONFIGURE * ( $obj->instance_allow_multiple() || $obj->instance_allow_config() ); $obj->add_edit_controls($options); } @@ -322,8 +292,7 @@ function blocks_execute_action($page, &$pageblocks, $blockaction, $instanceorid) switch($blockaction) { case 'config': - // Series of ugly hacks following... - global $course, $USER; // First hack; we need $course to print out the headers + global $USER; $block = blocks_get_record($instance->blockid); $blockobject = block_instance($block->name, $instance); if ($blockobject === false) { @@ -335,15 +304,12 @@ function blocks_execute_action($page, &$pageblocks, $blockaction, $instanceorid) // so we can strip them from the submitted data BEFORE serializing it. $hiddendata = array( 'sesskey' => $USER->sesskey, - 'id' => $course->id, 'instanceid' => $instance->id, 'blockaction' => 'config' ); - // The 'id' thing is a crude hack in all its glory... - // Redirecting the form submission back to ourself with qualified_me() was a good idea since otherwise - // we'd need to have an "extra" script that would have to infer where to redirect us back just from - // the data in $instance (pagetype and pageid). But, "ourself" is most likely course/view.php and it needs - // a course id. Hence the hack. + + // To this data, add anything the page itself needs to display + $hiddendata = array_merge($hiddendata, $page->url_get_parameters()); if($data = data_submitted()) { $remove = array_keys($hiddendata); @@ -356,11 +322,10 @@ function blocks_execute_action($page, &$pageblocks, $blockaction, $instanceorid) // And nothing more, continue with displaying the page } else { - $loggedinas = '

'. user_login_string($course, $USER) .'

'; - print_header(get_string('blockconfigin', 'moodle', $course->fullname), $course->fullname, $course->shortname, - '', '', true, update_course_icon($course->id), $loggedinas); + // We need to show the config screen, so we highjack the display logic and then die + $page->print_header(get_string('pageheaderconfigablock', 'moodle')); print_heading(get_string('blockconfiga', 'moodle', $block->name)); - echo '
'; // This I wouldn't call a hack but it sure looks cheeky + echo ''; echo '

'; foreach($hiddendata as $name => $val) { echo ''; @@ -369,7 +334,7 @@ function blocks_execute_action($page, &$pageblocks, $blockaction, $instanceorid) $blockobject->instance_config_print(); echo '

'; print_footer(); - die(); // Do not go on with the other course-related stuff + die(); // Do not go on with the other page-related stuff } break; case 'toggle': @@ -389,79 +354,81 @@ function blocks_execute_action($page, &$pageblocks, $blockaction, $instanceorid) if(empty($instance)) { error('Invalid block instance for '. $blockaction); } - // This configuration will make sure that even if somehow the weights - // become not continuous, block move operations will eventually bring - // the situation back to normal without printing any warnings. - if(!empty($pageblocks[$instance->position][$instance->weight - 1])) { - $other = $pageblocks[$instance->position][$instance->weight - 1]; + + if($instance->weight == 0) { + // The block is the first one, so a move "up" probably means it changes position + // Where is the instance going to be moved? + $newpos = $page->blocks_move_position($instance, BLOCK_MOVE_UP); + $newweight = max(array_keys($pageblocks[$newpos])) + 1; + + blocks_execute_repositioning($instance, $newpos, $newweight); } - if(!empty($other)) { - ++$other->weight; - update_record('block_instance', $other); + else { + // The block is just moving upwards in the same position. + // This configuration will make sure that even if somehow the weights + // become not continuous, block move operations will eventually bring + // the situation back to normal without printing any warnings. + if(!empty($pageblocks[$instance->position][$instance->weight - 1])) { + $other = $pageblocks[$instance->position][$instance->weight - 1]; + } + if(!empty($other)) { + ++$other->weight; + update_record('block_instance', $other); + } + --$instance->weight; + update_record('block_instance', $instance); } - --$instance->weight; - update_record('block_instance', $instance); break; case 'movedown': if(empty($instance)) { error('Invalid block instance for '. $blockaction); } - // This configuration will make sure that even if somehow the weights - // become not continuous, block move operations will eventually bring - // the situation back to normal without printing any warnings. - if(!empty($pageblocks[$instance->position][$instance->weight + 1])) { - $other = $pageblocks[$instance->position][$instance->weight + 1]; + + if($instance->weight == max(array_keys($pageblocks[$instance->position]))) { + // The block is the last one, so a move "down" probably means it changes position + // Where is the instance going to be moved? + $newpos = $page->blocks_move_position($instance, BLOCK_MOVE_DOWN); + $newweight = max(array_keys($pageblocks[$newpos])) + 1; + + blocks_execute_repositioning($instance, $newpos, $newweight); } - if(!empty($other)) { - --$other->weight; - update_record('block_instance', $other); + else { + // The block is just moving downwards in the same position. + // This configuration will make sure that even if somehow the weights + // become not continuous, block move operations will eventually bring + // the situation back to normal without printing any warnings. + if(!empty($pageblocks[$instance->position][$instance->weight + 1])) { + $other = $pageblocks[$instance->position][$instance->weight + 1]; + } + if(!empty($other)) { + --$other->weight; + update_record('block_instance', $other); + } + ++$instance->weight; + update_record('block_instance', $instance); } - ++$instance->weight; - update_record('block_instance', $instance); break; case 'moveleft': if(empty($instance)) { error('Invalid block instance for '. $blockaction); } - $sql = ''; - switch($instance->position) { - case BLOCK_POS_RIGHT: - // To preserve the continuity of block weights - $sql = 'UPDATE '. $CFG->prefix .'block_instance SET weight = weight - 1 WHERE pagetype = \''. $instance->pagetype. - '\' AND pageid = '. $instance->pageid .' AND position = \'' .$instance->position. - '\' AND weight > '. $instance->weight; - - $instance->position = BLOCK_POS_LEFT; - $maxweight = max(array_keys($pageblocks[$instance->position])); - $instance->weight = $maxweight + 1; - break; - } - if($sql) { - update_record('block_instance', $instance); - execute_sql($sql, false); - } + + // Where is the instance going to be moved? + $newpos = $page->blocks_move_position($instance, BLOCK_MOVE_LEFT); + $newweight = max(array_keys($pageblocks[$newpos])) + 1; + + blocks_execute_repositioning($instance, $newpos, $newweight); break; case 'moveright': if(empty($instance)) { error('Invalid block instance for '. $blockaction); } - $sql = ''; - switch($instance->position) { - case BLOCK_POS_LEFT: - // To preserve the continuity of block weights - $sql = 'UPDATE '. $CFG->prefix .'block_instance SET weight = weight - 1 WHERE pagetype = \''. $instance->pagetype. - '\' AND pageid = '. $instance->pageid .' AND position = \''. $instance->position. - '\' AND weight > '. $instance->weight; - - $instance->position = BLOCK_POS_RIGHT; - $maxweight = max(array_keys($pageblocks[$instance->position])); - $instance->weight = $maxweight + 1; - break; - } - if($sql) { - update_record('block_instance', $instance); - execute_sql($sql, false); - } + + // Where is the instance going to be moved? + $newpos = $page->blocks_move_position($instance, BLOCK_MOVE_RIGHT); + $newweight = max(array_keys($pageblocks[$newpos])) + 1; + + blocks_execute_repositioning($instance, $newpos, $newweight); break; case 'add': // Add a new instance of this block, if allowed @@ -477,25 +444,56 @@ function blocks_execute_action($page, &$pageblocks, $blockaction, $instanceorid) return false; } - $weight = get_record_sql('SELECT 1, max(weight) + 1 AS nextfree FROM '. $CFG->prefix .'block_instance WHERE pageid = '. $page->id .' AND pagetype = \''. $page->type .'\' AND position = \''. BLOCK_POS_RIGHT .'\''); + $newpos = $page->blocks_default_position(); + $weight = get_record_sql('SELECT 1, max(weight) + 1 AS nextfree FROM '. $CFG->prefix .'block_instance WHERE pageid = '. $page->get_id() .' AND pagetype = \''. $page->get_type() .'\' AND position = \''. $newpos .'\''); $newinstance = new stdClass; $newinstance->blockid = $blockid; - $newinstance->pageid = $page->id; - $newinstance->pagetype = $page->type; - $newinstance->position = BLOCK_POS_RIGHT; + $newinstance->pageid = $page->get_id(); + $newinstance->pagetype = $page->get_type(); + $newinstance->position = $newpos; $newinstance->weight = $weight->nextfree; $newinstance->visible = 1; $newinstance->configdata = ''; insert_record('block_instance', $newinstance); break; } + + // In order to prevent accidental duplicate actions, redirect to a page with a clean url + redirect($page->url_get_full()); +} + +// This shouldn't be used externally at all, it's here for use by blocks_execute_action() +// in order to reduce code repetition. +function blocks_execute_repositioning(&$instance, $newpos, $newweight) { + global $CFG; + + // If it's staying where it is, don't do anything + if($newpos == $instance->position) { + return; + } + + // Close the weight gap we 'll leave behind + execute_sql('UPDATE '. $CFG->prefix .'block_instance SET weight = weight - 1 WHERE pagetype = \''. $instance->pagetype. + '\' AND pageid = '. $instance->pageid .' AND position = \'' .$instance->position. + '\' AND weight > '. $instance->weight, + false); + + $instance->position = $newpos; + $instance->weight = $newweight; + + update_record('block_instance', $instance); } function blocks_get_by_page($page) { - $blocks = get_records_select('block_instance', 'pageid = '. $page->id .' AND pagetype = \''. $page->type .'\'', 'position, weight'); + $blocks = get_records_select('block_instance', 'pageid = '. $page->get_id() .' AND pagetype = \''. $page->get_type() .'\'', 'position, weight'); + + $positions = $page->blocks_get_positions(); + $arr = array(); + foreach($positions as $key => $position) { + $arr[$position] = array(); + } - $arr = array(BLOCK_POS_LEFT => array(), BLOCK_POS_RIGHT => array()); if(empty($blocks)) { return $arr; } @@ -516,14 +514,6 @@ function blocks_print_adminblock($page, $missingblocks) { if (!empty($missingblocks)) { foreach ($missingblocks as $blockid) { $block = blocks_get_record($blockid); - - switch($page->type) { - case MOODLE_PAGE_COURSE: - $course = get_record('course', 'id', $page->id); - break; - default: die('unknown pagetype: '. $page->type); - } - $blockobject = block_instance($block->name); if ($blockobject === false) { continue; @@ -531,15 +521,9 @@ function blocks_print_adminblock($page, $missingblocks) { $menu[$block->id] = $blockobject->get_title(); } - if($page->id == SITEID) { - $target = 'index.php'; - } - else { - $target = 'view.php'; - } - $content = popup_form($target .'?id='. $course->id .'&sesskey='. $USER->sesskey .'&blockaction=add&blockid=', - $menu, 'add_block', '', $stradd .'...', '', '', true); - $content = '
'. $content .'
'; + $target = $page->url_get_full(array('sesskey' => $USER->sesskey, 'blockaction' => 'add')); + $content = popup_form($target.'&blockid=', $menu, 'add_block', '', $stradd .'...', '', '', true); + $content = '
'. $content .'
'; print_side_block($strblocks, $content, NULL, NULL, NULL); } } @@ -547,88 +531,55 @@ function blocks_print_adminblock($page, $missingblocks) { function blocks_repopulate_page($page) { global $CFG; - /// If the site override has been defined, it is the only valid one. - if (!empty($CFG->defaultblocks_override)) { - $blocknames = $CFG->defaultblocks_override; - } - /// Else, try to find out what page this is - else { - switch($page->type) { - case MOODLE_PAGE_COURSE: - // Is it the site? - if($page->id == SITEID) { - if (!empty($CFG->defaultblocks_site)) { - $blocknames = $CFG->defaultblocks_site; - } - /// Failsafe - in case nothing was defined. - else { - $blocknames = 'site_main_menu,admin,course_list:course_summary,calendar_month'; - } - } - // It's a normal course, so do it accodring to the course format - else { - $course = get_record('course', 'id', $page->id); - if (!empty($CFG->{'defaultblocks_'. $course->format})) { - $blocknames = $CFG->{'defaultblocks_'. $course->format}; - } - else { - $format_config = $CFG->dirroot.'/course/format/'.$course->format.'/config.php'; - if (@is_file($format_config) && is_readable($format_config)) { - require($format_config); - } - if (!empty($format['defaultblocks'])) { - $blocknames = $format['defaultblocks']; - } - else if (!empty($CFG->defaultblocks)){ - $blocknames = $CFG->defaultblocks; - } - /// Failsafe - in case nothing was defined. - else { - $blocknames = 'participants,activity_modules,search_forums,admin,course_list:news_items,calendar_upcoming,recent_activity'; - } - } - } - break; - default: - error('Invalid page type: '. $page->type); - break; - } - } - $allblocks = blocks_get_record(); if(empty($allblocks)) { error('Could not retrieve blocks from the database'); } - // We have the blocks, make up two arrays - $left = ''; - $right = ''; - @list($left, $right) = explode(':', $blocknames); - $instances = array(BLOCK_POS_LEFT => explode(',', $left), BLOCK_POS_RIGHT => explode(',', $right)); - - // Arrays are fine, now we have to correlate block names to ids + // Assemble the information to correlate block names to ids $idforname = array(); foreach($allblocks as $block) { $idforname[$block->name] = $block->id; } - // Ready to start creating block instances, but first drop any existing ones - delete_records('block_instance', 'pageid', $page->id, 'pagetype', $page->type); + /// If the site override has been defined, it is the only valid one. + if (!empty($CFG->defaultblocks_override)) { + $blocknames = $CFG->defaultblocks_override; + } + else { + $blocknames = $page->blocks_get_default(); + } + + $positions = $page->blocks_get_positions(); + $posblocks = explode(':', $blocknames); + + // Now one array holds the names of the positions, and the other one holds the blocks + // that are going to go in each position. Luckily for us, both arrays are numerically + // indexed and the indexes match, so we can work straight away... but CAREFULLY! - foreach($instances as $position => $blocknames) { + // Ready to start creating block instances, but first drop any existing ones + delete_records('block_instance', 'pageid', $page->get_id(), 'pagetype', $page->get_type()); + + // Here we slyly count $posblocks and NOT $positions. This can actually make a difference + // if the textual representation has undefined slots in the end. So we only work with as many + // positions were retrieved, not with all the page says it has available. + $numpositions = count($posblocks); + for($i = 0; $i < $numpositions; ++$i) { + $position = $positions[$i]; + $blocknames = explode(',', $posblocks[$i]); $weight = 0; foreach($blocknames as $blockname) { $newinstance = new stdClass; $newinstance->blockid = $idforname[$blockname]; - $newinstance->pageid = $page->id; - $newinstance->pagetype = $page->type; + $newinstance->pageid = $page->get_id(); + $newinstance->pagetype = $page->get_type(); $newinstance->position = $position; $newinstance->weight = $weight; $newinstance->visible = 1; $newinstance->configdata = ''; insert_record('block_instance', $newinstance); - $weight++; + ++$weight; } } @@ -884,18 +835,14 @@ function upgrade_blocks_plugins($continueto) { //Iterate over each course if ($courses = get_records('course')) { foreach ($courses as $course) { - $page = new stdClass; - $page->type = MOODLE_PAGE_COURSE; - $page->id = $course->id; + $page = MoodlePage::create_object(MOODLE_PAGE_COURSE, $course->id); blocks_repopulate_page($page); } } } if (!empty($CFG->siteblocksadded)) { /// This is a once-off hack to make a proper upgrade - $page = new stdClass; - $page->type = MOODLE_PAGE_COURSE; - $page->id = SITEID; + $page = MoodlePage::create_object(MOODLE_PAGE_COURSE, SITEID); blocks_repopulate_page($page); delete_records('config', 'name', 'siteblocksadded'); } diff --git a/lib/pagelib.php b/lib/pagelib.php new file mode 100644 index 0000000000..1d1464e31e --- /dev/null +++ b/lib/pagelib.php @@ -0,0 +1,287 @@ +url_get_path(); + if(empty($path)) { + return NULL; + } + + $params = $this->url_get_parameters(); + $params = array_merge($params, $extraparams); + + if(empty($params)) { + return $path; + } + + $first = true; + + foreach($params as $var => $value) { + $path .= $first? '?' : '&'; + $path .= $var.'='.urlencode($value); + $first = false; + } + + return $path; + } + function get_type() { + error('get_type() called on a page object which is not correctly implemented'); + } + function get_id() { + return $this->id; + } + function get_format_name() { + return NULL; + } + function user_allowed_editing() { + return false; + } + function user_is_editing() { + return false; + } + + function init_quick($data) { + $this->type = $data->pagetype; + $this->id = $data->pageid; + } + + function create_object($type, $id = NULL) { + + $data = new stdClass; + $data->pagetype = $type; + $data->pageid = $id; + + // This might be moved somewhere more easily accessible from the outside, + // as anyone that implements a new Page class will need to add a line here. + $typeids = array( + MOODLE_PAGE_COURSE => 'MoodlePage_Course' + ); + + if(!isset($typeids[$type])) { + error('Unrecognized type passed to MoodlePage::create_object: '.$type); + } + + $object = &new $typeids[$type]; + + if($object->get_type() !== $type) { + // Somehow somewhere someone made a mistake + error("Page object's type (".$object->get_type().") does not match requested type ($type)"); + } + + $object->init_quick($data); + return $object; + } +} + + +/** + * Class that models the behavior of a moodle course + * + * @version $Id$ + * @license http://www.gnu.org/copyleft/gpl.html GNU Public License + * @author Jon Papaioannou + * @package pages + */ + +class MoodlePage_Course extends MoodlePage { + + // Any data we might need to store specifically about ourself should be declared here. + // After init_full() is called for the first time, ALL of these variables should be + // initialized correctly and ready for use. + var $courserecord = NULL; + + // Do any validation of the officially recognized bits of the data and forward to parent. + // Do NOT load up "expensive" resouces (e.g. SQL data) here! + function init_quick($data) { + if(empty($data->pageid)) { + error('Cannot quickly initialize page: empty course id '); + } + parent::init_quick($data); + } + + // Here you should load up all heavy-duty data for your course. Basically everything that + // does not NEED to be loaded for the class to make basic decisions should NOT be loaded + // in init_quick() and instead deferred here. Of course this function had better recognize + // $this->full_init_done to prevent wasteful multiple-time data retrieval. + function init_full() { + if($this->full_init_done) { + return; + } + $this->courserecord = get_record('course', 'id', $this->id); + if(empty($this->courserecord)) { + error('Cannot fully initialize page: invalid course id '.$this->id); + } + $this->full_init_done = true; + } + + // When is a user said to have "editing rights" in this page? This would have something + // to do with roles, in the future. + function user_allowed_editing() { + return isteacheredit($this->id); + } + + // Is the user actually editing this page right now? This would have something + // to do with roles, in the future. + function user_is_editing() { + return isediting($this->id); + } + + // HTML output section. This function prints out the common part of the page's header. + // You should NEVER print the header "by hand" in other code. + function print_header($title) { + global $USER; + $this->init_full(); + $replacements = array( + '%fullname%' => $this->courserecord->fullname + ); + foreach($replacements as $search => $replace) { + $title = str_replace($search, $replace, $title); + } + + $loggedinas = '

'. user_login_string($this->courserecord, $USER) .'

'; + print_header($title, $this->courserecord->fullname, $this->courserecord->shortname, + '', '', true, update_course_icon($this->courserecord->id), $loggedinas); + } + + // This is hardwired here so the factory method create_object() can be sure there was no mistake. + // Also, it doubles as a way to let others inquire about our type. + function get_type() { + return MOODLE_PAGE_COURSE; + } + + // This is like the "category" of a page of this "type". For example, if the type is MOODLE_PAGE_COURSE + // the format_name is the actual name of the course format. If the type were MOODLE_PAGE_ACTIVITY, then + // the format_name might be that activity's name etc. + function get_format_name() { + $this->init_full(); + return $this->courserecord->format; + } + + // This should return a fully qualified path to the URL which is responsible for displaying us. + function url_get_path() { + global $CFG; + if($this->id == SITEID) { + return $CFG->wwwroot.'/index.php'; + } + else { + return $CFG->wwwroot.'/course/view.php'; + } + } + + // This should return an associative array of any GET/POST parameters that are needed by the URL + // which displays us to make it work. If none are needed, return an empty array. + function url_get_parameters() { + if($this->id == SITEID) { + return array(); + } + else { + return array('id' => $this->id); + } + } + + // Blocks-related section + + // Which are the positions in this page which support blocks? Return an array containing their identifiers. + // BE CAREFUL, ORDER DOES MATTER! In textual representations, lists of blocks in a page use the ':' character + // to delimit different positions in the page. The part before the first ':' in such a representation will map + // directly to the first item of the array you return here, the second to the next one and so on. This way, + // you can add more positions in the future without interfering with legacy textual representations. + function blocks_get_positions() { + return array(BLOCK_POS_LEFT, BLOCK_POS_RIGHT); + } + + // When a new block is created in this page, which position should it go to? + function blocks_default_position() { + return BLOCK_POS_RIGHT; + } + + // When we are creating a new page, use the data at your disposal to provide a textual representation of the + // blocks that are going to get added to this new page. Delimit block names with commas (,) and use double + // colons (:) to delimit between block positions in the page. See blocks_get_positions() for additional info. + function blocks_get_default() { + global $CFG; + + $this->init_full(); + + if($this->id == SITEID) { + // Is it the site? + if (!empty($CFG->defaultblocks_site)) { + $blocknames = $CFG->defaultblocks_site; + } + /// Failsafe - in case nothing was defined. + else { + $blocknames = 'site_main_menu,admin,course_list:course_summary,calendar_month'; + } + } + // It's a normal course, so do it according to the course format + else { + $pageformat = $this->courserecord->format; + if (!empty($CFG->{'defaultblocks_'. $pageformat})) { + $blocknames = $CFG->{'defaultblocks_'. $pageformat}; + } + else { + $format_config = $CFG->dirroot.'/course/format/'.$pageformat.'/config.php'; + if (@is_file($format_config) && is_readable($format_config)) { + require($format_config); + } + if (!empty($format['defaultblocks'])) { + $blocknames = $format['defaultblocks']; + } + else if (!empty($CFG->defaultblocks)){ + $blocknames = $CFG->defaultblocks; + } + /// Failsafe - in case nothing was defined. + else { + $blocknames = 'participants,activity_modules,search_forums,admin,course_list:news_items,calendar_upcoming,recent_activity'; + } + } + } + + return $blocknames; + } + + // Given an instance of a block in this page and the direction in which we want to move it, where is + // it going to go? Return the identifier of the instance's new position. This allows us to tell blocklib + // how we want the blocks to move around in this page in an arbitrarily complex way. If the move as given + // does not make sense, make sure to return the instance's original position. + // + // Since this is going to get called a LOT, pass the instance by reference purely for speed. Do **NOT** + // modify its data in any way, this will actually confuse blocklib!!! + function blocks_move_position(&$instance, $move) { + if($instance->position == BLOCK_POS_LEFT && $move == BLOCK_MOVE_RIGHT) { + return BLOCK_POS_RIGHT; + } + else if ($instance->position == BLOCK_POS_RIGHT && $move == BLOCK_MOVE_LEFT) { + return BLOCK_POS_LEFT; + } + return $instance->position; + } +} + +?> diff --git a/lib/weblib.php b/lib/weblib.php index 6e57b4ba8f..59cd3c9208 100644 --- a/lib/weblib.php +++ b/lib/weblib.php @@ -3476,15 +3476,5 @@ function print_speller_button () { echo ''."\n"; } -function page_source_script($page) { - global $CFG; - - switch($page->type) { - case MOODLE_PAGE_COURSE: - return $CFG->wwwroot.'/course/view.php?id='.$page->id; - break; - } -} - // vim:autoindent:expandtab:shiftwidth=4:tabstop=4:tw=140: ?>