From: tjhunt Date: Thu, 16 Jul 2009 10:50:19 +0000 (+0000) Subject: blocks editing ui: MDL-19398 Can how precisely control block positioning using the... X-Git-Url: http://git.mjollnir.org/gw?a=commitdiff_plain;h=1d13c75c1582beb0fd920bb5fafdd434a4a12de7;p=moodle.git blocks editing ui: MDL-19398 Can how precisely control block positioning using the edit icon This mostly works now, but ... * The UI needs further work. In particular we need a non-advanced mode. * This only works for blocks that use a new edit_form.php to replace config_instance.html. * .. and so far I have only implemented edit_form.php for the HTML block so far. * Needs to be enabled (with no block-specific config) for blocks without instance config, so you can control their positioning. --- diff --git a/blocks/edit.php b/blocks/edit.php new file mode 100644 index 0000000000..369f606a33 --- /dev/null +++ b/blocks/edit.php @@ -0,0 +1,148 @@ +. + +/** + * This script is used to edit the settings for a block instance. + * + * It works with the {@link block_edit_form} class, or rather the particular + * subclass defined by this block, to do the editing. + * + * @package moodlecore + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once(dirname(__FILE__) . '/../config.php'); +require_once($CFG->dirroot . '/blocks/edit_form.php'); + +$blockid = required_param('id', PARAM_INTEGER); +$pagecontextid = required_param('pagecontextid', PARAM_INTEGER); +$pagetype = required_param('pagetype', PARAM_ALPHANUMEXT); +$subpage = optional_param('subpage', null, PARAM_ALPHANUMEXT); +$returnurl = required_param('returnurl', PARAM_LOCALURL); + +$urlparams = array( + 'id' => $blockid, + 'pagecontextid' => $pagecontextid, + 'pagetype' => $pagetype, + 'returnurl' => $returnurl, +); +if ($subpage) { + $urlparams['subpage'] = $subpage; +} +$PAGE->set_url('blocks/edit.php', $urlparams); + +require_login(); + +$blockpage = new moodle_page(); +$blockpage->set_context(get_context_instance_by_id($pagecontextid)); +$blockpage->set_pagetype($pagetype); +$blockpage->set_subpage($subpage); +$url = new moodle_url($returnurl); +$blockpage->set_url($url->out(true), $url->params()); + +$block = block_load_for_page($blockid, $blockpage); + +if (!$block->user_can_edit() || (!$block->instance_allow_multiple() && !$block->instance_allow_config())) { + throw new moodle_exception('nopermissions', '', $page->url->out(), get_string('editblock')); +} + +$PAGE->set_context($block->context); +$PAGE->set_generaltype('form'); + +$formfile = $CFG->dirroot . '/blocks/' . $block->name() . '/edit_form.php'; +if (is_readable($formfile)) { + require_once($formfile); + $classname = 'block_' . $block->name() . '_edit_form'; +} else { + $classname = 'block_edit_form'; +} +$mform = new $classname($block, $blockpage); + +$mform->set_data($block->instance); + +if ($mform->is_cancelled()) { + redirect($CFG->wwwroot . '/' . $returnurl); + +} else if ($data = $mform->get_data()) { + $bi = new stdClass; + $bi->id = $block->instance->id; + $bi->showinsubcontexts = $data->showinsubcontexts; + $bi->pagetypepattern = $data->pagetypepattern; + if (empty($data->subpagepattern) || $data->subpagepattern == '%@NULL@%') { + $bi->subpagepattern = null; + } else { + $bi->subpagepattern = $data->subpagepattern; + } + $bi->defaultregion = $data->defaultregion; + $bi->defaultweight = $data->defaultweight; + $DB->update_record('block_instances', $bi); + + $config = new stdClass; + foreach ($data as $configfield => $value) { + if (strpos($configfield, 'config_') !== 0) { + continue; + } + $field = substr($configfield, 7); + $config->$field = $value; + } + $block->instance_config_save($config); + + $bp = new stdClass; + $bp->visible = $data->visible; + $bp->region = $data->region; + $bp->weight = $data->weight; + $needbprecord = !$data->visible || $data->region != $data->defaultregion || + $data->weight != $data->defaultweight; + + if ($block->instance->blockpositionid && !$needbprecord) { + $DB->delete_records('block_positions', array('id' => $block->instance->blockpositionid)); + + } else if ($block->instance->blockpositionid && $needbprecord) { + $bp->id = $block->instance->blockpositionid; + $DB->update_record('block_positions', $bp); + + } else if ($needbprecord) { + $bp->blockinstanceid = $block->instance->id; + $bp->contextid = $blockpage->contextid; + $bp->pagetype = $blockpage->pagetype; + if ($blockpage->subpage) { + $bp->subpage = $blockpage->subpage; + } else { + $bp->subpage = null; + } + $DB->insert_record('block_positions', $bp); + } + + redirect($CFG->wwwroot . '/' . $returnurl); + +} else { + $strheading = get_string('editinga', $block->name()); + if (strpos($strheading, '[[') === 0) { + $strheading = get_string('blockconfiga', 'moodle', $block->get_title()); + } + +$PAGE->set_title($strheading); + $PAGE->set_heading($strheading); + + echo $OUTPUT->header(); + echo $OUTPUT->heading($strheading, 2); + + $mform->display(); + + echo $OUTPUT->footer(); +} \ No newline at end of file diff --git a/blocks/edit_form.php b/blocks/edit_form.php new file mode 100644 index 0000000000..1de15d03d0 --- /dev/null +++ b/blocks/edit_form.php @@ -0,0 +1,151 @@ +. + +/** + * Defines the base class form used by blocks/edit.php to edit block instance configuration. + * + * It works with the {@link block_edit_form} class, or rather the particular + * subclass defined by this block, to do the editing. + * + * @package moodlecore + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once($CFG->libdir . '/formslib.php'); + +/** + * The base class form used by blocks/edit.php to edit block instance configuration. + * + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class block_edit_form extends moodleform { + const MAX_WEIGHT = 10; + /** + * The block instance we are editing. + * @var block_base + */ + public $block; + /** + * The page we are editing this block in association with. + * @var moodle_page + */ + public $page; + + function __construct($block, $page) { + global $CFG; + $this->block = $block; + $this->page = $page; + parent::moodleform(block_edit_url($block, $page)); + } + + function definition() { + $mform =& $this->_form; + + // First show fields specific to this type of block. + $this->specific_definition($mform); + + // Then show the fields about where this block appears. + $mform->addElement('header', 'whereheader', get_string('wherethisblockappears', 'block')); + + // If the current weight of the block is out-of-range, add that option in. + $blockweight = $this->block->instance->weight; + $weightoptions = array(); + if ($blockweight < -self::MAX_WEIGHT) { + $weightoptions[$blockweight] = $blockweight; + } + for ($i = -self::MAX_WEIGHT; $i <= self::MAX_WEIGHT; $i++) { + $weightoptions[$i] = $i; + } + if ($blockweight > self::MAX_WEIGHT) { + $weightoptions[$blockweight] = $blockweight; + } + $first = reset($weightoptions); + $weightoptions[$first] = get_string('bracketfirst', 'block', $first); + $last = end($weightoptions); + $weightoptions[$last] = get_string('bracketlast', 'block', $last); + + $regionoptions = $this->page->theme->get_all_block_regions(); + + $parentcontext = get_context_instance_by_id($this->block->instance->parentcontextid); + $mform->addElement('static', 'contextname', get_string('thisblockbelongsto', 'block'), print_context_name($parentcontext)); + + $mform->addElement('selectyesno', 'showinsubcontexts', get_string('appearsinsubcontexts', 'block')); + + $pagetypeoptions = matching_page_type_patterns($this->page->pagetype); + $pagetypeoptions = array_combine($pagetypeoptions, $pagetypeoptions); + $mform->addElement('select', 'pagetypepattern', get_string('pagetypes', 'block'), $pagetypeoptions); + + if ($this->page->subpage) { + $subpageoptions = array( + '%@NULL@%' => get_string('anypagematchingtheabove', 'block'), + $this->page->subpage => get_string('thisspecificpage', 'block', $this->page->subpage), + ); + $mform->addElement('select', 'subpagepattern', get_string('subpages', 'block'), $subpageoptions); + } + + $defaultregionoptions = $regionoptions; + $defaultregion = $this->block->instance->defaultregion; + if (!array_key_exists($defaultregion, $defaultregionoptions)) { + $defaultregionoptions[$defaultregion] = $defaultregion; + } + $mform->addElement('select', 'defaultregion', get_string('defaultregion', 'block'), $defaultregionoptions); + + $mform->addElement('select', 'defaultweight', get_string('defaultweight', 'block'), $weightoptions); + + // Where this block is positioned on this page. + $mform->addElement('header', 'whereheader', get_string('onthispage', 'block')); + + $mform->addElement('selectyesno', 'visible', get_string('visible', 'block')); + + $blockregion = $this->block->instance->region; + if (!array_key_exists($blockregion, $regionoptions)) { + $regionoptions[$blockregion] = $blockregion; + } + $mform->addElement('select', 'region', get_string('region', 'block'), $regionoptions); + + $mform->addElement('select', 'weight', get_string('weight', 'block'), $weightoptions); + + $this->add_action_buttons(); + } + + function set_data($defaults) { + // Copy block config into config_ fields. + if (!empty($this->block->config)) { + foreach ($this->block->config as $field => $value) { + $configfield = 'config_' . $field; + $defaults->$configfield = $value; + } + } + + // Munge ->subpagepattern becuase HTML selects don't play nicely with NULLs. + if (empty($defaults->subpagepattern)) { + $defaults->subpagepattern = '%@NULL@%'; + } + + parent::set_data($defaults); + } + + /** + * Override this to create any form fields specific to this type of block. + * @param object $mform the form being built. + */ + protected function specific_definition($mform) { + // By default, do nothing. + } +} \ No newline at end of file diff --git a/blocks/html/edit_form.php b/blocks/html/edit_form.php new file mode 100644 index 0000000000..4cb70e65b4 --- /dev/null +++ b/blocks/html/edit_form.php @@ -0,0 +1,44 @@ +. + +/** + * Form for editing HTML block instances. + * + * @package moodlecore + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +/** + * Form for editing HTML block instances. + * + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class block_html_edit_form extends block_edit_form { + protected function specific_definition($mform) { + // Then show the fields about where this block appears. + $mform->addElement('header', 'configheader', get_string('blocksettings', 'block')); + + $mform->addElement('text', 'config_title', get_string('configtitle', 'block_html')); + $mform->setType('configtitle', PARAM_MULTILANG); + + // TODO MDL-19844 should use the new editor field type. + $mform->addElement('htmleditor', 'config_text', get_string('configcontent', 'block_html')); + $mform->setType('config_text', PARAM_RAW); // no XSS prevention here, users must be trusted + } +} \ No newline at end of file diff --git a/lang/en_utf8/block.php b/lang/en_utf8/block.php new file mode 100644 index 0000000000..ce33f3a619 --- /dev/null +++ b/lang/en_utf8/block.php @@ -0,0 +1,19 @@ +page->user_is_editing(); } @@ -401,7 +402,7 @@ class block_manager { $contexttest = "($contexttest OR (bi.showinsubcontexts = 1 AND bi.parentcontextid $parentcontexttest))"; } - $pagetypepatterns = $this->matching_page_type_patterns($this->page->pagetype); + $pagetypepatterns = matching_page_type_patterns($this->page->pagetype); list($pagetypepatterntest, $pagetypepatternparams) = $DB->get_in_or_equal($pagetypepatterns, SQL_PARAMS_NAMED, 'pagetypepatterntest0000'); @@ -578,30 +579,6 @@ class block_manager { /// Inner workings ============================================================= - /** - * Given a specific page type, return all the page type patterns that might - * match it. - * - * @param string $pagetype for example 'course-view-weeks' or 'mod-quiz-view'. - * @return array an array of all the page type patterns that might match this page type. - */ - protected function matching_page_type_patterns($pagetype) { - $patterns = array($pagetype, '*'); - $bits = explode('-', $pagetype); - if (count($bits) == 3 && $bits[0] == 'mod') { - if ($bits[2] == 'view') { - $patterns[] = 'mod-*-view'; - } else if ($bits[2] == 'index') { - $patterns[] = 'mod-*-index'; - } - } - while (count($bits) > 0) { - $patterns[] = implode('-', $bits) . '-*'; - array_pop($bits); - } - return $patterns; - } - /** * Check whether the page blocks have been loaded yet * @@ -754,6 +731,66 @@ function block_method_result($blockname, $method, $param = NULL) { return call_user_func(array('block_'.$blockname, $method), $param); } +/** + * Load a block instance, with position information about where that block appears + * on a given page. + * + * @param integer$blockid the block_instance.id. + * @param moodle_page $page the page the block is appearing on. + * @return block_base the requested block. + */ +function block_load_for_page($blockid, $page) { + global $DB; + + // The code here needs to be consistent with the code in block_manager::load_blocks. + $params = array( + 'blockinstanceid' => $blockid, + 'subpage' => $page->subpage, + 'contextid' => $page->context->id, + 'pagetype' => $page->pagetype, + 'contextblock' => CONTEXT_BLOCK, + ); + $sql = "SELECT + bi.id, + bp.id AS blockpositionid, + bi.blockname, + bi.parentcontextid, + bi.showinsubcontexts, + bi.pagetypepattern, + bi.subpagepattern, + bi.defaultregion, + bi.defaultweight, + COALESCE(bp.visible, 1) AS visible, + COALESCE(bp.region, bi.defaultregion) AS region, + COALESCE(bp.weight, bi.defaultweight) AS weight, + bi.configdata, + ctx.id AS ctxid, + ctx.path AS ctxpath, + ctx.depth AS ctxdepth, + ctx.contextlevel AS ctxlevel + + FROM {block_instances} bi + JOIN {block} b ON bi.blockname = b.name + LEFT JOIN {block_positions} bp ON bp.blockinstanceid = bi.id + AND bp.contextid = :contextid + AND bp.pagetype = :pagetype + AND bp.subpage = :subpage + JOIN {context} ctx ON ctx.contextlevel = :contextblock + AND ctx.instanceid = bi.id + + WHERE + bi.id = :blockinstanceid + AND b.visible = 1 + + ORDER BY + COALESCE(bp.region, bi.defaultregion), + COALESCE(bp.weight, bi.defaultweight), + bi.id"; + $bi = $DB->get_record_sql($sql, $params, MUST_EXIST); + $bi = make_context_subobj($bi); + return block_instance($bi->blockname, $bi, $page); +} + /** * Creates a new object of the specified block class. * @@ -803,6 +840,31 @@ function block_load_class($blockname) { return class_exists($classname); } +/** + * Given a specific page type, return all the page type patterns that might + * match it. + * + * @param string $pagetype for example 'course-view-weeks' or 'mod-quiz-view'. + * @return array an array of all the page type patterns that might match this page type. + */ +function matching_page_type_patterns($pagetype) { + $patterns = array($pagetype); + $bits = explode('-', $pagetype); + if (count($bits) == 3 && $bits[0] == 'mod') { + if ($bits[2] == 'view') { + $patterns[] = 'mod-*-view'; + } else if ($bits[2] == 'index') { + $patterns[] = 'mod-*-index'; + } + } + while (count($bits) > 0) { + $patterns[] = implode('-', $bits) . '-*'; + array_pop($bits); + } + $patterns[] = '*'; + return $patterns; +} + /// Functions update the blocks if required by the request parameters ========== /** @@ -856,12 +918,11 @@ function block_edit_controls($block, $page) { $controls = array(); $actionurl = $page->url->out_action(); - $returnurlparam = '&returnurl=' . urlencode($page->url->out_returnurl()); // Assign roles icon. if (has_capability('moodle/role:assign', $block->context)) { $controls[] = array('url' => $CFG->wwwroot . '/' . $CFG->admin . - '/roles/assign.php?contextid=' . $block->context->id . $returnurlparam, + '/roles/assign.php?contextid=' . $block->context->id . '&returnurl=' . urlencode($page->url->out_returnurl()), 'icon' => 'i/roles', 'caption' => get_string('assignroles', 'role')); } @@ -877,11 +938,7 @@ function block_edit_controls($block, $page) { // Edit config icon. if ($block->instance_allow_multiple() || $block->instance_allow_config()) { - $editurl = $CFG->wwwroot . '/blocks/edit.php?block=' . $block->instance->id; - if (!empty($block->instance->blockpositionid)) { - $editurl .= '&positionid=' . $block->instance->blockpositionid; - } - $controls[] = array('url' => $editurl . $returnurlparam, + $controls[] = array('url' => block_edit_url($block, $page)->out(), 'icon' => 't/edit', 'caption' => get_string('configuration')); } @@ -899,6 +956,25 @@ function block_edit_controls($block, $page) { return $controls; } +/** + * Get the URL for editing a particular block instance. + * @param block_base $block a block object. + * @return moodle_url the url. + */ +function block_edit_url($block, $page) { + global $CFG; + $urlparams = array( + 'id' => $block->instance->id, + 'pagecontextid' => $page->context->id, + 'pagetype' => $page->pagetype, + 'returnurl' => $page->url->out_returnurl(), + ); + if ($page->subpage) { + $urlparams['subpage'] = $page->subpage; + } + return new moodle_url($CFG->wwwroot . '/blocks/edit.php', $urlparams); +} + /** * Process any block actions that were specified in the URL. * diff --git a/lib/outputlib.php b/lib/outputlib.php index 2bc2a81097..d0bf74c254 100644 --- a/lib/outputlib.php +++ b/lib/outputlib.php @@ -672,6 +672,35 @@ class theme_config { } } + /** + * Get the list of all block regions known to this theme in all templates. + * @return array internal region name => human readable name. + */ + public function get_all_block_regions() { + // Legacy fallback. + if (empty($this->layouts)) { + return array( + 'side-pre' => get_string('region-side-pre', 'theme_standard'), + 'side-post' => get_string('region-side-post', 'theme_standard'), + ); + } + + $regions = array(); + foreach ($this->layouts as $layoutinfo) { + $ownertheme = $this->name; + if (strpos($layoutinfo['layout'], 'standard:') === 0) { + $ownertheme = 'standard'; + } else if (strpos($layoutinfo['layout'], 'parent:') === 0) { + $ownertheme = $this->parent; + } + + foreach ($layoutinfo['regions'] as $region) { + $regions[$region] = get_string('region-' . $region, 'theme_' . $ownertheme); + } + } + return $regions; + } + /** * Helper method used by {@link update_legacy_information()}. Update one entry * in the $this->pluginsheets array, based on the legacy $property propery. diff --git a/lib/simpletest/testblocklib_blockmanager.php b/lib/simpletest/testblocklib_blockmanager.php index fd01d341c4..31ebc870ee 100644 --- a/lib/simpletest/testblocklib_blockmanager.php +++ b/lib/simpletest/testblocklib_blockmanager.php @@ -46,9 +46,6 @@ class testable_block_manager extends block_manager { public function get_loaded_blocks() { return $this->birecordsbyregion; } - public function matching_page_type_patterns($pagetype) { - return parent::matching_page_type_patterns($pagetype); - } } class block_ablocktype extends block_base { public function init() { @@ -140,19 +137,19 @@ class moodle_block_manager_test extends UnitTestCase { public function test_matching_page_type_patterns() { $this->assert(new ArraysHaveSameValuesExpectation( array('site-index', 'site-index-*', 'site-*', '*')), - $this->blockmanager->matching_page_type_patterns('site-index')); + matching_page_type_patterns('site-index')); $this->assert(new ArraysHaveSameValuesExpectation( array('mod-quiz-report-overview', 'mod-quiz-report-overview-*', 'mod-quiz-report-*', 'mod-quiz-*', 'mod-*', '*')), - $this->blockmanager->matching_page_type_patterns('mod-quiz-report-overview')); + matching_page_type_patterns('mod-quiz-report-overview')); $this->assert(new ArraysHaveSameValuesExpectation( array('mod-forum-view', 'mod-*-view', 'mod-forum-view-*', 'mod-forum-*', 'mod-*', '*')), - $this->blockmanager->matching_page_type_patterns('mod-forum-view')); + matching_page_type_patterns('mod-forum-view')); $this->assert(new ArraysHaveSameValuesExpectation( array('mod-forum-index', 'mod-*-index', 'mod-forum-index-*', 'mod-forum-*', 'mod-*', '*')), - $this->blockmanager->matching_page_type_patterns('mod-forum-index')); + matching_page_type_patterns('mod-forum-index')); } }