Skip to content

Commit

Permalink
Merge branch 'wip-MDL-51802-master4' of https://github.com/marinaglan…
Browse files Browse the repository at this point in the history
  • Loading branch information
stronk7 committed Feb 16, 2016
2 parents c70d544 + f26481c commit 642a4fd
Show file tree
Hide file tree
Showing 39 changed files with 1,078 additions and 225 deletions.
23 changes: 1 addition & 22 deletions course/editsection.php
Original file line number Diff line number Diff line change
Expand Up @@ -114,28 +114,7 @@
$data->availability = null;
}
}
$DB->update_record('course_sections', $data);
rebuild_course_cache($course->id, true);
if (isset($data->section)) {
// Usually edit form does not change relative section number but just in case.
$sectionnum = $data->section;
}
course_get_format($course->id)->update_section_format_options($data);

// Set section info, as this might not be present in form_data.
if (!isset($data->section)) {
$data->section = $sectionnum;
}
// Trigger an event for course section update.
$event = \core\event\course_section_updated::create(
array(
'objectid' => $data->id,
'courseid' => $course->id,
'context' => $context,
'other' => array('sectionnum' => $data->section)
)
);
$event->trigger();
course_update_section($course, $section, $data);

$PAGE->navigation->clear_cache();
redirect(course_get_url($course, $section, array('sr' => $sectionreturn)));
Expand Down
70 changes: 70 additions & 0 deletions course/format/lib.php
Original file line number Diff line number Diff line change
Expand Up @@ -1030,6 +1030,76 @@ public function delete_section($section, $forcedeleteifnotempty = false) {

return true;
}

/**
* Prepares the templateable object to display section name
*
* @param \section_info|\stdClass $section
* @param bool $linkifneeded
* @param bool $editable
* @param null|lang_string|string $edithint
* @param null|lang_string|string $editlabel
* @return \core\output\inplace_editable
*/
public function inplace_editable_render_section_name($section, $linkifneeded = true,
$editable = null, $edithint = null, $editlabel = null) {
global $USER, $CFG;
require_once($CFG->dirroot.'/course/lib.php');

if ($editable === null) {
$editable = !empty($USER->editing) && has_capability('moodle/course:update',
context_course::instance($section->course));
}

$displayvalue = $title = get_section_name($section->course, $section);
if ($linkifneeded) {
// Display link under the section name if the course format setting is to display one section per page.
$url = course_get_url($section->course, $section->section, array('navigation' => true));
if ($url) {
$displayvalue = html_writer::link($url, $title);
}
$itemtype = 'sectionname';
} else {
// If $linkifneeded==false, we never display the link (this is used when rendering the section header).
// Itemtype 'sectionnamenl' (nl=no link) will tell the callback that link should not be rendered -
// there is no other way callback can know where we display the section name.
$itemtype = 'sectionnamenl';
}
if (empty($edithint)) {
$edithint = new lang_string('editsectionname');
}
if (empty($editlabel)) {
$editlabel = new lang_string('newsectionname', '', $title);
}

return new \core\output\inplace_editable('format_' . $this->format, $itemtype, $section->id, $editable,
$displayvalue, $section->name, $edithint, $editlabel);
}

/**
* Updates the value in the database and modifies this object respectively.
*
* ALWAYS check user permissions before performing an update! Throw exceptions if permissions are not sufficient
* or value is not legit.
*
* @param stdClass $section
* @param string $itemtype
* @param mixed $newvalue
* @return \core\output\inplace_editable
*/
public function inplace_editable_update_section_name($section, $itemtype, $newvalue) {
if ($itemtype === 'sectionname' || $itemtype === 'sectionnamenl') {
require_login($section->course, false, null, true, true);
$context = context_course::instance($section->course);
require_capability('moodle/course:update', $context);

$newtitle = clean_param($newvalue, PARAM_TEXT);
if (strval($section->name) !== strval($newtitle)) {
course_update_section($section->course, $section, array('name' => $newtitle));
}
return $this->inplace_editable_render_section_name($section, ($itemtype === 'sectionname'), true);
}
}
}

