Commit 951b414b authored by Damyon Wiese's avatar Damyon Wiese
Browse files

Merge branch 'MDL-40975-master' of git://github.com/andrewnicols/moodle

Conflicts:
	theme/bootstrapbase/style/moodle.css
parents c32ba4e2 1e5620a6
......@@ -21,14 +21,18 @@ Feature: Duplicate activities
And I add a "Database" to section "1" and I fill the form with:
| Name | Test database name |
| Description | Test database description |
And I click on "Actions" "link" in the "Test database name" activity
And I open "Test database name" actions menu
When I click on "Duplicate" "link" in the "Test database name" activity
And I press "Continue"
And I press "Edit the new copy"
And I click on "Edit settings" "link" in the "Test database name" activity
And I fill the moodle form with:
| Name | Original database name |
And I press "Save and return to course"
And I open "Test database name" actions menu
And I click on "Edit settings" "link" in the "Test database name" activity
And I fill the moodle form with:
| Name | Duplicated database name |
| Description | Duplicated database description |
And I press "Save and return to course"
Then I should see "Test database name" in the "#section-1" "css_element"
Then I should see "Original database name" in the "#section-1" "css_element"
And I should see "Duplicated database name" in the "#section-1" "css_element"
And "Test database name" "link" should appear before "Duplicated database name" "link"
And "Original database name" "link" should appear before "Duplicated database name" "link"
......@@ -40,7 +40,7 @@ Feature: Restore Moodle 2 course backups
Then I should see "Course 1 restored in a new course"
And I should see "Community finder" in the "Community finder" "block"
And I should see "Test forum name"
And I follow "Edit settings"
And I click on "Edit settings" "link" in the "Administration" "block"
And I expand all fieldsets
And the "id_format" field should match "Topics format" value
And the "Number of sections" field should match "15" value
......@@ -85,7 +85,7 @@ Feature: Restore Moodle 2 course backups
When I restore "test_backup.mbz" backup into a new course using this options:
Then I should see "Topic 1"
And I should see "Test forum name"
And I follow "Edit settings"
And I click on "Edit settings" "link" in the "Administration" "block"
And I expand all fieldsets
And the "id_format" field should match "Topics format" value
And I fill the moodle form with:
......@@ -96,14 +96,14 @@ Feature: Restore Moodle 2 course backups
And I press "Save changes"
And I should see "1 January - 7 January"
And I should see "Test forum name"
And I follow "Edit settings"
And I click on "Edit settings" "link" in the "Administration" "block"
And I expand all fieldsets
And the "id_format" field should match "Weekly format" value
And I fill the moodle form with:
| id_format | Social format |
And I press "Save changes"
And I should see "An open forum for chatting about anything you want to"
And I follow "Edit settings"
And I click on "Edit settings" "link" in the "Administration" "block"
And I expand all fieldsets
And the "id_format" field should match "Social format" value
And I press "Cancel"
......@@ -120,7 +120,7 @@ Feature: Restore Moodle 2 course backups
| Filename | test_backup.mbz |
And I restore "test_backup.mbz" backup into "Course 2" course using this options:
| Overwrite course configuration | Yes |
And I follow "Edit settings"
And I click on "Edit settings" "link" in the "Administration" "block"
And I expand all fieldsets
Then the "id_format" field should match "Topics format" value
And the "Number of sections" field should match "15" value
......
......@@ -18,6 +18,7 @@ Feature: Add and configure blocks throughout the site
And I log in as "manager1"
And I follow "Turn editing on"
And I add the "Comments" block
And I click on "Actions" "link" in the "Comments" "block"
And I follow "Configure Comments block"
And I fill the moodle form with:
| Page contexts | Display throughout the entire site |
......@@ -26,6 +27,7 @@ Feature: Add and configure blocks throughout the site
Then I should see "Comments" in the "Comments" "block"
And I should see "Save comment" in the "Comments" "block"
And I am on homepage
And I click on "Actions" "link" in the "Comments" "block"
And I follow "Configure Comments block"
And I fill the moodle form with:
| Default weight | -10 (first) |
......
......@@ -15,6 +15,7 @@ Feature: The context of a block can always be returned to it's original state.
And I add the "Tags" block
Then I should see "Tags" in the "Tags" "block"
And I click on "Participants" "link" in the "//li[p/span[contains(normalize-space(string(.)), 'Current course')]]" "xpath_element"
And I click on "Actions" "link" in the "Tags" "block"
And I follow "Configure Tags block"
And I fill the moodle form with:
| Display on page types | Any page |
......@@ -24,6 +25,7 @@ Feature: The context of a block can always be returned to it's original state.
| Assignment name | Assignment1 |
| Description | Description |
And I follow "Assignment1"
And I click on "Actions" "link" in the "Tags" "block"
And I follow "Configure Tags block"
And I fill the moodle form with:
| Display on page types | Any assignment module page |
......@@ -39,6 +41,7 @@ Feature: The context of a block can always be returned to it's original state.
| Description | Description |
And I follow "Assignment2"
And I should see "Tags" in the "Tags" "block"
And I click on "Actions" "link" in the "Tags" "block"
And I follow "Configure Tags block"
And I fill the moodle form with:
| Display on page types | Any page |
......
......@@ -25,7 +25,7 @@ Feature: Allow students to manually mark an activity as complete
And I log in as "teacher1"
And I follow "Course 1"
And I turn editing mode on
And I follow "Edit settings"
And I click on "Edit settings" "link" in the "Administration" "block"
And I fill the moodle form with:
| Enable completion tracking | Yes |
And I press "Save changes"
......
......@@ -27,7 +27,7 @@ Feature: Restrict sections availability through completion or grade conditions
Given I log in as "teacher1"
And I follow "Course 1"
And I turn editing mode on
And I follow "Edit settings"
And I click on "Edit settings" "link" in the "Administration" "block"
And I fill the moodle form with:
| Enable completion tracking | Yes |
And I press "Save changes"
......@@ -93,4 +93,4 @@ Feature: Restrict sections availability through completion or grade conditions
And I log in as "student1"
And I follow "Course 1"
And "Test page name" activity should be visible
And I should not see "Not available until you achieve a required score in Grade assignment"
\ No newline at end of file
And I should not see "Not available until you achieve a required score in Grade assignment"
......@@ -611,8 +611,7 @@ class core_course_management_renderer extends plugin_renderer_base {
new action_menu_link_secondary($shortnameurl, null, get_string('resortbyshortname')),
new action_menu_link_secondary($idnumberurl, null, get_string('resortbyidnumber'))
));
$menu->actiontext = get_string('resortcourses');
$menu->actionicon = new pix_icon('t/contextmenu', ' ', 'moodle', array('class' => 'iconsmall', 'title' => ''));
$menu->set_menu_trigger(get_string('resortcourses'));
$actions[] = $this->render($menu);
}
$strall = get_string('all');
......@@ -628,7 +627,7 @@ class core_course_management_renderer extends plugin_renderer_base {
$perpage = $strall;
}
$menu->attributes['class'] .= ' courses-per-page';
$menu->actiontext = get_string('perpagea', 'moodle', $perpage);
$menu->set_menu_trigger(get_string('perpagea', 'moodle', $perpage));
$actions[] = $this->render($menu);
return html_writer::div(join(' | ', $actions), 'listing-actions course-listing-actions');
}
......@@ -922,7 +921,7 @@ class core_course_management_renderer extends plugin_renderer_base {
$menu->add(new action_menu_link_secondary($modeurl, null, $modestr, $attributes));
}
$menu->actiontext = get_string('viewing', 'moodle', $selected);
$menu->set_menu_trigger(get_string('viewing', 'moodle', $selected));
$html = html_writer::start_div('view-mode-selector vms');
$html .= $this->render($menu);
......
......@@ -1949,14 +1949,11 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
if (!isset($str)) {
$str = get_strings(array('delete', 'move', 'moveright', 'moveleft',
'update', 'duplicate', 'hide', 'show', 'edittitle'), 'moodle');
'editsettings', 'duplicate', 'hide', 'show'), 'moodle');
$str->assign = get_string('assignroles', 'role');
$str->groupsnone = get_string('clicktochangeinbrackets', 'moodle', get_string("groupsnone"));
$str->groupsseparate = get_string('clicktochangeinbrackets', 'moodle', get_string("groupsseparate"));
$str->groupsvisible = get_string('clicktochangeinbrackets', 'moodle', get_string("groupsvisible"));
$str->forcedgroupsnone = get_string('forcedmodeinbrackets', 'moodle', get_string("groupsnone"));
$str->forcedgroupsseparate = get_string('forcedmodeinbrackets', 'moodle', get_string("groupsseparate"));
$str->forcedgroupsvisible = get_string('forcedmodeinbrackets', 'moodle', get_string("groupsvisible"));
}
$baseurl = new moodle_url('/course/mod.php', array('sesskey' => sesskey()));
......@@ -1966,21 +1963,21 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
}
$actions = array();
// AJAX edit title.
if ($mod->has_view() && $hasmanageactivities &&
(($mod->course == $COURSE->id && course_ajax_enabled($COURSE)) ||
($mod->course == SITEID && course_ajax_enabled($SITE)))) {
// we will not display link if we are on some other-course page (where we should not see this module anyway)
$actions['title'] = new action_menu_link_secondary(
// Update.
if ($hasmanageactivities) {
$actions['update'] = new action_menu_link_secondary(
new moodle_url($baseurl, array('update' => $mod->id)),
new pix_icon('t/editstring', $str->edittitle, 'moodle', array('class' => 'iconsmall visibleifjs', 'title' => '')),
$str->edittitle,
array('class' => 'editing_title', 'data-action' => 'edittitle')
new pix_icon('t/edit', $str->editsettings, 'moodle', array('class' => 'iconsmall', 'title' => '')),
$str->editsettings,
array('class' => 'editing_update', 'data-action' => 'update')
);
}
// Indent.
if ($hasmanageactivities) {
$indentlimits = new stdClass();
$indentlimits->min = 0;
$indentlimits->max = 16;
if (right_to_left()) { // Exchange arrows on RTL
$rightarrow = 't/left';
$leftarrow = 't/right';
......@@ -1989,81 +1986,43 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
$leftarrow = 't/left';
}
$hiddenclass = 'hidden';
if ($indent > 0) {
$hiddenclass = '';
}
$actions['moveleft'] = new action_menu_link_secondary(
new moodle_url($baseurl, array('id' => $mod->id, 'indent' => '-1')),
new pix_icon($leftarrow, $str->moveleft, 'moodle', array('class' => 'iconsmall', 'title' => '')),
$str->moveleft,
array('class' => 'editing_moveleft ' . $hiddenclass, 'data-action' => 'moveleft')
);
$hiddenclass = 'hidden';
if ($indent >= 0) {
$hiddenclass = '';
if ($indent >= $indentlimits->max) {
$enabledclass = 'hidden';
} else {
$enabledclass = '';
}
$actions['moveright'] = new action_menu_link_secondary(
new moodle_url($baseurl, array('id' => $mod->id, 'indent' => '1')),
new pix_icon($rightarrow, $str->moveright, 'moodle', array('class' => 'iconsmall', 'title' => '')),
$str->moveright,
array('class' => 'editing_moveright ' . $hiddenclass, 'data-action' => 'moveright')
);
}
// Move.
if ($hasmanageactivities) {
$actions['move'] = new action_menu_link_primary(
new moodle_url($baseurl, array('copy' => $mod->id)),
new pix_icon('t/move', $str->move, 'moodle', array('class' => 'iconsmall', 'title' => '')),
$str->move,
array('class' => 'editing_move status', 'data-action' => 'move')
array('class' => 'editing_moveright ' . $enabledclass, 'data-action' => 'moveright')
);
}
// Update.
if ($hasmanageactivities) {
$actions['update'] = new action_menu_link_secondary(
new moodle_url($baseurl, array('update' => $mod->id)),
new pix_icon('t/edit', $str->update, 'moodle', array('class' => 'iconsmall', 'title' => '')),
$str->update,
array('class' => 'editing_update', 'data-action' => 'update')
);
}
// Duplicate (require both target import caps to be able to duplicate and backup2 support, see modduplicate.php)
// Note that restoring on front page is never allowed.
if ($mod->course != SITEID && has_all_capabilities($dupecaps, $coursecontext) &&
plugin_supports('mod', $mod->modname, FEATURE_BACKUP_MOODLE2)) {
$actions['duplicate'] = new action_menu_link_secondary(
new moodle_url($baseurl, array('duplicate' => $mod->id)),
new pix_icon('t/copy', $str->duplicate, 'moodle', array('class' => 'iconsmall', 'title' => '')),
$str->duplicate,
array('class' => 'editing_duplicate', 'data-action' => 'duplicate')
if ($indent <= $indentlimits->min) {
$enabledclass = 'hidden';
} else {
$enabledclass = '';
}
$actions['moveleft'] = new action_menu_link_secondary(
new moodle_url($baseurl, array('id' => $mod->id, 'indent' => '-1')),
new pix_icon($leftarrow, $str->moveleft, 'moodle', array('class' => 'iconsmall', 'title' => '')),
$str->moveleft,
array('class' => 'editing_moveleft ' . $enabledclass, 'data-action' => 'moveleft')
);
}
// Delete.
if ($hasmanageactivities) {
$actions['delete'] = new action_menu_link_secondary(
new moodle_url($baseurl, array('delete' => $mod->id)),
new pix_icon('t/delete', $str->delete, 'moodle', array('class' => 'iconsmall', 'title' => '')),
$str->delete,
array('class' => 'editing_delete', 'data-action' => 'delete')
);
}
// Hide/Show.
if (has_capability('moodle/course:activityvisibility', $modcontext)) {
if ($mod->visible) {
$actions['hide'] = new action_menu_link_primary(
$actions['hide'] = new action_menu_link_secondary(
new moodle_url($baseurl, array('hide' => $mod->id)),
new pix_icon('t/hide', $str->hide, 'moodle', array('class' => 'iconsmall', 'title' => '')),
$str->hide,
array('class' => 'editing_hide', 'data-action' => 'hide')
);
} else {
$actions['show'] = new action_menu_link_primary(
$actions['show'] = new action_menu_link_secondary(
new moodle_url($baseurl, array('show' => $mod->id)),
new pix_icon('t/show', $str->show, 'moodle', array('class' => 'iconsmall', 'title' => '')),
$str->show,
......@@ -2072,28 +2031,38 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
}
}
// Duplicate (require both target import caps to be able to duplicate and backup2 support, see modduplicate.php)
// Note that restoring on front page is never allowed.
if ($mod->course != SITEID && has_all_capabilities($dupecaps, $coursecontext) &&
plugin_supports('mod', $mod->modname, FEATURE_BACKUP_MOODLE2)) {
$actions['duplicate'] = new action_menu_link_secondary(
new moodle_url($baseurl, array('duplicate' => $mod->id)),
new pix_icon('t/copy', $str->duplicate, 'moodle', array('class' => 'iconsmall', 'title' => '')),
$str->duplicate,
array('class' => 'editing_duplicate', 'data-action' => 'duplicate', 'data-sr' => $sr)
);
}
// Groupmode.
if ($hasmanageactivities and plugin_supports('mod', $mod->modname, FEATURE_GROUPS, 0)) {
if ($mod->effectivegroupmode == SEPARATEGROUPS) {
$nextgroupmode = VISIBLEGROUPS;
$grouptitle = $str->groupsseparate;
$forcedgrouptitle = $str->forcedgroupsseparate;
$actionname = 'groupsseparate';
$groupimage = 't/groups';
} else if ($mod->effectivegroupmode == VISIBLEGROUPS) {
$nextgroupmode = NOGROUPS;
$grouptitle = $str->groupsvisible;
$forcedgrouptitle = $str->forcedgroupsvisible;
$actionname = 'groupsvisible';
$groupimage = 't/groupv';
} else {
$nextgroupmode = SEPARATEGROUPS;
$grouptitle = $str->groupsnone;
$forcedgrouptitle = $str->forcedgroupsnone;
$actionname = 'groupsnone';
$groupimage = 't/groupn';
}
if (!$mod->coursegroupmodeforce) {
if ($hasmanageactivities && !$mod->coursegroupmodeforce) {
if (plugin_supports('mod', $mod->modname, FEATURE_GROUPS, 0)) {
if ($mod->effectivegroupmode == SEPARATEGROUPS) {
$nextgroupmode = VISIBLEGROUPS;
$grouptitle = $str->groupsseparate;
$actionname = 'groupsseparate';
$groupimage = 'i/groups';
} else if ($mod->effectivegroupmode == VISIBLEGROUPS) {
$nextgroupmode = NOGROUPS;
$grouptitle = $str->groupsvisible;
$actionname = 'groupsvisible';
$groupimage = 'i/groupv';
} else {
$nextgroupmode = SEPARATEGROUPS;
$grouptitle = $str->groupsnone;
$actionname = 'groupsnone';
$groupimage = 'i/groupn';
}
$actions[$actionname] = new action_menu_link_primary(
new moodle_url($baseurl, array('id' => $mod->id, 'groupmode' => $nextgroupmode)),
new pix_icon($groupimage, $grouptitle, 'moodle', array('class' => 'iconsmall', 'title' => '')),
......@@ -2101,7 +2070,7 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
array('class' => 'editing_'. $actionname, 'data-action' => $actionname, 'data-nextgroupmode' => $nextgroupmode)
);
} else {
$actions[$actionname] = new pix_icon($groupimage, $forcedgrouptitle, 'moodle', array('class' => 'iconsmall'));
$actions['nogroupsupport'] = new action_menu_filler();
}
}
......@@ -2115,9 +2084,111 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
);
}
// Delete.
if ($hasmanageactivities) {
$actions['delete'] = new action_menu_link_secondary(
new moodle_url($baseurl, array('delete' => $mod->id)),
new pix_icon('t/delete', $str->delete, 'moodle', array('class' => 'iconsmall', 'title' => '')),
$str->delete,
array('class' => 'editing_delete', 'data-action' => 'delete')
);
}
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.
*
* @param cm_info $mod The module to produce a move button for
* @param int $sr The section to link back to (used for creating the links)
* @return The markup for the move action, or an empty string if not available.
*/
function course_get_cm_move(cm_info $mod, $sr = null) {
global $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('move'));
}
if (!isset($baseurl)) {
$baseurl = new moodle_url('/course/mod.php', array('sesskey' => sesskey()));
if ($sr !== null) {
$baseurl->param('sr', $sr);
}
}
if ($hasmanageactivities) {
$pixicon = 'i/dragdrop';
if ($mod->course == SITEID) {
// Override for course frontpage until we get drag/drop working there.
$pixicon = 't/move';
}
return html_writer::link(
new moodle_url($baseurl, array('copy' => $mod->id)),
$OUTPUT->pix_icon($pixicon, $str->move, 'moodle', array('class' => 'iconsmall', 'title' => '')),
array('class' => 'editing_move', 'data-action' => 'move')
);
}
return '';
}
/**
* given a course object with shortname & fullname, this function will
* truncate the the number of chars allowed and add ... if it was too long
......@@ -3309,6 +3380,111 @@ function update_module($moduleinfo) {
return $moduleinfo;
}
/**
* Duplicate a module on the course.
*
* @param object $course The course
* @param object $cm The course module to duplicate
* @throws moodle_exception if the plugin doesn't support duplication
* @return Object containing:
* - fullcontent: The HTML markup for the created CM
* - cmid: The CMID of the newly created CM
* - redirect: Whether to trigger a redirect following this change
*/
function mod_duplicate_activity($course, $cm, $sr = null) {
global $CFG, $USER, $PAGE, $DB;
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
require_once($CFG->libdir . '/filelib.php');
$a = new stdClass();
$a->modtype = get_string('modulename', $cm->modname);
$a->modname = format_string($cm->name);
if (!plugin_supports('mod', $cm->modname, FEATURE_BACKUP_MOODLE2)) {
throw new moodle_exception('duplicatenosupport', 'error');
}
// backup the activity
$bc = new backup_controller(backup::TYPE_1ACTIVITY, $cm->id, backup::FORMAT_MOODLE,
backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id);
$backupid = $bc->get_backupid();
$backupbasepath = $bc->get_plan()->get_basepath();
$bc->execute_plan();
$bc->destroy();
// restore the backup immediately
$rc = new restore_controller($backupid, $course->id,
backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, backup::TARGET_CURRENT_ADDING);
$cmcontext = context_module::instance($cm->id);
if (!$rc->execute_precheck()) {
$precheckresults = $rc->get_precheck_results();
if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
if (empty($CFG->keeptempdirectoriesonbackup)) {
fulldelete($backupbasepath);
}
}
}
$rc->execute_plan();
// now a bit hacky part follows - we try to get the cmid of the newly
// restored copy of the module
$newcmid = null;
$tasks = $rc->get_plan()->get_tasks();
foreach ($tasks as $task) {
error_log("Looking at a task");
if (is_subclass_of($task, 'restore_activity_task')) {
error_log("Looking at a restore_activity_task task");
if ($task->get_old_contextid() == $cmcontext->id) {
error_log("Contexts match");
$newcmid = $task->get_moduleid();
break;
}
}
}
// if we know the cmid of the new course module, let us move it
// right below the original one. otherwise it will stay at the
// end of the section
if ($newcmid) {
$info = get_fast_modinfo($course);
$newcm = $info->get_cm($newcmid);
$section = $DB->get_record('course_sections', array('id' => $cm->section, 'course' => $cm->course));
moveto_module($newcm, $section, $cm);
moveto_module($cm, $section, $newcm);
}
rebuild_course_cache($cm->course);
$rc->destroy();
if (empty($CFG->keeptempdirectoriesonbackup)) {
fulldelete($backupbasepath);
}
$resp = new stdClass();
if ($newcm) {
$courserenderer = $PAGE->get_renderer('core', 'course');
$completioninfo = new completion_info($course);
$modulehtml = $courserenderer->course_section_cm($course, $completioninfo,
$newcm, null, array());
$resp->fullcontent = $courserenderer->course_section_cm_list_item($course, $completioninfo, $newcm, $sr);
$resp->cmid = $newcm->id;
} else {
// Trigger a redirect
$resp->redirect = true;
}
return $resp;
}
/**
* Compare two objects to find out their correct order based on timestamp (to be used by usort).
* Sorts by descending order of time.
......
......@@ -364,9 +364,37 @@ class core_course_renderer extends plugin_renderer_base {
$menu = new