diff --git a/blocks/site_main_menu/block_site_main_menu.php b/blocks/site_main_menu/block_site_main_menu.php index cd26cbc918bb9..866047db83262 100644 --- a/blocks/site_main_menu/block_site_main_menu.php +++ b/blocks/site_main_menu/block_site_main_menu.php @@ -51,6 +51,7 @@ function get_content() { require_once($CFG->dirroot.'/course/lib.php'); $context = context_course::instance($course->id); $isediting = $this->page->user_is_editing() && has_capability('moodle/course:manageactivities', $context); + $courserenderer = $this->page->get_renderer('core', 'course'); /// extra fast view mode if (!$isediting) { @@ -69,32 +70,18 @@ function get_content() { } if (!empty($cm->url)) { - $attrs = array(); - $attrs['title'] = $cm->modfullname; - $attrs['class'] = $cm->extraclasses . ' activity-action'; - if ($cm->onclick) { - // Get on-click attribute value if specified and decode the onclick - it - // has already been encoded for display. - $attrs['onclick'] = htmlspecialchars_decode($cm->onclick); - } - if (!$cm->visible) { - $attrs['class'] .= ' dimmed'; - } - $icon = ''; - $content = html_writer::link($cm->url, $icon . $cm->get_formatted_name(), $attrs); + $content = html_writer::div($courserenderer->course_section_cm_name($cm), 'activity'); } else { $content = $cm->get_formatted_content(array('overflowdiv' => true, 'noclean' => true)); } - $this->content->items[] = $indent.html_writer::div($content, 'main-menu-content'); + $this->content->items[] = $indent . $content; } } return $this->content; } // Slow & hacky editing mode. - /** @var core_course_renderer $courserenderer */ - $courserenderer = $this->page->get_renderer('core', 'course'); $ismoving = ismoving($course->id); course_create_sections_if_missing($course, 0); $modinfo = get_fast_modinfo($course); @@ -104,11 +91,9 @@ function get_content() { $strmovehere = get_string('movehere'); $strmovefull = strip_tags(get_string('movefull', '', "'$USER->activitycopyname'")); $strcancel= get_string('cancel'); - $stractivityclipboard = $USER->activitycopyname; } else { $strmove = get_string('move'); } - $editbuttons = ''; if ($ismoving) { $this->content->icons[] = ''; @@ -116,7 +101,6 @@ function get_content() { } if (!empty($modinfo->sections[0])) { - $options = array('overflowdiv'=>true); foreach ($modinfo->sections[0] as $modnumber) { $mod = $modinfo->cms[$modnumber]; if (!$mod->uservisible) { @@ -153,27 +137,12 @@ function get_content() { } else { $indent = ''; } - $url = $mod->url; - if (!$url) { + if (!$mod->url) { $content = $mod->get_formatted_content(array('overflowdiv' => true, 'noclean' => true)); } else { - //Accessibility: incidental image - should be empty Alt text - $attrs = array(); - $attrs['title'] = $mod->modfullname; - $attrs['class'] = $mod->extraclasses . ' activity-action'; - if ($mod->onclick) { - // Get on-click attribute value if specified and decode the onclick - it - // has already been encoded for display. - $attrs['onclick'] = htmlspecialchars_decode($mod->onclick); - } - if (!$mod->visible) { - $attrs['class'] .= ' dimmed'; - } - - $icon = ''; - $content = html_writer::link($url, $icon . $mod->get_formatted_name(), $attrs); + $content = html_writer::div($courserenderer->course_section_cm_name($mod), ' activity'); } - $this->content->items[] = $indent.html_writer::div($content . $editbuttons, 'main-menu-content'); + $this->content->items[] = $indent. $content . $editbuttons; } } } diff --git a/blocks/site_main_menu/styles.css b/blocks/site_main_menu/styles.css index 0e15ab77f3503..f437bae204bcc 100644 --- a/blocks/site_main_menu/styles.css +++ b/blocks/site_main_menu/styles.css @@ -6,5 +6,4 @@ .block_site_main_menu .footer { margin-top: 1em; } .block_site_main_menu .section_add_menus noscript div { display: inline;} .block_site_main_menu .mod-indent, -.block_site_main_menu .main-menu-content { display: table-cell; } -.block_site_main_menu .main-menu-content > .activity-action { display: block; } +.block_site_main_menu .activity { display: table-cell; } diff --git a/blocks/site_main_menu/tests/behat/edit_activities.feature b/blocks/site_main_menu/tests/behat/edit_activities.feature new file mode 100644 index 0000000000000..b94a0ad51c782 --- /dev/null +++ b/blocks/site_main_menu/tests/behat/edit_activities.feature @@ -0,0 +1,21 @@ +@block @block_main_menu +Feature: Edit activities in main menu block + In order to use main menu block + As an admin + I need to add and edit activities there + + @javascript + Scenario: Edit name of acitivity in-place in site main menu block + Given I log in as "admin" + And I am on site homepage + And I navigate to "Turn editing on" node in "Front page settings" + When I add a "Forum" to section "0" and I fill the form with: + | Forum name | My forum name | + And I click on "Edit title" "link" in the "//.[contains(@class,'block_site_main_menu')]//li[contains(.,'My forum name')]" "xpath_element" + And I set the field "New name for activity My forum name" to "New forum name" + And I press key "13" in the field "New name for activity My forum name" + Then I should not see "My forum name" + And I should see "New forum name" + And I follow "New forum name" + And I should not see "My forum name" + And I should see "New forum name" diff --git a/blocks/social_activities/block_social_activities.php b/blocks/social_activities/block_social_activities.php index 6c6971b013d32..2553413169bbd 100644 --- a/blocks/social_activities/block_social_activities.php +++ b/blocks/social_activities/block_social_activities.php @@ -48,6 +48,7 @@ function get_content() { } $course = $this->page->course; + $courserenderer = $this->page->get_renderer('core', 'course'); require_once($CFG->dirroot.'/course/lib.php'); @@ -58,25 +59,18 @@ function get_content() { /// extra fast view mode if (!$isediting) { if (!empty($modinfo->sections[0])) { - $options = array('overflowdiv'=>true); foreach($modinfo->sections[0] as $cmid) { $cm = $modinfo->cms[$cmid]; if (!$cm->uservisible) { continue; } - $content = $cm->get_formatted_content(array('overflowdiv' => true, 'noclean' => true)); - $instancename = $cm->get_formatted_name(); - - if (!($url = $cm->url)) { + if (!$cm->url) { + $content = $cm->get_formatted_content(array('overflowdiv' => true, 'noclean' => true)); $this->content->items[] = $content; $this->content->icons[] = ''; } else { - $linkcss = $cm->visible ? '' : ' class="dimmed" '; - //Accessibility: incidental image - should be empty Alt text - $icon = ' '; - $this->content->items[] = 'extra. - ' href="' . $url . '">' . $icon . $instancename . ''; + $this->content->items[] = html_writer::div($courserenderer->course_section_cm_name($cm), 'activity'); } } } @@ -85,21 +79,16 @@ function get_content() { // Slow & hacky editing mode. - /** @var core_course_renderer $courserenderer */ - $courserenderer = $this->page->get_renderer('core', 'course'); $ismoving = ismoving($course->id); - $modinfo = get_fast_modinfo($course); $section = $modinfo->get_section_info(0); if ($ismoving) { $strmovehere = get_string('movehere'); $strmovefull = strip_tags(get_string('movefull', '', "'$USER->activitycopyname'")); $strcancel= get_string('cancel'); - $stractivityclipboard = $USER->activitycopyname; } else { $strmove = get_string('move'); } - $editbuttons = ''; if ($ismoving) { $this->content->icons[] = ' '; @@ -107,7 +96,6 @@ function get_content() { } if (!empty($modinfo->sections[0])) { - $options = array('overflowdiv'=>true); foreach ($modinfo->sections[0] as $modnumber) { $mod = $modinfo->cms[$modnumber]; if (!$mod->uservisible) { @@ -139,19 +127,13 @@ function get_content() { ''.$strmovehere.''; $this->content->icons[] = ''; } - $content = $mod->get_formatted_content(array('overflowdiv' => true, 'noclean' => true)); - $instancename = $mod->get_formatted_name(); - - $linkcss = $mod->visible ? '' : ' class="dimmed" '; - - if (!($url = $mod->url)) { + if (!$mod->url) { + $content = $mod->get_formatted_content(array('overflowdiv' => true, 'noclean' => true)); $this->content->items[] = $content . $editbuttons; $this->content->icons[] = ''; } else { - //Accessibility: incidental image - should be empty Alt text - $icon = ' '; - $this->content->items[] = 'extra . - ' href="' . $url . '">' . $icon . $instancename . '' . $editbuttons; + $this->content->items[] = html_writer::div($courserenderer->course_section_cm_name($mod), 'activity') . + $editbuttons; } } } diff --git a/blocks/social_activities/tests/behat/edit_activities.feature b/blocks/social_activities/tests/behat/edit_activities.feature new file mode 100644 index 0000000000000..1db7a7ff9897c --- /dev/null +++ b/blocks/social_activities/tests/behat/edit_activities.feature @@ -0,0 +1,31 @@ +@block @block_social_activities +Feature: Edit activities in social activities block + In order to use social activities block + As a teacher + I need to add and edit activities there + + @javascript + Scenario: Edit name of acitivity in-place in social activities block + Given the following "courses" exist: + | fullname | shortname | format | + | Course 1 | C1 | social | + And the following "users" exist: + | username | firstname | lastname | + | user1 | User | One | + And the following "course enrolments" exist: + | user | course | role | + | user1 | C1 | editingteacher | + Given I log in as "user1" + And I follow "Course 1" + And I turn editing mode on + And I set the field "Add an activity to section 'section 0'" to "Forum" + And I set the field "Forum name" to "My forum name" + And I press "Save and return to course" + And I click on "Edit title" "link" in the "//.[contains(@class,'block_social_activities')]//li[contains(.,'My forum name')]" "xpath_element" + And I set the field "New name for activity My forum name" to "New forum name" + And I press key "13" in the field "New name for activity My forum name" + Then I should not see "My forum name" in the "Social activities" "block" + And I should see "New forum name" + And I follow "New forum name" + And I should not see "My forum name" + And I should see "New forum name" diff --git a/course/classes/output/course_module_name.php b/course/classes/output/course_module_name.php new file mode 100644 index 0000000000000..e286e555b2d7e --- /dev/null +++ b/course/classes/output/course_module_name.php @@ -0,0 +1,100 @@ +. + +/** + * Contains class core_tag\output\course_module_name + * + * @package core_course + * @copyright 2016 Marina Glancy + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core_course\output; + +use context_module; +use lang_string; +use cm_info; + +/** + * Class to prepare a course module name for display and in-place editing + * + * @package core_course + * @copyright 2016 Marina Glancy + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class course_module_name extends \core\output\inplace_editable { + + /** @var cm_info */ + protected $cm; + + /** @var array */ + protected $displayoptions; + + /** + * Constructor. + * + * @param cm_info $cm + * @param bool $editable + * @param array $displayoptions + */ + public function __construct(cm_info $cm, $editable, $displayoptions = array()) { + $this->cm = $cm; + $this->displayoptions = $displayoptions; + $value = $cm->name; + $edithint = new lang_string('edittitle'); + $editlabel = new lang_string('newactivityname', '', $cm->get_formatted_name()); + $editable = $editable && has_capability('moodle/course:manageactivities', + context_module::instance($cm->id)); + parent::__construct( + 'core_course', 'activityname', $cm->id, $editable, $value, $value, $edithint, $editlabel); + } + + /** + * Export this data so it can be used as the context for a mustache template (core/inplace_editable). + * + * @param renderer_base $output typically, the renderer that's calling this function + * @return array data context for a mustache template + */ + public function export_for_template(\renderer_base $output) { + global $PAGE; + $courserenderer = $PAGE->get_renderer('core', 'course'); + $this->displayvalue = $courserenderer->course_section_cm_name_title($this->cm, $this->displayoptions); + if (strval($this->displayvalue) === '') { + $this->editable = false; + } + return parent::export_for_template($output); + } + + /** + * Updates course module name + * + * @param int $itemid course module id + * @param string $newvalue new name + * @return static + */ + public static function update($itemid, $newvalue) { + list($course, $cm) = get_course_and_cm_from_cmid($itemid); + $context = context_module::instance($cm->id); + // Check access. + require_login($course, false, $cm, true, true); + require_capability('moodle/course:manageactivities', $context); + // Update value. + set_coursemodule_name($cm->id, $newvalue); + // Return instance. + $cm = get_fast_modinfo($course)->get_cm($cm->id); + return new static($cm, true); + } +} diff --git a/course/lib.php b/course/lib.php index 6afaae0a7e874..59b69e7d84563 100644 --- a/course/lib.php +++ b/course/lib.php @@ -1605,6 +1605,50 @@ function set_coursemodule_visible($id, $visible) { return true; } +/** + * Changes the course module name + * + * @param int $id course module id + * @param string $name new value for a name + * @return bool whether a change was made + */ +function set_coursemodule_name($id, $name) { + global $CFG, $DB; + require_once($CFG->libdir . '/gradelib.php'); + + $cm = get_coursemodule_from_id('', $id, 0, false, MUST_EXIST); + + $module = new \stdClass(); + $module->id = $cm->instance; + + // Escape strings as they would be by mform. + if (!empty($CFG->formatstringstriptags)) { + $module->name = clean_param($name, PARAM_TEXT); + } else { + $module->name = clean_param($name, PARAM_CLEANHTML); + } + if ($module->name === $cm->name || strval($module->name) === '') { + return false; + } + if (\core_text::strlen($module->name) > 255) { + throw new \moodle_exception('maximumchars', 'moodle', '', 255); + } + + $module->timemodified = time(); + $DB->update_record($cm->modname, $module); + $cm->name = $module->name; + \core\event\course_module_updated::create_from_cm($cm)->trigger(); + rebuild_course_cache($cm->course, true); + + // Attempt to update the grade item if relevant. + $grademodule = $DB->get_record($cm->modname, array('id' => $cm->instance)); + $grademodule->cmidnumber = $cm->idnumber; + $grademodule->modname = $cm->modname; + grade_update_mod_grades($grademodule); + + return true; +} + /** * This function will handle the whole deletion process of a module. This includes calling * the modules delete_instance function, deleting files, events, grades, conditional data, @@ -2241,53 +2285,6 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) { return $actions; } -/** - * Returns the rename action. - * - * @param cm_info $mod The module to produce editing buttons for - * @param int $sr The section to link back to (used for creating the links) - * @return The markup for the rename action, or an empty string if not available. - */ -function course_get_cm_rename_action(cm_info $mod, $sr = null) { - global $COURSE, $OUTPUT; - - static $str; - static $baseurl; - - $modcontext = context_module::instance($mod->id); - $hasmanageactivities = has_capability('moodle/course:manageactivities', $modcontext); - - if (!isset($str)) { - $str = get_strings(array('edittitle')); - } - - if (!isset($baseurl)) { - $baseurl = new moodle_url('/course/mod.php', array('sesskey' => sesskey())); - } - - if ($sr !== null) { - $baseurl->param('sr', $sr); - } - - // AJAX edit title. - if ($mod->has_view() && $hasmanageactivities && course_ajax_enabled($COURSE) && - (($mod->course == $COURSE->id) || ($mod->course == SITEID))) { - // we will not display link if we are on some other-course page (where we should not see this module anyway) - return html_writer::span( - html_writer::link( - new moodle_url($baseurl, array('update' => $mod->id)), - $OUTPUT->pix_icon('t/editstring', '', 'moodle', array('class' => 'iconsmall visibleifjs', 'title' => '')), - array( - 'class' => 'editing_title', - 'data-action' => 'edittitle', - 'title' => $str->edittitle, - ) - ) - ); - } - return ''; -} - /** * Returns the move action. * @@ -3897,3 +3894,17 @@ function course_get_tagged_courses($tag, $exclusivemode = false, $fromctx = 0, $ return new core_tag\output\tagindex($tag, 'core', 'course', $content, $exclusivemode, $fromctx, $ctx, $rec, $page, $totalpages); } + +/** + * Implements callback inplace_editable() allowing to edit values in-place + * + * @param string $itemtype + * @param int $itemid + * @param mixed $newvalue + * @return \core\output\inplace_editable + */ +function core_course_inplace_editable($itemtype, $itemid, $newvalue) { + if ($itemtype === 'activityname') { + return \core_course\output\course_module_name::update($itemid, $newvalue); + } +} diff --git a/course/renderer.php b/course/renderer.php index b71d4882d410e..617f4e81315ce 100644 --- a/course/renderer.php +++ b/course/renderer.php @@ -733,10 +733,34 @@ protected function is_cm_conditionally_hidden(cm_info $mod) { * @return string */ public function course_section_cm_name(cm_info $mod, $displayoptions = array()) { - global $CFG; + if ((!$mod->uservisible && empty($mod->availableinfo)) || !$mod->url) { + // Nothing to be displayed to the user. + return ''; + } + + // Render element that allows to edit activity name inline. It calls {@link course_section_cm_name_title()} + // to get the display title of the activity. + $tmpl = new \core_course\output\course_module_name($mod, $this->page->user_is_editing(), $displayoptions); + return $this->output->render_from_template('core/inplace_editable', $tmpl->export_for_template($this->output)); + } + + /** + * Renders html to display a name with the link to the course module on a course page + * + * If module is unavailable for user but still needs to be displayed + * in the list, just the name is returned without a link + * + * Note, that for course modules that never have separate pages (i.e. labels) + * this function return an empty string + * + * @param cm_info $mod + * @param array $displayoptions + * @return string + */ + public function course_section_cm_name_title(cm_info $mod, $displayoptions = array()) { $output = ''; if (!$mod->uservisible && empty($mod->availableinfo)) { - // nothing to be displayed to the user + // Nothing to be displayed to the user. return $output; } $url = $mod->url; @@ -985,10 +1009,6 @@ public function course_section_cm($course, &$completioninfo, cm_info $mod, $sect $output .= $cmname; - if ($this->page->user_is_editing()) { - $output .= ' ' . course_get_cm_rename_action($mod, $sectionreturn); - } - // Module can put text after the link (e.g. forum unread) $output .= $mod->afterlink; diff --git a/course/rest.php b/course/rest.php index 675f3671e961a..42b45ce1303d4 100644 --- a/course/rest.php +++ b/course/rest.php @@ -150,49 +150,6 @@ $isvisible = moveto_module($cm, $section, $beforemod); echo json_encode(array('visible' => (bool) $isvisible)); break; - case 'gettitle': - require_capability('moodle/course:manageactivities', $modcontext); - $cm = get_coursemodule_from_id('', $id, 0, false, MUST_EXIST); - $module = new stdClass(); - $module->id = $cm->instance; - - // Don't pass edit strings through multilang filters - we need the entire string - echo json_encode(array('instancename' => $cm->name)); - break; - case 'updatetitle': - require_capability('moodle/course:manageactivities', $modcontext); - require_once($CFG->libdir . '/gradelib.php'); - $cm = get_coursemodule_from_id('', $id, 0, false, MUST_EXIST); - $module = new stdClass(); - $module->id = $cm->instance; - - // Escape strings as they would be by mform - if (!empty($CFG->formatstringstriptags)) { - $module->name = clean_param($title, PARAM_TEXT); - } else { - $module->name = clean_param($title, PARAM_CLEANHTML); - } - - if (strval($module->name) !== '') { - $DB->update_record($cm->modname, $module); - $cm->name = $module->name; - \core\event\course_module_updated::create_from_cm($cm, $modcontext)->trigger(); - rebuild_course_cache($cm->course); - } else { - $module->name = $cm->name; - } - - // Attempt to update the grade item if relevant - $grademodule = $DB->get_record($cm->modname, array('id' => $cm->instance)); - $grademodule->cmidnumber = $cm->idnumber; - $grademodule->modname = $cm->modname; - grade_update_mod_grades($grademodule); - - // We need to return strings after they've been through filters for multilang - $stringoptions = new stdClass; - $stringoptions->context = $coursecontext; - echo json_encode(array('instancename' => html_entity_decode(format_string($module->name, true, $stringoptions)))); - break; } break; diff --git a/course/tests/behat/activities_edit_name.feature b/course/tests/behat/activities_edit_name.feature new file mode 100644 index 0000000000000..2d53b7cc8fc3b --- /dev/null +++ b/course/tests/behat/activities_edit_name.feature @@ -0,0 +1,44 @@ +@core @core_course +Feature: Edit activity name in-place + In order to quickly edit activity name + As a teacher + I need to use inplace editing + + @javascript + Scenario: Edit activity name in-place + Given the following "users" exist: + | username | firstname | lastname | email | + | teacher1 | Teacher | 1 | teacher1@example.com | + And the following "courses" exist: + | fullname | shortname | format | + | Course 1 | C1 | topics | + And the following "course enrolments" exist: + | user | course | role | + | teacher1 | C1 | editingteacher | + When I log in as "teacher1" + And I follow "Course 1" + And I turn editing mode on + And I add a "Forum" to section "1" and I fill the form with: + | Forum name | Test forum name | + | Description | Test forum description | + # Rename activity + And I click on "Edit title" "link" in the "//div[contains(@class,'activityinstance') and contains(.,'Test forum name')]" "xpath_element" + And I set the field "New name for activity Test forum name" to "Good news" + And I press key "13" in the field "New name for activity Test forum name" + Then I should not see "Test forum name" in the ".course-content" "css_element" + And "New name for activity Test forum name" "field" should not exist + And I should see "Good news" + And I follow "Course 1" + And I should see "Good news" + And I should not see "Test forum name" + # Cancel renaming + And I click on "Edit title" "link" in the "//div[contains(@class,'activityinstance') and contains(.,'Good news')]" "xpath_element" + And I set the field "New name for activity Good news" to "Terrible news" + And I press key "27" in the field "New name for activity Good news" + And "New name for activity Good news" "field" should not exist + And I should see "Good news" + And I should not see "Terrible news" + And I follow "Course 1" + And I should see "Good news" + And I should not see "Terrible news" + And I log out diff --git a/course/tests/behat/behat_course.php b/course/tests/behat/behat_course.php index 344a208c3dfeb..05a096f6ff374 100644 --- a/course/tests/behat/behat_course.php +++ b/course/tests/behat/behat_course.php @@ -467,7 +467,7 @@ public function section_activities_should_be_hidden($sectionnumber) { foreach ($activities as $activity) { // Dimmed. $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), ' activityinstance ')]" . - "/a[contains(concat(' ', normalize-space(@class), ' '), ' dimmed ')]", $dimmedexception, $activity); + "//a[contains(concat(' ', normalize-space(@class), ' '), ' dimmed ')]", $dimmedexception, $activity); } } } else { diff --git a/course/tests/courselib_test.php b/course/tests/courselib_test.php index 0e4e9dea6102f..aa50329657f34 100644 --- a/course/tests/courselib_test.php +++ b/course/tests/courselib_test.php @@ -2779,4 +2779,34 @@ public function test_empty_availability_settings() { $this->assertNull($DB->get_field('course_modules', 'availability', array('id' => $label->cmid))); } + + /** + * Test update_inplace_editable() + */ + public function test_update_module_name_inplace() { + global $CFG, $DB, $PAGE; + require_once($CFG->dirroot . '/lib/external/externallib.php'); + + $this->setUser($this->getDataGenerator()->create_user()); + + $this->resetAfterTest(true); + $course = $this->getDataGenerator()->create_course(); + $forum = self::getDataGenerator()->create_module('forum', array('course' => $course->id, 'name' => 'forum name')); + + // Call service for core_course component without necessary permissions. + try { + core_external::update_inplace_editable('core_course', 'activityname', $forum->cmid, 'New forum name'); + $this->fail('Exception expected'); + } catch (moodle_exception $e) { + $this->assertEquals('Course or activity not accessible. (Not enrolled)', + $e->getMessage()); + } + + // Change to admin user and make sure that cm name can be updated using web service update_inplace_editable(). + $this->setAdminUser(); + $res = core_external::update_inplace_editable('core_course', 'activityname', $forum->cmid, 'New forum name'); + $res = external_api::clean_returnvalue(core_external::update_inplace_editable_returns(), $res); + $this->assertEquals('New forum name', $res['value']); + $this->assertEquals('New forum name', $DB->get_field('forum', 'name', array('id' => $forum->id))); + } } diff --git a/course/yui/build/moodle-course-toolboxes/moodle-course-toolboxes-debug.js b/course/yui/build/moodle-course-toolboxes/moodle-course-toolboxes-debug.js index 03d72a6199038..75ef3b8772fc4 100644 --- a/course/yui/build/moodle-course-toolboxes/moodle-course-toolboxes-debug.js +++ b/course/yui/build/moodle-course-toolboxes/moodle-course-toolboxes-debug.js @@ -19,31 +19,28 @@ var CSS = { DIMCLASS : 'dimmed', DIMMEDTEXT : 'dimmed_text', EDITINSTRUCTIONS : 'editinstructions', - EDITINGTITLE: 'editor_displayed', HIDE : 'hide', MODINDENTCOUNT : 'mod-indent-', MODINDENTHUGE : 'mod-indent-huge', MODULEIDPREFIX : 'module-', SECTIONHIDDENCLASS : 'hidden', SECTIONIDPREFIX : 'section-', - SHOW : 'editing_show', - TITLEEDITOR : 'titleeditor' + SHOW : 'editing_show' }, // The CSS selectors we use. SELECTOR = { ACTIONAREA: '.actions', ACTIONLINKTEXT : '.actionlinktext', - ACTIVITYACTION : 'a.cm-edit-action[data-action], a.editing_title', - ACTIVITYFORM : '.' + CSS.ACTIVITYINSTANCE + ' form', + ACTIVITYACTION : 'a.cm-edit-action[data-action]', ACTIVITYICON : 'img.activityicon', ACTIVITYINSTANCE : '.' + CSS.ACTIVITYINSTANCE, - ACTIVITYLINK: '.' + CSS.ACTIVITYINSTANCE + ' > a', + ACTIVITYLINK: '.' + CSS.ACTIVITYINSTANCE + ' > a, .'+ CSS.ACTIVITYINSTANCE + + ' > span[data-inplaceeditable] > a:not([data-inplaceeditablelink])', ACTIVITYLI : 'li.activity', - ACTIVITYTITLE : 'input[name=title]', COMMANDSPAN : '.commands', CONTENTAFTERLINK : 'div.contentafterlink', CONTENTWITHOUTLINK : 'div.contentwithoutlink', - EDITTITLE: 'a.editing_title', + GROUPINGLABEL: '.' + CSS.ACTIVITYINSTANCE + ' .groupinglabel', HIDE : 'a.editing_hide', HIGHLIGHT : 'a.editing_highlight', INSTANCENAME : 'span.instancename', @@ -256,17 +253,6 @@ Y.extend(RESOURCETOOLBOX, TOOLBOX, { */ GROUPS_VISIBLE: 2, - /** - * An Array of events added when editing a title. - * These should all be detached when editing is complete. - * - * @property edittitleevents - * @protected - * @type Array - * @protected - */ - edittitleevents: [], - /** * Initialize the resource toolbox * @@ -317,10 +303,6 @@ Y.extend(RESOURCETOOLBOX, TOOLBOX, { // Switch based upon the action and do the desired thing. switch (action) { - case 'edittitle': - // The user wishes to edit the title of the event. - this.edit_title(ev, node, activity, action); - break; case 'moveleft': case 'moveright': // The user changing the indent of the activity. @@ -633,11 +615,13 @@ Y.extend(RESOURCETOOLBOX, TOOLBOX, { dimarea.addClass(toggleclass); // We need to toggle dimming on the description too. activity.all(SELECTOR.CONTENTAFTERLINK).addClass(CSS.DIMMEDTEXT); + activity.all(SELECTOR.GROUPINGLABEL).addClass(CSS.DIMMEDTEXT); } else { // Change the UI. dimarea.removeClass(toggleclass); // We need to toggle dimming on the description too. activity.all(SELECTOR.CONTENTAFTERLINK).removeClass(CSS.DIMMEDTEXT); + activity.all(SELECTOR.GROUPINGLABEL).removeClass(CSS.DIMMEDTEXT); } } // Toggle availablity info for conditional activities. @@ -714,172 +698,6 @@ Y.extend(RESOURCETOOLBOX, TOOLBOX, { return this; }, - /** - * Edit the title for the resource - * - * @method edit_title - * @protected - * @param {EventFacade} ev The event that was fired. - * @param {Node} button The button that triggered this action. - * @param {Node} activity The activity node that this action will be performed on. - * @param {String} action The action that has been requested. - * @chainable - */ - edit_title: function(ev, button, activity) { - // Get the element we're working on - var activityid = Y.Moodle.core_course.util.cm.getId(activity), - instancename = activity.one(SELECTOR.INSTANCENAME), - instance = activity.one(SELECTOR.ACTIVITYINSTANCE), - currenttitle = instancename.get('firstChild'), - oldtitle = currenttitle.get('data'), - titletext = oldtitle, - thisevent, - anchor = instancename.ancestor('a'),// Grab the anchor so that we can swap it with the edit form. - data = { - 'class': 'resource', - 'field': 'gettitle', - 'id': activityid - }; - - // Prevent the default actions. - ev.preventDefault(); - - this.send_request(data, null, function(response) { - if (M.core.actionmenu && M.core.actionmenu.instance) { - M.core.actionmenu.instance.hideMenu(ev); - } - - // Try to retrieve the existing string from the server - if (response.instancename) { - titletext = response.instancename; - } - - // Create the editor and submit button - var editform = Y.Node.create('
'); - var editinstructions = Y.Node.create('') - .set('innerHTML', M.util.get_string('edittitleinstructions', 'moodle')); - var editor = Y.Node.create('').setAttrs({ - 'value': titletext, - 'autocomplete': 'off', - 'aria-describedby': 'id_editinstructions', - 'maxLength': '255' - }); - - // Clear the existing content and put the editor in - editform.appendChild(activity.one(SELECTOR.ACTIVITYICON).cloneNode()); - editform.appendChild(editor); - editform.setData('anchor', anchor); - instance.insert(editinstructions, 'before'); - anchor.replace(editform); - - // Force the editing instruction to match the mod-indent position. - var padside = 'left'; - if (window.right_to_left()) { - padside = 'right'; - } - - // We hide various components whilst editing: - activity.addClass(CSS.EDITINGTITLE); - - // Focus and select the editor text - editor.focus().select(); - - // Cancel the edit if we lose focus or the escape key is pressed. - thisevent = editor.on('blur', this.edit_title_cancel, this, activity, false); - this.edittitleevents.push(thisevent); - thisevent = editor.on('key', this.edit_title_cancel, 'esc', this, activity, true); - this.edittitleevents.push(thisevent); - - // Handle form submission. - thisevent = editform.on('submit', this.edit_title_submit, this, activity, oldtitle); - this.edittitleevents.push(thisevent); - }); - return this; - }, - - /** - * Handles the submit event when editing the activity or resources title. - * - * @method edit_title_submit - * @protected - * @param {EventFacade} ev The event that triggered this. - * @param {Node} activity The activity whose title we are altering. - * @param {String} originaltitle The original title the activity or resource had. - */ - edit_title_submit: function(ev, activity, originaltitle) { - // We don't actually want to submit anything - ev.preventDefault(); - - var newtitle = Y.Lang.trim(activity.one(SELECTOR.ACTIVITYFORM + ' ' + SELECTOR.ACTIVITYTITLE).get('value')); - this.edit_title_clear(activity); - var spinner = this.add_spinner(activity); - if (newtitle !== null && newtitle !== "" && newtitle !== originaltitle) { - var data = { - 'class': 'resource', - 'field': 'updatetitle', - 'title': newtitle, - 'id': Y.Moodle.core_course.util.cm.getId(activity) - }; - this.send_request(data, spinner, function(response) { - if (response.instancename) { - activity.one(SELECTOR.INSTANCENAME).setContent(response.instancename); - } - }); - } - }, - - /** - * Handles the cancel event when editing the activity or resources title. - * - * @method edit_title_cancel - * @protected - * @param {EventFacade} ev The event that triggered this. - * @param {Node} activity The activity whose title we are altering. - * @param {Boolean} preventdefault If true we should prevent the default action from occuring. - */ - edit_title_cancel: function(ev, activity, preventdefault) { - if (preventdefault) { - ev.preventDefault(); - } - this.edit_title_clear(activity); - }, - - /** - * Handles clearing the editing UI and returning things to the original state they were in. - * - * @method edit_title_clear - * @protected - * @param {Node} activity The activity whose title we were altering. - */ - edit_title_clear: function(activity) { - // Detach all listen events to prevent duplicate triggers - new Y.EventHandle(this.edittitleevents).detach(); - - var editform = activity.one(SELECTOR.ACTIVITYFORM), - instructions = activity.one('#id_editinstructions'); - if (editform) { - editform.replace(editform.getData('anchor')); - } - if (instructions) { - instructions.remove(); - } - - // Remove the editing class again to revert the display. - activity.removeClass(CSS.EDITINGTITLE); - - // Refocus the link which was clicked originally so the user can continue using keyboard nav. - Y.later(100, this, function() { - activity.one(SELECTOR.EDITTITLE).focus(); - }); - - // TODO MDL-50768 This hack is to keep Behat happy until they release a version of - // MinkSelenium2Driver that fixes - // https://github.com/Behat/MinkSelenium2Driver/issues/80. - if (!Y.one('input[name=title]')) { - Y.one('body').append(''); - } - }, - /** * Set the visibility of the specified resource to match the visible parameter. * diff --git a/course/yui/build/moodle-course-toolboxes/moodle-course-toolboxes-min.js b/course/yui/build/moodle-course-toolboxes/moodle-course-toolboxes-min.js index ec2b8fab4c329..290b316e7e407 100644 --- a/course/yui/build/moodle-course-toolboxes/moodle-course-toolboxes-min.js +++ b/course/yui/build/moodle-course-toolboxes/moodle-course-toolboxes-min.js @@ -1,3 +1,2 @@ -YUI.add("moodle-course-toolboxes",function(e,t){var n={ACTIVITYINSTANCE:"activityinstance",AVAILABILITYINFODIV:"div.availabilityinfo",CONTENTWITHOUTLINK:"contentwithoutlink",CONDITIONALHIDDEN:"conditionalhidden",DIMCLASS:"dimmed",DIMMEDTEXT:"dimmed_text",EDITINSTRUCTIONS:"editinstructions",EDITINGTITLE:"editor_displayed",HIDE:"hide",MODINDENTCOUNT:"mod-indent-",MODINDENTHUGE:"mod-indent-huge",MODULEIDPREFIX:"module-",SECTIONHIDDENCLASS:"hidden",SECTIONIDPREFIX:"section-",SHOW:"editing_show",TITLEEDITOR:"titleeditor"},r={ACTIONAREA:".actions",ACTIONLINKTEXT:".actionlinktext",ACTIVITYACTION:"a.cm-edit-action[data-action], a.editing_title",ACTIVITYFORM:"."+n.ACTIVITYINSTANCE+" form",ACTIVITYICON:"img.activityicon",ACTIVITYINSTANCE:"."+n.ACTIVITYINSTANCE,ACTIVITYLINK:"."+n.ACTIVITYINSTANCE+" > a",ACTIVITYLI:"li.activity",ACTIVITYTITLE:"input[name=title]",COMMANDSPAN:".commands",CONTENTAFTERLINK:"div.contentafterlink",CONTENTWITHOUTLINK:"div.contentwithoutlink",EDITTITLE:"a.editing_title",HIDE:"a.editing_hide",HIGHLIGHT:"a.editing_highlight",INSTANCENAME:"span.instancename",MODINDENTDIV:".mod-indent",MODINDENTOUTER:".mod-indent-outer",PAGECONTENT:"body",SECTIONLI:"li.section",SHOW:"a."+n.SHOW,SHOWHIDE:"a.editing_showhide"},i={MIN:0,MAX:16},s=e.one(document.body);M.course=M.course||{};var o=function(){o.superclass.constructor.apply(this,arguments)};e.extend(o,e.Base,{send_request:function(t,n,r,i){t||(t={});var s=this.get("config").pageparams,o;for(o in s)t[o]=s[o];t.sesskey=M.cfg.sesskey,t.courseId=this.get("courseid");var u=M.cfg.wwwroot+this.get("ajaxurl"),a=[],f={method:"POST",data:t,on:{success:function(t,i){try{a=e.JSON.parse(i.responseText),a.error&&new M.core.ajaxException(a)}catch(s){}r&&e.bind(r,this,a)(),n&&window.setTimeout(function(){n.hide()},400)},failure:function(e,t){n&&n.hide(),new M.core.ajaxException(t)}},context:this};if(i)for(o in i)f[o]=i[o];return n&&n.show(),e.io(u,f),this}},{NAME:"course-toolbox",ATTRS:{courseid:{value:0},format:{value:"topics"},ajaxurl:{value:null},config:{value:{}}}});var u=function(){u.superclass.constructor.apply(this,arguments)};e.extend(u,o,{GROUPS_NONE:0,GROUPS_SEPARATE:1,GROUPS_VISIBLE:2,edittitleevents:[],initializer:function(){M.course.coursebase.register_module(this),s.delegate("key",this.handle_data_action,"down:enter",r.ACTIVITYACTION,this),e.delegate("click",this.handle_data_action,s,r.ACTIVITYACTION,this)},handle_data_action:function(e){var t=e.target;t.test("a")||(t=t.ancestor(r.ACTIVITYACTION));var n=t.getData("action"),i=t.ancestor(r.ACTIVITYLI);if(!t.test("a")||!n||!i)return;switch(n){case"edittitle":this.edit_title(e,t,i,n);break;case"moveleft":case"moveright":this.change_indent(e,t,i,n);break;case"delete":this.delete_with_confirmation(e,t,i,n);break;case"duplicate":this.duplicate(e,t,i,n);break;case"hide":case"show":this.change_visibility(e,t,i,n);break;case"groupsseparate":case"groupsvisible":case"groupsnone":this.change_groupmode(e,t,i,n);break;case"move":case"update":case"duplicate":case"assignroles":break;default:}},add_spinner:function(t){var n=t.one(r.ACTIONAREA);return n?M.util.add_spinner(e,n):null},change_indent:function(t,s,o,u){t.preventDefault();var a=u==="moveleft"?-1:1,f=o.one(r.MODINDENTDIV),l=f.getAttribute("class").match(/mod-indent-(\d{1,})/),c=0,h;l&&(c=parseInt(l[1],10)),h=c+parseInt(a,10);if(hi.MAX)return;l&&f.removeClass(l[0]),f.addClass(n.MODINDENTCOUNT+h);var p={"class":"resource",field:"indent",value:h,id:e.Moodle.core_course.util.cm.getId(o)},d=this.add_spinner(o);this.send_request(p,d);var v;h===i.MIN?(s.addClass("hidden"),v=o.one(".editing_moveright")):h>i.MIN&&c===i.MIN&&s.ancestor(".menu").one("[data-action=moveleft]").removeClass("hidden"),h===i.MAX?(s.addClass("hidden"),v=o.one(".editing_moveleft")):h15&&!m?f.addClass(n.MODINDENTHUGE):h<=15&&m&&f.removeClass(n.MODINDENTHUGE),t.type&&t.type==="key"&&v&&v.focus()},delete_with_confirmation:function(t,n,r){t.preventDefault();var i=r,s="",o={type:M.util.get_string("pluginname",i.getAttribute("class").match(/modtype_([^\s]*)/)[1])};e.Moodle.core_course.util.cm.getName(i)!==null?(o.name=e.Moodle.core_course.util.cm.getName(i),s=M.util.get_string("deletechecktypename","moodle",o)):s=M.util.get_string("deletechecktype","moodle",o);var u=new M.core.confirm({question:s,modal:!0,visible:!1});return u.show(),u.on("complete-yes",function(){i.remove();var n={"class":"resource",action:"DELETE",id:e.Moodle.core_course.util.cm.getId(i)};this.send_request(n),M.core.actionmenu&&M.core.actionmenu.instance&&M.core.actionmenu.instance.hideMenu(t)},this),this},duplicate:function(t,n,r){t.preventDefault();var i=r,s=r.ancestor(M.course.format.get_section_selector(e)),o=M.util.add_lightbox(e,s).show(),u={"class":"resource",field:"duplicate",id:e.Moodle.core_course.util.cm.getId(i),sr:n.getData("sr")};return this.send_request(u,o,function(t){var n=e.Node.create(t.fullcontent);r.insert(n,"after"),e.use("moodle-course-coursebase",function(){M.course.coursebase.invoke_function("setup_for_resource",n)}),M.core.actionmenu&&M.core.actionmenu.newDOMNode&&M.core.actionmenu.newDOMNode(n)}),this},change_visibility:function(t,n,r,i){t.preventDefault();var s=r,o=this.handle_resource_dim(n,r,i),u={"class":"resource",field:"visible",value:o,id:e.Moodle.core_course.util.cm.getId(s)},a=this.add_spinner(s);return this.send_request(u,a),this},handle_resource_dim:function(t,i,s){var o=n.DIMCLASS,u=i.one([r.ACTIVITYLINK,r.CONTENTWITHOUTLINK].join(", ")),a=i.one(n.AVAILABILITYINFODIV),f=s==="hide"?"show":"hide",l=t.one("span"),c=M.util.get_string(f,"moodle"),h=t.one("img");return h.setAttrs({src:M.util.image_url("t/"+f)}),e.Lang.trim(t.getAttribute("title"))&&t.setAttribute("title",c),e.Lang.trim(h.getAttribute("alt"))&&h.setAttribute("alt",c),t.replaceClass("editing_"+s,"editing_"+f),t.setData("action",f),l&&l.set("text",c),i.one(r.CONTENTWITHOUTLINK)&&(u=i.one( -r.CONTENTWITHOUTLINK),o=n.DIMMEDTEXT),u.hasClass(n.CONDITIONALHIDDEN)||(s==="hide"?(u.addClass(o),i.all(r.CONTENTAFTERLINK).addClass(n.DIMMEDTEXT)):(u.removeClass(o),i.all(r.CONTENTAFTERLINK).removeClass(n.DIMMEDTEXT))),a&&a.toggleClass(n.HIDE),s==="hide"?0:1},change_groupmode:function(t,n,r){t.preventDefault();var i=parseInt(n.getData("nextgroupmode"),10),s="",o="",u,a,f,l=i+1,c=n.one("img");l>2&&(l=0),i===this.GROUPS_NONE?(s="groupsnone",o=M.util.image_url("i/groupn","moodle")):i===this.GROUPS_SEPARATE?(s="groupsseparate",o=M.util.image_url("i/groups","moodle")):i===this.GROUPS_VISIBLE&&(s="groupsvisible",o=M.util.image_url("i/groupv","moodle")),u=M.util.get_string("clicktochangeinbrackets","moodle",M.util.get_string(s,"moodle"));var h=n.getData("action");return n.replaceClass("editing_"+h,"editing_"+s),c.setAttrs({src:o}),e.Lang.trim(n.getAttribute("title"))&&n.setAttribute("title",u).setData("action",s).setData("nextgroupmode",l),e.Lang.trim(c.getAttribute("alt"))&&c.setAttribute("alt",u),a={"class":"resource",field:"groupmode",value:i,id:e.Moodle.core_course.util.cm.getId(r)},f=this.add_spinner(r),this.send_request(a,f),this},edit_title:function(t,i,s){var o=e.Moodle.core_course.util.cm.getId(s),u=s.one(r.INSTANCENAME),a=s.one(r.ACTIVITYINSTANCE),f=u.get("firstChild"),l=f.get("data"),c=l,h,p=u.ancestor("a"),d={"class":"resource",field:"gettitle",id:o};return t.preventDefault(),this.send_request(d,null,function(i){M.core.actionmenu&&M.core.actionmenu.instance&&M.core.actionmenu.instance.hideMenu(t),i.instancename&&(c=i.instancename);var o=e.Node.create(''),u=e.Node.create('').set("innerHTML",M.util.get_string("edittitleinstructions","moodle")),f=e.Node.create('').setAttrs({value:c,autocomplete:"off","aria-describedby":"id_editinstructions",maxLength:"255"});o.appendChild(s.one(r.ACTIVITYICON).cloneNode()),o.appendChild(f),o.setData("anchor",p),a.insert(u,"before"),p.replace(o);var d="left";window.right_to_left()&&(d="right"),s.addClass(n.EDITINGTITLE),f.focus().select(),h=f.on("blur",this.edit_title_cancel,this,s,!1),this.edittitleevents.push(h),h=f.on("key",this.edit_title_cancel,"esc",this,s,!0),this.edittitleevents.push(h),h=o.on("submit",this.edit_title_submit,this,s,l),this.edittitleevents.push(h)}),this},edit_title_submit:function(t,n,i){t.preventDefault();var s=e.Lang.trim(n.one(r.ACTIVITYFORM+" "+r.ACTIVITYTITLE).get("value"));this.edit_title_clear(n);var o=this.add_spinner(n);if(s!==null&&s!==""&&s!==i){var u={"class":"resource",field:"updatetitle",title:s,id:e.Moodle.core_course.util.cm.getId(n)};this.send_request(u,o,function(e){e.instancename&&n.one(r.INSTANCENAME).setContent(e.instancename)})}},edit_title_cancel:function(e,t,n){n&&e.preventDefault(),this.edit_title_clear(t)},edit_title_clear:function(t){(new e.EventHandle(this.edittitleevents)).detach();var i=t.one(r.ACTIVITYFORM),s=t.one("#id_editinstructions");i&&i.replace(i.getData("anchor")),s&&s.remove(),t.removeClass(n.EDITINGTITLE),e.later(100,this,function(){t.one(r.EDITTITLE).focus()}),e.one("input[name=title]")||e.one("body").append('')},set_visibility_resource_ui:function(e){var t=e.element,n=t.one(r.HIDE),i=!0,s=!1;n||(n=t.one(r.SHOW),i=!1,s=!0),typeof e.visible!="undefined"&&(s=e.visible);if(i!==s){var o="hide";s&&(o="show"),this.handle_resource_dim(n,t,o)}}},{NAME:"course-resource-toolbox",ATTRS:{}}),M.course.resource_toolbox=null,M.course.init_resource_toolbox=function(e){return M.course.resource_toolbox=new u(e),M.course.resource_toolbox};var a=function(){a.superclass.constructor.apply(this,arguments)};e.extend(a,o,{initializer:function(){M.course.coursebase.register_module(this),e.delegate("click",this.toggle_highlight,r.PAGECONTENT,r.SECTIONLI+" "+r.HIGHLIGHT,this),e.delegate("click",this.toggle_hide_section,r.PAGECONTENT,r.SECTIONLI+" "+r.SHOWHIDE,this)},toggle_hide_section:function(t){t.preventDefault();var i=t.target.ancestor(M.course.format.get_section_selector(e)),s=t.target.ancestor("a",!0),o=s.one("img"),u=s.one("span"),a,f,l;i.hasClass(n.SECTIONHIDDENCLASS)?(i.removeClass(n.SECTIONHIDDENCLASS),a=1,f="show",l="hide"):(i.addClass(n.SECTIONHIDDENCLASS),a=0,f="hide",l="show");var c=M.util.get_string(l+"fromothers","format_"+this.get("format"));o.setAttrs({alt:c,src:M.util.image_url("i/"+l)}),s.set("title",c),u&&u.set("text",c);var h={"class":"section",field:"visible",id:e.Moodle.core_course.util.section.getId(i.ancestor(M.course.format.get_section_wrapper(e),!0)),value:a},p=M.util.add_lightbox(e,i);p.show(),this.send_request(h,p,function(t){var n=i.all(r.ACTIVITYLI);n.each(function(n){var i;n.one(r.SHOW)?i=n.one(r.SHOW):i=n.one(r.HIDE);var s=e.Moodle.core_course.util.cm.getId(n);e.Array.indexOf(t.resourcestotoggle,""+s)!==-1&&M.course.resource_toolbox.handle_resource_dim(i,n,f)},this)})},toggle_highlight:function(t){t.preventDefault();var n=t.target.ancestor(M.course.format.get_section_selector(e)),i=t.target.ancestor("a",!0),s=i.one("img"),o=i.one("span"),u=n.hasClass("current"),a=0,f=M.util.get_string("markthistopic","moodle"),l=e.one(r.PAGECONTENT);l.all(M.course.format.get_section_selector(e)+".current "+r.HIGHLIGHT).set("title",f),l.all(M.course.format.get_section_selector(e)+".current "+r.HIGHLIGHT+" span").set("text",M.util.get_string("highlight","moodle")),l.all(M.course.format.get_section_selector(e)+".current "+r.HIGHLIGHT+" img").set("alt",f).set("src",M.util.image_url("i/marker")),l.all(M.course.format.get_section_selector(e)).removeClass("current");if(!u){n.addClass("current"),a=e.Moodle.core_course.util.section.getId(n.ancestor(M.course.format.get_section_wrapper(e),!0));var c=M.util.get_string("markedthistopic","moodle");i.set("title",c),s.set("alt",c).set("src",M.util.image_url("i/marked")),o&&o.set("text",M.util.get_string("highlightoff","moodle"))}var h={"class":"course",field:"marker",value -:a},p=M.util.add_lightbox(e,n);p.show(),this.send_request(h,p)}},{NAME:"course-section-toolbox",ATTRS:{}}),M.course.init_section_toolbox=function(e){return new a(e)}},"@VERSION@",{requires:["node","base","event-key","node","io","moodle-course-coursebase","moodle-course-util"]}); +YUI.add("moodle-course-toolboxes",function(e,t){var n={ACTIVITYINSTANCE:"activityinstance",AVAILABILITYINFODIV:"div.availabilityinfo",CONTENTWITHOUTLINK:"contentwithoutlink",CONDITIONALHIDDEN:"conditionalhidden",DIMCLASS:"dimmed",DIMMEDTEXT:"dimmed_text",EDITINSTRUCTIONS:"editinstructions",HIDE:"hide",MODINDENTCOUNT:"mod-indent-",MODINDENTHUGE:"mod-indent-huge",MODULEIDPREFIX:"module-",SECTIONHIDDENCLASS:"hidden",SECTIONIDPREFIX:"section-",SHOW:"editing_show"},r={ACTIONAREA:".actions",ACTIONLINKTEXT:".actionlinktext",ACTIVITYACTION:"a.cm-edit-action[data-action]",ACTIVITYICON:"img.activityicon",ACTIVITYINSTANCE:"."+n.ACTIVITYINSTANCE,ACTIVITYLINK:"."+n.ACTIVITYINSTANCE+" > a, ."+n.ACTIVITYINSTANCE+" > span[data-inplaceeditable] > a:not([data-inplaceeditablelink])",ACTIVITYLI:"li.activity",COMMANDSPAN:".commands",CONTENTAFTERLINK:"div.contentafterlink",CONTENTWITHOUTLINK:"div.contentwithoutlink",GROUPINGLABEL:"."+n.ACTIVITYINSTANCE+" .groupinglabel",HIDE:"a.editing_hide",HIGHLIGHT:"a.editing_highlight",INSTANCENAME:"span.instancename",MODINDENTDIV:".mod-indent",MODINDENTOUTER:".mod-indent-outer",PAGECONTENT:"body",SECTIONLI:"li.section",SHOW:"a."+n.SHOW,SHOWHIDE:"a.editing_showhide"},i={MIN:0,MAX:16},s=e.one(document.body);M.course=M.course||{};var o=function(){o.superclass.constructor.apply(this,arguments)};e.extend(o,e.Base,{send_request:function(t,n,r,i){t||(t={});var s=this.get("config").pageparams,o;for(o in s)t[o]=s[o];t.sesskey=M.cfg.sesskey,t.courseId=this.get("courseid");var u=M.cfg.wwwroot+this.get("ajaxurl"),a=[],f={method:"POST",data:t,on:{success:function(t,i){try{a=e.JSON.parse(i.responseText),a.error&&new M.core.ajaxException(a)}catch(s){}r&&e.bind(r,this,a)(),n&&window.setTimeout(function(){n.hide()},400)},failure:function(e,t){n&&n.hide(),new M.core.ajaxException(t)}},context:this};if(i)for(o in i)f[o]=i[o];return n&&n.show(),e.io(u,f),this}},{NAME:"course-toolbox",ATTRS:{courseid:{value:0},format:{value:"topics"},ajaxurl:{value:null},config:{value:{}}}});var u=function(){u.superclass.constructor.apply(this,arguments)};e.extend(u,o,{GROUPS_NONE:0,GROUPS_SEPARATE:1,GROUPS_VISIBLE:2,initializer:function(){M.course.coursebase.register_module(this),s.delegate("key",this.handle_data_action,"down:enter",r.ACTIVITYACTION,this),e.delegate("click",this.handle_data_action,s,r.ACTIVITYACTION,this)},handle_data_action:function(e){var t=e.target;t.test("a")||(t=t.ancestor(r.ACTIVITYACTION));var n=t.getData("action"),i=t.ancestor(r.ACTIVITYLI);if(!t.test("a")||!n||!i)return;switch(n){case"moveleft":case"moveright":this.change_indent(e,t,i,n);break;case"delete":this.delete_with_confirmation(e,t,i,n);break;case"duplicate":this.duplicate(e,t,i,n);break;case"hide":case"show":this.change_visibility(e,t,i,n);break;case"groupsseparate":case"groupsvisible":case"groupsnone":this.change_groupmode(e,t,i,n);break;case"move":case"update":case"duplicate":case"assignroles":break;default:}},add_spinner:function(t){var n=t.one(r.ACTIONAREA);return n?M.util.add_spinner(e,n):null},change_indent:function(t,s,o,u){t.preventDefault();var a=u==="moveleft"?-1:1,f=o.one(r.MODINDENTDIV),l=f.getAttribute("class").match(/mod-indent-(\d{1,})/),c=0,h;l&&(c=parseInt(l[1],10)),h=c+parseInt(a,10);if(hi.MAX)return;l&&f.removeClass(l[0]),f.addClass(n.MODINDENTCOUNT+h);var p={"class":"resource",field:"indent",value:h,id:e.Moodle.core_course.util.cm.getId(o)},d=this.add_spinner(o);this.send_request(p,d);var v;h===i.MIN?(s.addClass("hidden"),v=o.one(".editing_moveright")):h>i.MIN&&c===i.MIN&&s.ancestor(".menu").one("[data-action=moveleft]").removeClass("hidden"),h===i.MAX?(s.addClass("hidden"),v=o.one(".editing_moveleft")):h15&&!m?f.addClass(n.MODINDENTHUGE):h<=15&&m&&f.removeClass(n.MODINDENTHUGE),t.type&&t.type==="key"&&v&&v.focus()},delete_with_confirmation:function(t,n,r){t.preventDefault();var i=r,s="",o={type:M.util.get_string("pluginname",i.getAttribute("class").match(/modtype_([^\s]*)/)[1])};e.Moodle.core_course.util.cm.getName(i)!==null?(o.name=e.Moodle.core_course.util.cm.getName(i),s=M.util.get_string("deletechecktypename","moodle",o)):s=M.util.get_string("deletechecktype","moodle",o);var u=new M.core.confirm({question:s,modal:!0,visible:!1});return u.show(),u.on("complete-yes",function(){i.remove();var n={"class":"resource",action:"DELETE",id:e.Moodle.core_course.util.cm.getId(i)};this.send_request(n),M.core.actionmenu&&M.core.actionmenu.instance&&M.core.actionmenu.instance.hideMenu(t)},this),this},duplicate:function(t,n,r){t.preventDefault();var i=r,s=r.ancestor(M.course.format.get_section_selector(e)),o=M.util.add_lightbox(e,s).show(),u={"class":"resource",field:"duplicate",id:e.Moodle.core_course.util.cm.getId(i),sr:n.getData("sr")};return this.send_request(u,o,function(t){var n=e.Node.create(t.fullcontent);r.insert(n,"after"),e.use("moodle-course-coursebase",function(){M.course.coursebase.invoke_function("setup_for_resource",n)}),M.core.actionmenu&&M.core.actionmenu.newDOMNode&&M.core.actionmenu.newDOMNode(n)}),this},change_visibility:function(t,n,r,i){t.preventDefault();var s=r,o=this.handle_resource_dim(n,r,i),u={"class":"resource",field:"visible",value:o,id:e.Moodle.core_course.util.cm.getId(s)},a=this.add_spinner(s);return this.send_request(u,a),this},handle_resource_dim:function(t,i,s){var o=n.DIMCLASS,u=i.one([r.ACTIVITYLINK,r.CONTENTWITHOUTLINK].join(", ")),a=i.one(n.AVAILABILITYINFODIV),f=s==="hide"?"show":"hide",l=t.one("span"),c=M.util.get_string(f,"moodle"),h=t.one("img");return h.setAttrs({src:M.util.image_url("t/"+f)}),e.Lang.trim(t.getAttribute("title"))&&t.setAttribute("title",c),e.Lang.trim(h.getAttribute("alt"))&&h.setAttribute("alt",c),t.replaceClass("editing_"+s,"editing_"+f),t.setData("action",f),l&&l.set("text",c),i.one(r.CONTENTWITHOUTLINK)&&(u=i.one(r.CONTENTWITHOUTLINK),o=n.DIMMEDTEXT),u.hasClass(n.CONDITIONALHIDDEN)||(s==="hide"?(u.addClass(o),i.all +(r.CONTENTAFTERLINK).addClass(n.DIMMEDTEXT),i.all(r.GROUPINGLABEL).addClass(n.DIMMEDTEXT)):(u.removeClass(o),i.all(r.CONTENTAFTERLINK).removeClass(n.DIMMEDTEXT),i.all(r.GROUPINGLABEL).removeClass(n.DIMMEDTEXT))),a&&a.toggleClass(n.HIDE),s==="hide"?0:1},change_groupmode:function(t,n,r){t.preventDefault();var i=parseInt(n.getData("nextgroupmode"),10),s="",o="",u,a,f,l=i+1,c=n.one("img");l>2&&(l=0),i===this.GROUPS_NONE?(s="groupsnone",o=M.util.image_url("i/groupn","moodle")):i===this.GROUPS_SEPARATE?(s="groupsseparate",o=M.util.image_url("i/groups","moodle")):i===this.GROUPS_VISIBLE&&(s="groupsvisible",o=M.util.image_url("i/groupv","moodle")),u=M.util.get_string("clicktochangeinbrackets","moodle",M.util.get_string(s,"moodle"));var h=n.getData("action");return n.replaceClass("editing_"+h,"editing_"+s),c.setAttrs({src:o}),e.Lang.trim(n.getAttribute("title"))&&n.setAttribute("title",u).setData("action",s).setData("nextgroupmode",l),e.Lang.trim(c.getAttribute("alt"))&&c.setAttribute("alt",u),a={"class":"resource",field:"groupmode",value:i,id:e.Moodle.core_course.util.cm.getId(r)},f=this.add_spinner(r),this.send_request(a,f),this},set_visibility_resource_ui:function(e){var t=e.element,n=t.one(r.HIDE),i=!0,s=!1;n||(n=t.one(r.SHOW),i=!1,s=!0),typeof e.visible!="undefined"&&(s=e.visible);if(i!==s){var o="hide";s&&(o="show"),this.handle_resource_dim(n,t,o)}}},{NAME:"course-resource-toolbox",ATTRS:{}}),M.course.resource_toolbox=null,M.course.init_resource_toolbox=function(e){return M.course.resource_toolbox=new u(e),M.course.resource_toolbox};var a=function(){a.superclass.constructor.apply(this,arguments)};e.extend(a,o,{initializer:function(){M.course.coursebase.register_module(this),e.delegate("click",this.toggle_highlight,r.PAGECONTENT,r.SECTIONLI+" "+r.HIGHLIGHT,this),e.delegate("click",this.toggle_hide_section,r.PAGECONTENT,r.SECTIONLI+" "+r.SHOWHIDE,this)},toggle_hide_section:function(t){t.preventDefault();var i=t.target.ancestor(M.course.format.get_section_selector(e)),s=t.target.ancestor("a",!0),o=s.one("img"),u=s.one("span"),a,f,l;i.hasClass(n.SECTIONHIDDENCLASS)?(i.removeClass(n.SECTIONHIDDENCLASS),a=1,f="show",l="hide"):(i.addClass(n.SECTIONHIDDENCLASS),a=0,f="hide",l="show");var c=M.util.get_string(l+"fromothers","format_"+this.get("format"));o.setAttrs({alt:c,src:M.util.image_url("i/"+l)}),s.set("title",c),u&&u.set("text",c);var h={"class":"section",field:"visible",id:e.Moodle.core_course.util.section.getId(i.ancestor(M.course.format.get_section_wrapper(e),!0)),value:a},p=M.util.add_lightbox(e,i);p.show(),this.send_request(h,p,function(t){var n=i.all(r.ACTIVITYLI);n.each(function(n){var i;n.one(r.SHOW)?i=n.one(r.SHOW):i=n.one(r.HIDE);var s=e.Moodle.core_course.util.cm.getId(n);e.Array.indexOf(t.resourcestotoggle,""+s)!==-1&&M.course.resource_toolbox.handle_resource_dim(i,n,f)},this)})},toggle_highlight:function(t){t.preventDefault();var n=t.target.ancestor(M.course.format.get_section_selector(e)),i=t.target.ancestor("a",!0),s=i.one("img"),o=i.one("span"),u=n.hasClass("current"),a=0,f=M.util.get_string("markthistopic","moodle"),l=e.one(r.PAGECONTENT);l.all(M.course.format.get_section_selector(e)+".current "+r.HIGHLIGHT).set("title",f),l.all(M.course.format.get_section_selector(e)+".current "+r.HIGHLIGHT+" span").set("text",M.util.get_string("highlight","moodle")),l.all(M.course.format.get_section_selector(e)+".current "+r.HIGHLIGHT+" img").set("alt",f).set("src",M.util.image_url("i/marker")),l.all(M.course.format.get_section_selector(e)).removeClass("current");if(!u){n.addClass("current"),a=e.Moodle.core_course.util.section.getId(n.ancestor(M.course.format.get_section_wrapper(e),!0));var c=M.util.get_string("markedthistopic","moodle");i.set("title",c),s.set("alt",c).set("src",M.util.image_url("i/marked")),o&&o.set("text",M.util.get_string("highlightoff","moodle"))}var h={"class":"course",field:"marker",value:a},p=M.util.add_lightbox(e,n);p.show(),this.send_request(h,p)}},{NAME:"course-section-toolbox",ATTRS:{}}),M.course.init_section_toolbox=function(e){return new a(e)}},"@VERSION@",{requires:["node","base","event-key","node","io","moodle-course-coursebase","moodle-course-util"]}); diff --git a/course/yui/build/moodle-course-toolboxes/moodle-course-toolboxes.js b/course/yui/build/moodle-course-toolboxes/moodle-course-toolboxes.js index 03d72a6199038..75ef3b8772fc4 100644 --- a/course/yui/build/moodle-course-toolboxes/moodle-course-toolboxes.js +++ b/course/yui/build/moodle-course-toolboxes/moodle-course-toolboxes.js @@ -19,31 +19,28 @@ var CSS = { DIMCLASS : 'dimmed', DIMMEDTEXT : 'dimmed_text', EDITINSTRUCTIONS : 'editinstructions', - EDITINGTITLE: 'editor_displayed', HIDE : 'hide', MODINDENTCOUNT : 'mod-indent-', MODINDENTHUGE : 'mod-indent-huge', MODULEIDPREFIX : 'module-', SECTIONHIDDENCLASS : 'hidden', SECTIONIDPREFIX : 'section-', - SHOW : 'editing_show', - TITLEEDITOR : 'titleeditor' + SHOW : 'editing_show' }, // The CSS selectors we use. SELECTOR = { ACTIONAREA: '.actions', ACTIONLINKTEXT : '.actionlinktext', - ACTIVITYACTION : 'a.cm-edit-action[data-action], a.editing_title', - ACTIVITYFORM : '.' + CSS.ACTIVITYINSTANCE + ' form', + ACTIVITYACTION : 'a.cm-edit-action[data-action]', ACTIVITYICON : 'img.activityicon', ACTIVITYINSTANCE : '.' + CSS.ACTIVITYINSTANCE, - ACTIVITYLINK: '.' + CSS.ACTIVITYINSTANCE + ' > a', + ACTIVITYLINK: '.' + CSS.ACTIVITYINSTANCE + ' > a, .'+ CSS.ACTIVITYINSTANCE + + ' > span[data-inplaceeditable] > a:not([data-inplaceeditablelink])', ACTIVITYLI : 'li.activity', - ACTIVITYTITLE : 'input[name=title]', COMMANDSPAN : '.commands', CONTENTAFTERLINK : 'div.contentafterlink', CONTENTWITHOUTLINK : 'div.contentwithoutlink', - EDITTITLE: 'a.editing_title', + GROUPINGLABEL: '.' + CSS.ACTIVITYINSTANCE + ' .groupinglabel', HIDE : 'a.editing_hide', HIGHLIGHT : 'a.editing_highlight', INSTANCENAME : 'span.instancename', @@ -256,17 +253,6 @@ Y.extend(RESOURCETOOLBOX, TOOLBOX, { */ GROUPS_VISIBLE: 2, - /** - * An Array of events added when editing a title. - * These should all be detached when editing is complete. - * - * @property edittitleevents - * @protected - * @type Array - * @protected - */ - edittitleevents: [], - /** * Initialize the resource toolbox * @@ -317,10 +303,6 @@ Y.extend(RESOURCETOOLBOX, TOOLBOX, { // Switch based upon the action and do the desired thing. switch (action) { - case 'edittitle': - // The user wishes to edit the title of the event. - this.edit_title(ev, node, activity, action); - break; case 'moveleft': case 'moveright': // The user changing the indent of the activity. @@ -633,11 +615,13 @@ Y.extend(RESOURCETOOLBOX, TOOLBOX, { dimarea.addClass(toggleclass); // We need to toggle dimming on the description too. activity.all(SELECTOR.CONTENTAFTERLINK).addClass(CSS.DIMMEDTEXT); + activity.all(SELECTOR.GROUPINGLABEL).addClass(CSS.DIMMEDTEXT); } else { // Change the UI. dimarea.removeClass(toggleclass); // We need to toggle dimming on the description too. activity.all(SELECTOR.CONTENTAFTERLINK).removeClass(CSS.DIMMEDTEXT); + activity.all(SELECTOR.GROUPINGLABEL).removeClass(CSS.DIMMEDTEXT); } } // Toggle availablity info for conditional activities. @@ -714,172 +698,6 @@ Y.extend(RESOURCETOOLBOX, TOOLBOX, { return this; }, - /** - * Edit the title for the resource - * - * @method edit_title - * @protected - * @param {EventFacade} ev The event that was fired. - * @param {Node} button The button that triggered this action. - * @param {Node} activity The activity node that this action will be performed on. - * @param {String} action The action that has been requested. - * @chainable - */ - edit_title: function(ev, button, activity) { - // Get the element we're working on - var activityid = Y.Moodle.core_course.util.cm.getId(activity), - instancename = activity.one(SELECTOR.INSTANCENAME), - instance = activity.one(SELECTOR.ACTIVITYINSTANCE), - currenttitle = instancename.get('firstChild'), - oldtitle = currenttitle.get('data'), - titletext = oldtitle, - thisevent, - anchor = instancename.ancestor('a'),// Grab the anchor so that we can swap it with the edit form. - data = { - 'class': 'resource', - 'field': 'gettitle', - 'id': activityid - }; - - // Prevent the default actions. - ev.preventDefault(); - - this.send_request(data, null, function(response) { - if (M.core.actionmenu && M.core.actionmenu.instance) { - M.core.actionmenu.instance.hideMenu(ev); - } - - // Try to retrieve the existing string from the server - if (response.instancename) { - titletext = response.instancename; - } - - // Create the editor and submit button - var editform = Y.Node.create(''); - var editinstructions = Y.Node.create('') - .set('innerHTML', M.util.get_string('edittitleinstructions', 'moodle')); - var editor = Y.Node.create('').setAttrs({ - 'value': titletext, - 'autocomplete': 'off', - 'aria-describedby': 'id_editinstructions', - 'maxLength': '255' - }); - - // Clear the existing content and put the editor in - editform.appendChild(activity.one(SELECTOR.ACTIVITYICON).cloneNode()); - editform.appendChild(editor); - editform.setData('anchor', anchor); - instance.insert(editinstructions, 'before'); - anchor.replace(editform); - - // Force the editing instruction to match the mod-indent position. - var padside = 'left'; - if (window.right_to_left()) { - padside = 'right'; - } - - // We hide various components whilst editing: - activity.addClass(CSS.EDITINGTITLE); - - // Focus and select the editor text - editor.focus().select(); - - // Cancel the edit if we lose focus or the escape key is pressed. - thisevent = editor.on('blur', this.edit_title_cancel, this, activity, false); - this.edittitleevents.push(thisevent); - thisevent = editor.on('key', this.edit_title_cancel, 'esc', this, activity, true); - this.edittitleevents.push(thisevent); - - // Handle form submission. - thisevent = editform.on('submit', this.edit_title_submit, this, activity, oldtitle); - this.edittitleevents.push(thisevent); - }); - return this; - }, - - /** - * Handles the submit event when editing the activity or resources title. - * - * @method edit_title_submit - * @protected - * @param {EventFacade} ev The event that triggered this. - * @param {Node} activity The activity whose title we are altering. - * @param {String} originaltitle The original title the activity or resource had. - */ - edit_title_submit: function(ev, activity, originaltitle) { - // We don't actually want to submit anything - ev.preventDefault(); - - var newtitle = Y.Lang.trim(activity.one(SELECTOR.ACTIVITYFORM + ' ' + SELECTOR.ACTIVITYTITLE).get('value')); - this.edit_title_clear(activity); - var spinner = this.add_spinner(activity); - if (newtitle !== null && newtitle !== "" && newtitle !== originaltitle) { - var data = { - 'class': 'resource', - 'field': 'updatetitle', - 'title': newtitle, - 'id': Y.Moodle.core_course.util.cm.getId(activity) - }; - this.send_request(data, spinner, function(response) { - if (response.instancename) { - activity.one(SELECTOR.INSTANCENAME).setContent(response.instancename); - } - }); - } - }, - - /** - * Handles the cancel event when editing the activity or resources title. - * - * @method edit_title_cancel - * @protected - * @param {EventFacade} ev The event that triggered this. - * @param {Node} activity The activity whose title we are altering. - * @param {Boolean} preventdefault If true we should prevent the default action from occuring. - */ - edit_title_cancel: function(ev, activity, preventdefault) { - if (preventdefault) { - ev.preventDefault(); - } - this.edit_title_clear(activity); - }, - - /** - * Handles clearing the editing UI and returning things to the original state they were in. - * - * @method edit_title_clear - * @protected - * @param {Node} activity The activity whose title we were altering. - */ - edit_title_clear: function(activity) { - // Detach all listen events to prevent duplicate triggers - new Y.EventHandle(this.edittitleevents).detach(); - - var editform = activity.one(SELECTOR.ACTIVITYFORM), - instructions = activity.one('#id_editinstructions'); - if (editform) { - editform.replace(editform.getData('anchor')); - } - if (instructions) { - instructions.remove(); - } - - // Remove the editing class again to revert the display. - activity.removeClass(CSS.EDITINGTITLE); - - // Refocus the link which was clicked originally so the user can continue using keyboard nav. - Y.later(100, this, function() { - activity.one(SELECTOR.EDITTITLE).focus(); - }); - - // TODO MDL-50768 This hack is to keep Behat happy until they release a version of - // MinkSelenium2Driver that fixes - // https://github.com/Behat/MinkSelenium2Driver/issues/80. - if (!Y.one('input[name=title]')) { - Y.one('body').append(''); - } - }, - /** * Set the visibility of the specified resource to match the visible parameter. * diff --git a/course/yui/src/toolboxes/js/resource.js b/course/yui/src/toolboxes/js/resource.js index 578630e347af3..3bb4e600ff153 100644 --- a/course/yui/src/toolboxes/js/resource.js +++ b/course/yui/src/toolboxes/js/resource.js @@ -52,17 +52,6 @@ Y.extend(RESOURCETOOLBOX, TOOLBOX, { */ GROUPS_VISIBLE: 2, - /** - * An Array of events added when editing a title. - * These should all be detached when editing is complete. - * - * @property edittitleevents - * @protected - * @type Array - * @protected - */ - edittitleevents: [], - /** * Initialize the resource toolbox * @@ -113,10 +102,6 @@ Y.extend(RESOURCETOOLBOX, TOOLBOX, { // Switch based upon the action and do the desired thing. switch (action) { - case 'edittitle': - // The user wishes to edit the title of the event. - this.edit_title(ev, node, activity, action); - break; case 'moveleft': case 'moveright': // The user changing the indent of the activity. @@ -429,11 +414,13 @@ Y.extend(RESOURCETOOLBOX, TOOLBOX, { dimarea.addClass(toggleclass); // We need to toggle dimming on the description too. activity.all(SELECTOR.CONTENTAFTERLINK).addClass(CSS.DIMMEDTEXT); + activity.all(SELECTOR.GROUPINGLABEL).addClass(CSS.DIMMEDTEXT); } else { // Change the UI. dimarea.removeClass(toggleclass); // We need to toggle dimming on the description too. activity.all(SELECTOR.CONTENTAFTERLINK).removeClass(CSS.DIMMEDTEXT); + activity.all(SELECTOR.GROUPINGLABEL).removeClass(CSS.DIMMEDTEXT); } } // Toggle availablity info for conditional activities. @@ -510,172 +497,6 @@ Y.extend(RESOURCETOOLBOX, TOOLBOX, { return this; }, - /** - * Edit the title for the resource - * - * @method edit_title - * @protected - * @param {EventFacade} ev The event that was fired. - * @param {Node} button The button that triggered this action. - * @param {Node} activity The activity node that this action will be performed on. - * @param {String} action The action that has been requested. - * @chainable - */ - edit_title: function(ev, button, activity) { - // Get the element we're working on - var activityid = Y.Moodle.core_course.util.cm.getId(activity), - instancename = activity.one(SELECTOR.INSTANCENAME), - instance = activity.one(SELECTOR.ACTIVITYINSTANCE), - currenttitle = instancename.get('firstChild'), - oldtitle = currenttitle.get('data'), - titletext = oldtitle, - thisevent, - anchor = instancename.ancestor('a'),// Grab the anchor so that we can swap it with the edit form. - data = { - 'class': 'resource', - 'field': 'gettitle', - 'id': activityid - }; - - // Prevent the default actions. - ev.preventDefault(); - - this.send_request(data, null, function(response) { - if (M.core.actionmenu && M.core.actionmenu.instance) { - M.core.actionmenu.instance.hideMenu(ev); - } - - // Try to retrieve the existing string from the server - if (response.instancename) { - titletext = response.instancename; - } - - // Create the editor and submit button - var editform = Y.Node.create(''); - var editinstructions = Y.Node.create('') - .set('innerHTML', M.util.get_string('edittitleinstructions', 'moodle')); - var editor = Y.Node.create('').setAttrs({ - 'value': titletext, - 'autocomplete': 'off', - 'aria-describedby': 'id_editinstructions', - 'maxLength': '255' - }); - - // Clear the existing content and put the editor in - editform.appendChild(activity.one(SELECTOR.ACTIVITYICON).cloneNode()); - editform.appendChild(editor); - editform.setData('anchor', anchor); - instance.insert(editinstructions, 'before'); - anchor.replace(editform); - - // Force the editing instruction to match the mod-indent position. - var padside = 'left'; - if (window.right_to_left()) { - padside = 'right'; - } - - // We hide various components whilst editing: - activity.addClass(CSS.EDITINGTITLE); - - // Focus and select the editor text - editor.focus().select(); - - // Cancel the edit if we lose focus or the escape key is pressed. - thisevent = editor.on('blur', this.edit_title_cancel, this, activity, false); - this.edittitleevents.push(thisevent); - thisevent = editor.on('key', this.edit_title_cancel, 'esc', this, activity, true); - this.edittitleevents.push(thisevent); - - // Handle form submission. - thisevent = editform.on('submit', this.edit_title_submit, this, activity, oldtitle); - this.edittitleevents.push(thisevent); - }); - return this; - }, - - /** - * Handles the submit event when editing the activity or resources title. - * - * @method edit_title_submit - * @protected - * @param {EventFacade} ev The event that triggered this. - * @param {Node} activity The activity whose title we are altering. - * @param {String} originaltitle The original title the activity or resource had. - */ - edit_title_submit: function(ev, activity, originaltitle) { - // We don't actually want to submit anything - ev.preventDefault(); - - var newtitle = Y.Lang.trim(activity.one(SELECTOR.ACTIVITYFORM + ' ' + SELECTOR.ACTIVITYTITLE).get('value')); - this.edit_title_clear(activity); - var spinner = this.add_spinner(activity); - if (newtitle !== null && newtitle !== "" && newtitle !== originaltitle) { - var data = { - 'class': 'resource', - 'field': 'updatetitle', - 'title': newtitle, - 'id': Y.Moodle.core_course.util.cm.getId(activity) - }; - this.send_request(data, spinner, function(response) { - if (response.instancename) { - activity.one(SELECTOR.INSTANCENAME).setContent(response.instancename); - } - }); - } - }, - - /** - * Handles the cancel event when editing the activity or resources title. - * - * @method edit_title_cancel - * @protected - * @param {EventFacade} ev The event that triggered this. - * @param {Node} activity The activity whose title we are altering. - * @param {Boolean} preventdefault If true we should prevent the default action from occuring. - */ - edit_title_cancel: function(ev, activity, preventdefault) { - if (preventdefault) { - ev.preventDefault(); - } - this.edit_title_clear(activity); - }, - - /** - * Handles clearing the editing UI and returning things to the original state they were in. - * - * @method edit_title_clear - * @protected - * @param {Node} activity The activity whose title we were altering. - */ - edit_title_clear: function(activity) { - // Detach all listen events to prevent duplicate triggers - new Y.EventHandle(this.edittitleevents).detach(); - - var editform = activity.one(SELECTOR.ACTIVITYFORM), - instructions = activity.one('#id_editinstructions'); - if (editform) { - editform.replace(editform.getData('anchor')); - } - if (instructions) { - instructions.remove(); - } - - // Remove the editing class again to revert the display. - activity.removeClass(CSS.EDITINGTITLE); - - // Refocus the link which was clicked originally so the user can continue using keyboard nav. - Y.later(100, this, function() { - activity.one(SELECTOR.EDITTITLE).focus(); - }); - - // TODO MDL-50768 This hack is to keep Behat happy until they release a version of - // MinkSelenium2Driver that fixes - // https://github.com/Behat/MinkSelenium2Driver/issues/80. - if (!Y.one('input[name=title]')) { - Y.one('body').append(''); - } - }, - /** * Set the visibility of the specified resource to match the visible parameter. * diff --git a/course/yui/src/toolboxes/js/toolbox.js b/course/yui/src/toolboxes/js/toolbox.js index 7fd7b3b6461af..e3f6c8a78332c 100644 --- a/course/yui/src/toolboxes/js/toolbox.js +++ b/course/yui/src/toolboxes/js/toolbox.js @@ -17,31 +17,28 @@ var CSS = { DIMCLASS : 'dimmed', DIMMEDTEXT : 'dimmed_text', EDITINSTRUCTIONS : 'editinstructions', - EDITINGTITLE: 'editor_displayed', HIDE : 'hide', MODINDENTCOUNT : 'mod-indent-', MODINDENTHUGE : 'mod-indent-huge', MODULEIDPREFIX : 'module-', SECTIONHIDDENCLASS : 'hidden', SECTIONIDPREFIX : 'section-', - SHOW : 'editing_show', - TITLEEDITOR : 'titleeditor' + SHOW : 'editing_show' }, // The CSS selectors we use. SELECTOR = { ACTIONAREA: '.actions', ACTIONLINKTEXT : '.actionlinktext', - ACTIVITYACTION : 'a.cm-edit-action[data-action], a.editing_title', - ACTIVITYFORM : '.' + CSS.ACTIVITYINSTANCE + ' form', + ACTIVITYACTION : 'a.cm-edit-action[data-action]', ACTIVITYICON : 'img.activityicon', ACTIVITYINSTANCE : '.' + CSS.ACTIVITYINSTANCE, - ACTIVITYLINK: '.' + CSS.ACTIVITYINSTANCE + ' > a', + ACTIVITYLINK: '.' + CSS.ACTIVITYINSTANCE + ' > a, .'+ CSS.ACTIVITYINSTANCE + + ' > span[data-inplaceeditable] > a:not([data-inplaceeditablelink])', ACTIVITYLI : 'li.activity', - ACTIVITYTITLE : 'input[name=title]', COMMANDSPAN : '.commands', CONTENTAFTERLINK : 'div.contentafterlink', CONTENTWITHOUTLINK : 'div.contentwithoutlink', - EDITTITLE: 'a.editing_title', + GROUPINGLABEL: '.' + CSS.ACTIVITYINSTANCE + ' .groupinglabel', HIDE : 'a.editing_hide', HIGHLIGHT : 'a.editing_highlight', INSTANCENAME : 'span.instancename', diff --git a/lang/en/moodle.php b/lang/en/moodle.php index 13641d5025d85..53c98f4a5abf7 100644 --- a/lang/en/moodle.php +++ b/lang/en/moodle.php @@ -1228,6 +1228,7 @@ $string['neverdeletelogs'] = 'Never delete logs'; $string['new'] = 'New'; $string['newaccount'] = 'New account'; +$string['newactivityname'] = 'New name for activity {$a}'; $string['newcourse'] = 'New course'; $string['newpassword'] = 'New password'; $string['newpassword_help'] = 'Enter a new password or leave blank to keep current password.'; diff --git a/lib/deprecatedlib.php b/lib/deprecatedlib.php index 5b806b14705df..b787c16f1cdd7 100644 --- a/lib/deprecatedlib.php +++ b/lib/deprecatedlib.php @@ -4408,3 +4408,54 @@ function get_clam_error_code($returncode) { $antivirus = \core\antivirus\manager::get_antivirus('clamav'); return $antivirus->get_clam_error_code($returncode); } + +/** + * Returns the rename action. + * + * @deprecated since 3.1 + * @param cm_info $mod The module to produce editing buttons for + * @param int $sr The section to link back to (used for creating the links) + * @return The markup for the rename action, or an empty string if not available. + */ +function course_get_cm_rename_action(cm_info $mod, $sr = null) { + global $COURSE, $OUTPUT; + + static $str; + static $baseurl; + + debugging('Function course_get_cm_rename_action() is deprecated. Please use inplace_editable ' . + 'https://docs.moodle.org/dev/Inplace_editable', DEBUG_DEVELOPER); + + $modcontext = context_module::instance($mod->id); + $hasmanageactivities = has_capability('moodle/course:manageactivities', $modcontext); + + if (!isset($str)) { + $str = get_strings(array('edittitle')); + } + + if (!isset($baseurl)) { + $baseurl = new moodle_url('/course/mod.php', array('sesskey' => sesskey())); + } + + if ($sr !== null) { + $baseurl->param('sr', $sr); + } + + // AJAX edit title. + if ($mod->has_view() && $hasmanageactivities && course_ajax_enabled($COURSE) && + (($mod->course == $COURSE->id) || ($mod->course == SITEID))) { + // we will not display link if we are on some other-course page (where we should not see this module anyway) + return html_writer::span( + html_writer::link( + new moodle_url($baseurl, array('update' => $mod->id)), + $OUTPUT->pix_icon('t/editstring', '', 'moodle', array('class' => 'iconsmall visibleifjs', 'title' => '')), + array( + 'class' => 'editing_title', + 'data-action' => 'edittitle', + 'title' => $str->edittitle, + ) + ) + ); + } + return ''; +} diff --git a/theme/upgrade.txt b/theme/upgrade.txt index 05587d51fff36..47492e5f24dd9 100644 --- a/theme/upgrade.txt +++ b/theme/upgrade.txt @@ -11,6 +11,9 @@ information provided here is intended especially for theme designer. * notification_problem.mustache => notification_error.mustache * notification_message => notification_info * notification_redirect => notification_warning +* Method core_course_renderer::course_section_cm_name() is split into two methods, custom themes that override + this method must be modified or otherwise editing functionality may become broken. This method is now also used by + social activities block and site menu block to display activities names. === 3.0 ===