/**
Expand Down
17 changes: 14 additions & 3 deletions course/format/renderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ abstract protected function end_section_list();
abstract protected function page_title();

/**
* Generate the section title
* Generate the section title, wraps it in a link to the section page if page is to be displayed on a separate page
*
* @param stdClass $section The course_section entry from DB
* @param stdClass $course The course entry from DB
Expand All @@ -86,6 +86,17 @@ public function section_title($section, $course) {
return $title;
}

/**
* Generate the section title to be displayed on the section page, without a link
*
* @param stdClass $section The course_section entry from DB
* @param stdClass $course The course entry from DB
* @return string HTML to output.
*/
public function section_title_without_link($section, $course) {
return get_section_name($course, $section);
}

/**
* Generate the edit control action menu
*
Expand Down Expand Up @@ -193,7 +204,7 @@ protected function section_header($section, $course, $onsectionpage, $sectionret
'aria-label'=> get_section_name($course, $section)));

// Create a span that contains the section title to be used to create the keyboard section move menu.
$o .= html_writer::tag('span', $this->section_title($section, $course), array('class' => 'hidden sectionname'));
$o .= html_writer::tag('span', get_section_name($course, $section), array('class' => 'hidden sectionname'));

$leftcontent = $this->section_left_content($section, $course, $onsectionpage);
$o.= html_writer::tag('div', $leftcontent, array('class' => 'left side'));
Expand Down Expand Up @@ -788,7 +799,7 @@ public function print_single_section_page($course, $sections, $mods, $modnames,
if (!$thissection->visible) {
$classes .= ' dimmed_text';
}
$sectionname = html_writer::tag('span', get_section_name($course, $displaysection));
$sectionname = html_writer::tag('span', $this->section_title_without_link($thissection, $course));
$sectiontitle .= $this->output->heading($sectionname, 3, $classes);

$sectiontitle .= html_writer::end_tag('div');
Expand Down
2 changes: 2 additions & 0 deletions course/format/topics/lang/en/format_topics.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@

$string['currentsection'] = 'This topic';
$string['editsection'] = 'Edit topic';
$string['editsectionname'] = 'Edit topic name';
$string['deletesection'] = 'Delete topic';
$string['newsectionname'] = 'New name for topic {$a}';
$string['sectionname'] = 'Topic';
$string['pluginname'] = 'Topics format';
$string['section0name'] = 'General';
Expand Down
41 changes: 41 additions & 0 deletions course/format/topics/lib.php
Original file line number Diff line number Diff line change
Expand Up @@ -382,4 +382,45 @@ public function update_course_format_options($data, $oldcourse = null) {
public function can_delete_section($section) {
return true;
}

/**
* Prepares the templateable object to display section name
*
* @param \section_info|\stdClass $section
* @param bool $linkifneeded
* @param bool $editable
* @param null|lang_string|string $edithint
* @param null|lang_string|string $editlabel
* @return \core\output\inplace_editable
*/
public function inplace_editable_render_section_name($section, $linkifneeded = true,
$editable = null, $edithint = null, $editlabel = null) {
if (empty($edithint)) {
$edithint = new lang_string('editsectionname', 'format_topics');
}
if (empty($editlabel)) {
$title = get_section_name($section->course, $section);
$editlabel = new lang_string('newsectionname', 'format_topics', $title);
}
return parent::inplace_editable_render_section_name($section, $linkifneeded, $editable, $edithint, $editlabel);
}
}

/**
* 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 format_topics_inplace_editable($itemtype, $itemid, $newvalue) {
global $DB, $CFG;
require_once($CFG->dirroot . '/course/lib.php');
if ($itemtype === 'sectionname' || $itemtype === 'sectionnamenl') {
$section = $DB->get_record_sql(
'SELECT s.* FROM {course_sections} s JOIN {course} c ON s.course = c.id WHERE s.id = ? AND c.format = ?',
array($itemid, 'topics'), MUST_EXIST);
return course_get_format($section->course)->inplace_editable_update_section_name($section, $itemtype, $newvalue);
}
}
22 changes: 22 additions & 0 deletions course/format/topics/renderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,28 @@ protected function page_title() {
return get_string('topicoutline');
}

/**
* Generate the section title, wraps it in a link to the section page if page is to be displayed on a separate page
*
* @param stdClass $section The course_section entry from DB
* @param stdClass $course The course entry from DB
* @return string HTML to output.
*/
public function section_title($section, $course) {
return $this->render(course_get_format($course)->inplace_editable_render_section_name($section));
}

/**
* Generate the section title to be displayed on the section page, without a link
*
* @param stdClass $section The course_section entry from DB
* @param stdClass $course The course entry from DB
* @return string HTML to output.
*/
public function section_title_without_link($section, $course) {
return $this->render(course_get_format($course)->inplace_editable_render_section_name($section, false));
}

/**
* Generate the edit control items of a section
*
Expand Down
12 changes: 12 additions & 0 deletions course/format/topics/tests/behat/edit_delete_sections.feature
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,18 @@ Feature: Sections can be edited and deleted in topics format
Then I should see "This is the second topic" in the "li#section-2" "css_element"
And I should not see "Topic 2" in the "li#section-2" "css_element"

@javascript
Scenario: Inline edit section name in topics format
When I click on "Edit topic name" "link" in the "li#section-1" "css_element"
And I set the field "New name for topic Topic 1" to "Midterm evaluation"
And I press key "13" in the field "New name for topic Topic 1"
Then I should not see "Topic 1" in the "#region-main" "css_element"
And "New name for topic" "field" should not exist
And I should see "Midterm evaluation" in the "li#section-1" "css_element"
And I follow "Course 1"
And I should not see "Topic 1" in the "#region-main" "css_element"
And I should see "Midterm evaluation" in the "li#section-1" "css_element"

Scenario: Deleting the last section in topics format
When I delete section "5"
Then I should see "Are you absolutely sure you want to completely delete \"Topic 5\" and all the activities it contains?"
Expand Down
65 changes: 65 additions & 0 deletions course/format/topics/tests/format_topics_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,69 @@ public function test_get_default_section_name() {
}
}
}

/**
* Test web service updating section name
*/
public function test_update_inplace_editable() {
global $CFG, $DB, $PAGE;
require_once($CFG->dirroot . '/lib/external/externallib.php');

$this->resetAfterTest();
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
$course = $this->getDataGenerator()->create_course(array('numsections' => 5, 'format' => 'topics'),
array('createsections' => true));
$section = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 2));

// Call webservice without necessary permissions.
try {
core_external::update_inplace_editable('format_topics', 'sectionname', $section->id, 'New section name');
$this->fail('Exception expected');
} catch (moodle_exception $e) {
$this->assertEquals('Course or activity not accessible. (Not enrolled)',
$e->getMessage());
}

// Change to teacher and make sure that section name can be updated using web service update_inplace_editable().
$teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
$this->getDataGenerator()->enrol_user($user->id, $course->id, $teacherrole->id);

$res = core_external::update_inplace_editable('format_topics', 'sectionname', $section->id, 'New section name');
$res = external_api::clean_returnvalue(core_external::update_inplace_editable_returns(), $res);
$this->assertEquals('New section name', $res['value']);
$this->assertEquals('New section name', $DB->get_field('course_sections', 'name', array('id' => $section->id)));
}

/**
* Test callback updating section name
*/
public function test_inplace_editable() {
global $DB, $PAGE;

$this->resetAfterTest();
$user = $this->getDataGenerator()->create_user();
$course = $this->getDataGenerator()->create_course(array('numsections' => 5, 'format' => 'topics'),
array('createsections' => true));
$teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
$this->getDataGenerator()->enrol_user($user->id, $course->id, $teacherrole->id);
$this->setUser($user);

$section = $DB->get_record('course_sections', array('course' => $course->id, 'section' => 2));

// Call callback format_topics_inplace_editable() directly.
$tmpl = component_callback('format_topics', 'inplace_editable', array('sectionname', $section->id, 'Rename me again'));
$this->assertInstanceOf('core\output\inplace_editable', $tmpl);
$res = $tmpl->export_for_template($PAGE->get_renderer('core'));
$this->assertEquals('Rename me again', $res['value']);
$this->assertEquals('Rename me again', $DB->get_field('course_sections', 'name', array('id' => $section->id)));

// Try updating using callback from mismatching course format.
try {
$tmpl = component_callback('format_weeks', 'inplace_editable', array('sectionname', $section->id, 'New name'));
$this->fail('Exception expected');
} catch (moodle_exception $e) {
$this->assertEquals(1, preg_match('/^Can not find data record in database/', $e->getMessage()));
}
}
}
4 changes: 4 additions & 0 deletions course/format/upgrade.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ This files describes API changes for course formats

Overview of this plugin type at http://docs.moodle.org/dev/Course_formats

=== 3.1 ===
* Course format may use the inplace_editable template to allow quick editing of section names, see
https://docs.moodle.org/dev/Inplace_editable and MDL-51802 for example implementation.

=== 3.0 ===
* Course formats should now use section_edit_control_items and use the returned array of controls items and their attributes to create a
renderable menu or array of links. Plugin calls to section_edit_controls will now include the section edit control in the returned array.
Expand Down
2 changes: 2 additions & 0 deletions course/format/weeks/lang/en/format_weeks.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@

$string['currentsection'] = 'This week';
$string['editsection'] = 'Edit week';
$string['editsectionname'] = 'Edit week name';
$string['deletesection'] = 'Delete week';
$string['newsectionname'] = 'New name for week {$a}';
$string['sectionname'] = 'Week';
$string['pluginname'] = 'Weekly format';
$string['section0name'] = 'General';
Expand Down
41 changes: 41 additions & 0 deletions course/format/weeks/lib.php
Original file line number Diff line number Diff line change
Expand Up @@ -432,4 +432,45 @@ public function is_section_current($section) {
public function can_delete_section($section) {
return true;
}

/**
* Prepares the templateable object to display section name
*
* @param \section_info|\stdClass $section
* @param bool $linkifneeded
* @param bool $editable
* @param null|lang_string|string $edithint
* @param null|lang_string|string $editlabel
* @return \core\output\inplace_editable
*/
public function inplace_editable_render_section_name($section, $linkifneeded = true,
$editable = null, $edithint = null, $editlabel = null) {
if (empty($edithint)) {
$edithint = new lang_string('editsectionname', 'format_weeks');
}
if (empty($editlabel)) {
$title = get_section_name($section->course, $section);
$editlabel = new lang_string('newsectionname', 'format_weeks', $title);
}
return parent::inplace_editable_render_section_name($section, $linkifneeded, $editable, $edithint, $editlabel);
}
}

/**
* 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 format_weeks_inplace_editable($itemtype, $itemid, $newvalue) {
global $DB, $CFG;
require_once($CFG->dirroot . '/course/lib.php');
if ($itemtype === 'sectionname' || $itemtype === 'sectionnamenl') {
$section = $DB->get_record_sql(
'SELECT s.* FROM {course_sections} s JOIN {course} c ON s.course = c.id WHERE s.id = ? AND c.format = ?',
array($itemid, 'weeks'), MUST_EXIST);
return course_get_format($section->course)->inplace_editable_update_section_name($section, $itemtype, $newvalue);
}
}
Loading

0 comments on commit 642a4fd

Please sign in to comment.