MDL-39814 frontend: improvements to course/block editing

This commit is contained in:
Sam Hemelryk
2013-07-30 16:07:26 +12:00
parent 56c3e4762f
commit e282c679e3
15 changed files with 772 additions and 94 deletions
+48 -30
View File
@@ -1806,9 +1806,10 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
// 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_link(
new moodle_url($baseurl, array('update' => $mod->id)),
new pix_icon('t/editstring', $str->edittitle, 'moodle', array('class' => 'iconsmall visibleifjs', 'title' => '')),
$str->edittitle,
null,
array('class' => 'editing_title', 'title' => $str->edittitle)
array('class' => 'editing_title', 'title' => $str->edittitle),
new pix_icon('t/editstring', $str->edittitle, 'moodle', array('class' => 'iconsmall visibleifjs', 'title' => ''))
);
}
@@ -1822,31 +1823,38 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
$leftarrow = 't/left';
}
$hiddenclass = 'hidden';
if ($indent > 0) {
$actions['moveleft'] = new action_link(
new moodle_url($baseurl, array('id' => $mod->id, 'indent' => '-1')),
new pix_icon($leftarrow, $str->moveleft, 'moodle', array('class' => 'iconsmall', 'title' => '')),
null,
array('class' => 'editing_moveleft', 'title' => $str->moveleft)
);
$hiddenclass = '';
}
$actions['moveleft'] = new action_link(
new moodle_url($baseurl, array('id' => $mod->id, 'indent' => '-1')),
$str->moveleft,
null,
array('class' => 'editing_moveleft ' . $hiddenclass, 'title' => $str->moveleft),
new pix_icon($leftarrow, $str->moveleft, 'moodle', array('class' => 'iconsmall', 'title' => ''))
);
$hiddenclass = 'hidden';
if ($indent >= 0) {
$actions['moveright'] = new action_link(
new moodle_url($baseurl, array('id' => $mod->id, 'indent' => '1')),
new pix_icon($rightarrow, $str->moveright, 'moodle', array('class' => 'iconsmall', 'title' => '')),
null,
array('class' => 'editing_moveright', 'title' => $str->moveright)
);
$hiddenclass = '';
}
$actions['moveright'] = new action_link(
new moodle_url($baseurl, array('id' => $mod->id, 'indent' => '1')),
$str->moveright,
null,
array('class' => 'editing_moveright ' . $hiddenclass, 'title' => $str->moveright),
new pix_icon($rightarrow, $str->moveright, 'moodle', array('class' => 'iconsmall', 'title' => ''))
);
}
// move
if ($hasmanageactivities) {
$actions['move'] = new action_link(
new moodle_url($baseurl, array('copy' => $mod->id)),
new pix_icon('t/move', $str->move, 'moodle', array('class' => 'iconsmall', 'title' => '')),
$str->move,
null,
array('class' => 'editing_move', 'title' => $str->move)
array('class' => 'editing_move status', 'title' => $str->move),
new pix_icon('t/move', $str->move, 'moodle', array('class' => 'iconsmall', 'title' => ''))
);
}
@@ -1854,9 +1862,10 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
if ($hasmanageactivities) {
$actions['update'] = new action_link(
new moodle_url($baseurl, array('update' => $mod->id)),
new pix_icon('t/edit', $str->update, 'moodle', array('class' => 'iconsmall', 'title' => '')),
$str->update,
null,
array('class' => 'editing_update', 'title' => $str->update)
array('class' => 'editing_update', 'title' => $str->update),
new pix_icon('t/edit', $str->update, 'moodle', array('class' => 'iconsmall', 'title' => ''))
);
}
@@ -1866,9 +1875,10 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
plugin_supports('mod', $mod->modname, FEATURE_BACKUP_MOODLE2)) {
$actions['duplicate'] = new action_link(
new moodle_url($baseurl, array('duplicate' => $mod->id)),
new pix_icon('t/copy', $str->duplicate, 'moodle', array('class' => 'iconsmall', 'title' => '')),
$str->duplicate,
null,
array('class' => 'editing_duplicate', 'title' => $str->duplicate)
array('class' => 'editing_duplicate', 'title' => $str->duplicate),
new pix_icon('t/copy', $str->duplicate, 'moodle', array('class' => 'iconsmall', 'title' => ''))
);
}
@@ -1876,9 +1886,10 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
if ($hasmanageactivities) {
$actions['delete'] = new action_link(
new moodle_url($baseurl, array('delete' => $mod->id)),
new pix_icon('t/delete', $str->delete, 'moodle', array('class' => 'iconsmall', 'title' => '')),
$str->delete,
null,
array('class' => 'editing_delete', 'title' => $str->delete)
array('class' => 'editing_delete', 'title' => $str->delete),
new pix_icon('t/delete', $str->delete, 'moodle', array('class' => 'iconsmall', 'title' => ''))
);
}
@@ -1887,17 +1898,21 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
if ($mod->visible) {
$actions['hide'] = new action_link(
new moodle_url($baseurl, array('hide' => $mod->id)),
new pix_icon('t/hide', $str->hide, 'moodle', array('class' => 'iconsmall', 'title' => '')),
$str->hide,
null,
array('class' => 'editing_hide', 'title' => $str->hide)
array('class' => 'editing_hide', 'title' => $str->hide),
new pix_icon('t/hide', $str->hide, 'moodle', array('class' => 'iconsmall', 'title' => ''))
);
$actions['hide']->add_class('status');
} else {
$actions['show'] = new action_link(
new moodle_url($baseurl, array('show' => $mod->id)),
new pix_icon('t/show', $str->show, 'moodle', array('class' => 'iconsmall', 'title' => '')),
$str->show,
null,
array('class' => 'editing_show', 'title' => $str->show)
array('class' => 'editing_show', 'title' => $str->show),
new pix_icon('t/show', $str->show, 'moodle', array('class' => 'iconsmall', 'title' => ''))
);
$actions['show']->add_class('status');
}
}
@@ -1930,22 +1945,25 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
if (!$mod->coursegroupmodeforce) {
$actions[$actionname] = new action_link(
new moodle_url($baseurl, array('id' => $mod->id, 'groupmode' => $groupmode)),
new pix_icon($groupimage, $grouptitle, 'moodle', array('class' => 'iconsmall', 'title' => '')),
$grouptitle,
null,
array('class' => 'editing_'. $actionname, 'title' => $grouptitle)
array('class' => 'editing_'. $actionname, 'title' => $grouptitle),
new pix_icon($groupimage, $grouptitle, 'moodle', array('class' => 'iconsmall', 'title' => ''))
);
} else {
$actions[$actionname] = new pix_icon($groupimage, $forcedgrouptitle, 'moodle', array('title' => $forcedgrouptitle, 'class' => 'iconsmall'));
}
$actions[$actionname]->add_class('status');
}
// Assign
if (has_capability('moodle/role:assign', $modcontext)){
$actions['assign'] = new action_link(
new moodle_url('/admin/roles/assign.php', array('contextid' => $modcontext->id)),
new pix_icon('t/assignroles', $str->assign, 'moodle', array('class' => 'iconsmall', 'title' => '')),
$str->assign,
null,
array('class' => 'editing_assign', 'title' => $str->assign)
array('class' => 'editing_assign', 'title' => $str->assign),
new pix_icon('t/assignroles', $str->assign, 'moodle', array('class' => 'iconsmall', 'title' => ''))
);
}
+25 -11
View File
@@ -325,19 +325,27 @@ class core_course_renderer extends plugin_renderer_base {
*
* @see course_get_cm_edit_actions()
*
* @param array $actions array of action_link or pix_icon objects
* @param array $actions array of action_link objects
* @return string
*/
public function course_section_cm_edit_actions($actions) {
$output = html_writer::start_tag('span', array('class' => 'commands'));
$this->page->requires->yui_module('moodle-core-actionmenu', 'M.core.actionmenu.init');
$output = html_writer::start_tag('span', array('class' => 'actionmenu'));
$output .= html_writer::start_tag('span', array('class' => 'statuses'));
foreach ($actions as $action) {
if ($action instanceof renderable) {
if ($action->has_class('status')) {
$output .= $this->output->render($action);
} else {
$output .= $action;
}
}
$output .= html_writer::end_tag('span');
$output .= html_writer::start_tag('span', array('class' => 'actions'));
foreach ($actions as $action) {
if (!$action->has_class('status')) {
$output .= $this->output->render($action);
}
}
$output .= html_writer::end_tag('span');
$output .= html_writer::end_tag('span');
return $output;
}
@@ -860,7 +868,7 @@ class core_course_renderer extends plugin_renderer_base {
return $output;
}
$indentclasses = 'mod-indent';
$indentclasses = 'mod-indent clearfix';
if (!empty($mod->indent)) {
$indentclasses .= ' mod-indent-'.$mod->indent;
if ($mod->indent > 15) {
@@ -893,14 +901,11 @@ class core_course_renderer extends plugin_renderer_base {
$output .= $contentpart;
}
if ($this->page->user_is_editing()) {
$editactions = course_get_cm_edit_actions($mod, $mod->indent, $sectionreturn);
$output .= ' '. $this->course_section_cm_edit_actions($editactions);
$output .= $mod->get_after_edit_icons();
}
// Display completion first - so it is the right most floated element (and will align vertically).
$output .= $this->course_section_cm_completion($course, $completioninfo, $mod, $displayoptions);
// If there is content AND a link, then display the content here
// (AFTER any icons). Otherwise it was displayed before
if (!empty($url)) {
@@ -910,6 +915,15 @@ class core_course_renderer extends plugin_renderer_base {
// show availability info (if module is not available)
$output .= $this->course_section_cm_availability($mod, $displayoptions);
// Now action icons (if editing).
if ($this->page->user_is_editing()) {
$editactions = course_get_cm_edit_actions($mod, $mod->indent, $sectionreturn);
$output .= ' '. $this->course_section_cm_edit_actions($editactions);
$output .= $mod->get_after_edit_icons();
}
// Add a clear div so the container includes all floated elements.
$output .= html_writer::end_tag('div'); // $indentclasses
return $output;
}
+3 -2
View File
@@ -2,7 +2,8 @@ YUI.add('moodle-course-dragdrop', function(Y) {
var CSS = {
ACTIVITY : 'activity',
COMMANDSPAN : 'span.commands',
COMMANDSPAN : 'span.actionmenu',
SPINNERCOMMANDSPAN : 'span.instancename',
CONTENT : 'content',
COURSECONTENT : 'course-content',
EDITINGMOVE : 'editing_move',
@@ -351,7 +352,7 @@ YUI.add('moodle-course-dragdrop', function(Y) {
var dropnode = e.drop.get('node');
// Add spinner if it not there
var spinner = M.util.add_spinner(Y, dragnode.one(CSS.COMMANDSPAN));
var spinner = M.util.add_spinner(Y, dragnode.one(CSS.SPINNERCOMMANDSPAN));
var params = {};
+59 -14
View File
@@ -3,8 +3,9 @@ YUI.add('moodle-course-toolboxes', function(Y) {
// The CSS selectors we use
var CSS = {
ACTIVITYLI : 'li.activity',
COMMANDSPAN : 'span.commands',
SPINNERCOMMANDSPAN : 'span.commands',
COMMANDSPAN : 'span.actionmenu',
ACTIONPREFIX : 'span.actions',
SPINNERCOMMANDSPAN : 'span.instancename',
CONTENTAFTERLINK : 'div.contentafterlink',
DELETE : 'a.editing_delete',
DIMCLASS : 'dimmed',
@@ -26,7 +27,7 @@ YUI.add('moodle-course-toolboxes', function(Y) {
MOVELEFT : 'a.editing_moveleft',
MOVELEFTCLASS : 'editing_moveleft',
MOVERIGHT : 'a.editing_moveright',
PAGECONTENT : 'div#page-content',
PAGECONTENT : 'body',
RIGHTSIDE : '.right',
SECTIONHIDDENCLASS : 'hidden',
SECTIONIDPREFIX : 'section-',
@@ -56,7 +57,7 @@ YUI.add('moodle-course-toolboxes', function(Y) {
* resource show/hide button
*/
toggle_hide_resource_ui : function(button) {
var element = button.ancestor(CSS.ACTIVITYLI);
var element = this.get_activity_container(button);
var hideicon = button.one('img');
var dimarea;
@@ -102,6 +103,47 @@ YUI.add('moodle-course-toolboxes', function(Y) {
}
return value;
},
get_activity_action_link : function(node, selector) {
// The node is either a direct descendant of the activity container,
// or it is linked through the actionmenu.
Y.log('find activity container');
var element = node.one(selector);
if (element) {
Y.log('direct ancestor ' + element);
return element;
}
var actionmenulink = node.one('a.actionmenu');
Y.log('get action menu link ' + actionmenulink);
var overlay = actionmenulink.getData('actionmenu-menu');
element = overlay.get('boundingBox').one(selector);
Y.log('get ancestor ' + element);
return element;
},
get_activity_container : function(node) {
// The node is either a direct descendant of the activity container,
// or it is linked through the actionmenu.
Y.log('find activity container');
var element = node.ancestor(CSS.ACTIVITYLI);
if (element) {
Y.log('direct ancestor ' + element);
return element;
}
var actionlink = node.ancestor('a');
Y.log('get action link ' + actionlink);
var actionmenulink = actionlink.getData('actionmenu-link');
Y.log('get menu link ' + actionmenulink);
element = actionmenulink.ancestor(CSS.ACTIVITYLI);
Y.log('get ancestor ' + element);
return element;
},
/**
* Send a request using the REST API
*
@@ -242,9 +284,9 @@ YUI.add('moodle-course-toolboxes', function(Y) {
M.course.coursebase.register_module(this);
var prefix = CSS.ACTIVITYLI + ' ' + CSS.COMMANDSPAN + ' ';
Y.delegate('click', this.edit_resource_title, CSS.PAGECONTENT, prefix + CSS.EDITTITLE, this);
Y.delegate('click', this.move_left, CSS.PAGECONTENT, prefix + CSS.MOVELEFT, this);
Y.delegate('click', this.move_right, CSS.PAGECONTENT, prefix + CSS.MOVERIGHT, this);
Y.delegate('click', this.edit_resource_title, CSS.PAGECONTENT, CSS.EDITTITLE, this);
Y.delegate('click', this.move_left, CSS.PAGECONTENT, CSS.MOVELEFT, this);
Y.delegate('click', this.move_right, CSS.PAGECONTENT, CSS.MOVERIGHT, this);
Y.delegate('click', this.delete_resource, CSS.PAGECONTENT, prefix + CSS.DELETE, this);
Y.delegate('click', this.toggle_hide_resource, CSS.PAGECONTENT, prefix + CSS.HIDE, this);
Y.delegate('click', this.toggle_hide_resource, CSS.PAGECONTENT, prefix + CSS.SHOW, this);
@@ -292,7 +334,7 @@ YUI.add('moodle-course-toolboxes', function(Y) {
e.preventDefault();
// Get the element we're working on
var element = e.target.ancestor(CSS.ACTIVITYLI);
var element = this.get_activity_container(e.target);
// And we need to determine the current and new indent level
var indentdiv = element.one(CSS.MODINDENTDIV);
@@ -319,10 +361,11 @@ YUI.add('moodle-course-toolboxes', function(Y) {
this.send_request(data, spinner);
// Handle removal/addition of the moveleft button
Y.log(element);
if (newindent == 0) {
element.one(CSS.MOVELEFT).remove();
this.get_activity_action_link(element, CSS.MOVELEFT).addClass('hidden');
} else if (newindent == 1 && oldindent == 0) {
this.add_moveleft(element);
this.get_activity_action_link(element, CSS.MOVELEFT).removeClass('hidden');
}
// Handle massive indentation to match non-ajax display
@@ -338,7 +381,8 @@ YUI.add('moodle-course-toolboxes', function(Y) {
e.preventDefault();
// Get the element we're working on
var element = e.target.ancestor(CSS.ACTIVITYLI);
var element = this.get_activity_container(e.target);
// Create confirm string (different if element has or does not have name)
var confirmstring = '';
@@ -377,7 +421,7 @@ YUI.add('moodle-course-toolboxes', function(Y) {
}
// Get the element we're working on
var element = e.target.ancestor(CSS.ACTIVITYLI);
var element = this.get_activity_container(e.target);
var button = e.target.ancestor('a', true);
@@ -399,7 +443,7 @@ YUI.add('moodle-course-toolboxes', function(Y) {
e.preventDefault();
// Get the element we're working on
var element = e.target.ancestor(CSS.ACTIVITYLI);
var element = this.get_activity_container(e.target);
var button = e.target.ancestor('a', true);
var icon = button.one('img');
@@ -456,6 +500,7 @@ YUI.add('moodle-course-toolboxes', function(Y) {
* @param target The encapsulating <li> element
*/
add_moveleft : function(target) {
Y.log(target);
var left_string = M.util.get_string('moveleft', 'moodle');
var moveimage = 't/left'; // ltr mode
if ( Y.one(document.body).hasClass('dir-rtl') ) {
@@ -484,7 +529,7 @@ YUI.add('moodle-course-toolboxes', function(Y) {
*/
edit_resource_title : function(e) {
// Get the element we're working on
var element = e.target.ancestor(CSS.ACTIVITYLI);
var element = this.get_activity_container(e.target);
var elementdiv = element.one('div');
var instancename = element.one(CSS.INSTANCENAME);
var currenttitle = instancename.get('firstChild');
+65 -20
View File
@@ -1037,35 +1037,72 @@ class block_manager {
if ($this->page->user_can_edit_blocks()) {
// Move icon.
$controls[] = array('url' => $actionurl . '&bui_moveid=' . $block->instance->id,
'icon' => 't/move', 'caption' => get_string('moveblock', 'block', $blocktitle),
'class' => 'editing_move');
//$controls[] = array('url' => $actionurl . '&bui_moveid=' . $block->instance->id,
// 'icon' => 't/move', 'caption' => get_string('moveblock', 'block', $blocktitle),
// 'class' => 'editing_move status');
$controls[] = new action_link(
new moodle_url($actionurl, array('bui_moveid' => $block->instance->id)),
get_string('moveblock', 'block', $blocktitle),
null,
array('class' => 'editing_move status', 'title' => get_string('moveblock', 'block', $blocktitle)),
new pix_icon('t/move', get_string('moveblock', 'block', $blocktitle), 'moodle', array('class' => 'iconsmall', 'title' => ''))
);
}
if ($this->page->user_can_edit_blocks() || $block->user_can_edit()) {
// Edit config icon - always show - needed for positioning UI.
$controls[] = array('url' => $actionurl . '&bui_editid=' . $block->instance->id,
'icon' => 't/edit', 'caption' => get_string('configureblock', 'block', $blocktitle),
'class' => 'editing_edit');
//$controls[] = array('url' => $actionurl . '&bui_editid=' . $block->instance->id,
// 'icon' => 't/edit', 'caption' => get_string('configureblock', 'block', $blocktitle),
// 'class' => 'editing_edit');
$controls[] = new action_link(
new moodle_url($actionurl, array('bui_editid' => $block->instance->id)),
get_string('configureblock', 'block', $blocktitle),
null,
array('class' => 'editing_edit', 'title' => get_string('configureblock', 'block', $blocktitle)),
new pix_icon('t/edit', get_string('configureblock', 'block', $blocktitle), 'moodle', array('class' => 'iconsmall', 'title' => ''))
);
}
if ($this->user_can_delete_block($block)) {
// Delete icon.
$controls[] = array('url' => $actionurl . '&bui_deleteid=' . $block->instance->id,
'icon' => 't/delete', 'caption' => get_string('deleteblock', 'block', $blocktitle),
'class' => 'editing_delete');
//$controls[] = array('url' => $actionurl . '&bui_deleteid=' . $block->instance->id,
//// 'icon' => 't/delete', 'caption' => get_string('deleteblock', 'block', $blocktitle),
// 'class' => 'editing_delete');
$controls[] = new action_link(
new moodle_url($actionurl, array('bui_deleteid' => $block->instance->id)),
get_string('deleteblock', 'block', $blocktitle),
null,
array('class' => 'editing_delete', 'title' => get_string('deleteblock', 'block', $blocktitle)),
new pix_icon('t/delete', get_string('deleteblock', 'block', $blocktitle), 'moodle', array('class' => 'iconsmall', 'title' => ''))
);
}
if ($this->page->user_can_edit_blocks() && $block->instance_can_be_hidden()) {
// Show/hide icon.
if ($block->instance->visible) {
$controls[] = array('url' => $actionurl . '&bui_hideid=' . $block->instance->id,
'icon' => 't/hide', 'caption' => get_string('hideblock', 'block', $blocktitle),
'class' => 'editing_hide');
$controls[] = new action_link(
new moodle_url($actionurl, array('bui_hideid' => $block->instance->id)),
get_string('deleteblock', 'block', $blocktitle),
null,
array('class' => 'editing_hide status', 'title' => get_string('hideblock', 'block', $blocktitle)),
new pix_icon('t/hide', get_string('hideblock', 'block', $blocktitle), 'moodle', array('class' => 'iconsmall', 'title' => ''))
);
//$controls[] = array('url' => $actionurl . '&bui_hideid=' . $block->instance->id,
// 'icon' => 't/hide', 'caption' => get_string('hideblock', 'block', $blocktitle),
// 'class' => 'editing_hide status');
} else {
$controls[] = array('url' => $actionurl . '&bui_showid=' . $block->instance->id,
'icon' => 't/show', 'caption' => get_string('showblock', 'block', $blocktitle),
'class' => 'editing_show');
$controls[] = new action_link(
new moodle_url($actionurl, array('bui_showid' => $block->instance->id)),
get_string('deleteblock', 'block', $blocktitle),
null,
array('class' => 'editing_show status', 'title' => get_string('showblock', 'block', $blocktitle)),
new pix_icon('t/show', get_string('showblock', 'block', $blocktitle), 'moodle', array('class' => 'iconsmall', 'title' => ''))
);
//$controls[] = array('url' => $actionurl . '&bui_showid=' . $block->instance->id,
// 'icon' => 't/show', 'caption' => get_string('showblock', 'block', $blocktitle),
// 'class' => 'editing_show status');
}
}
@@ -1077,12 +1114,20 @@ class block_manager {
$return = $this->page->url->out(false);
$return = str_replace($CFG->wwwroot . '/', '', $return);
$controls[] = array('url' => $CFG->wwwroot . '/' . $CFG->admin .
'/roles/assign.php?contextid=' . $block->context->id . '&returnurl=' . urlencode($return),
'icon' => 't/assignroles', 'caption' => get_string('assignrolesinblock', 'block', $blocktitle),
'class' => 'editing_roles');
$rolesurl = new moodle_url('/admin/roles/assign.php', array('contextid'=>$block->context->id,
'returnurl'=>$return));
$controls[] = new action_link(
$rolesurl,
get_string('assignrolesinblock', 'block', $blocktitle),
null,
array('class' => 'editing_roles', 'title' => get_string('assignrolesinblock', 'block', $blocktitle)),
new pix_icon('t/assignroles', get_string('assignrolesinblock', 'block', $blocktitle), 'moodle', array('class' => 'iconsmall', 'title' => ''))
);
//$controls[] = array('url' => $CFG->wwwroot . '/' . $CFG->admin .
// '/roles/assign.php?contextid=' . $block->context->id . '&returnurl=' . urlencode($return),
// 'icon' => 't/assignroles', 'caption' => get_string('assignrolesinblock', 'block', $blocktitle),
// 'class' => 'editing_roles');
}
return $controls;
}
+56 -1
View File
@@ -936,20 +936,31 @@ class action_link implements renderable {
*/
var $actions;
/**
* @var pix_icon Optional pix icon to render with the link
*/
var $icon;
/**
* Constructor
* @param moodle_url $url
* @param string $text HTML fragment
* @param component_action $action
* @param array $attributes associative array of html link attributes + disabled
* @param pix_icon $icon optional pix_icon to render with the link text
*/
public function __construct(moodle_url $url, $text, component_action $action = null, array $attributes = null) {
public function __construct(moodle_url $url,
$text,
component_action $action=null,
array $attributes=null,
pix_icon $icon=null) {
$this->url = clone($url);
$this->text = $text;
$this->attributes = (array)$attributes;
if ($action) {
$this->add_action($action);
}
$this->icon = $icon;
}
/**
@@ -972,6 +983,15 @@ class action_link implements renderable {
$this->attributes['class'] .= ' ' . $class;
}
}
/**
* Returns true if the specified class has been added to this link.
* @param string $class
* @return bool
*/
public function has_class($class) {
return strpos(' ' . $this->attributes['class'] . ' ', ' ' . $class . ' ') !== false;
}
}
/**
@@ -2858,6 +2878,41 @@ class custom_menu extends custom_menu_item {
}
}
/**
* A list of action links/icons relating to a specific element in the page.
*
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @package core
*/
class action_menu implements renderable {
/** @var string unique id of the tab in this tree, it is used to find selected and/or inactive tabs */
var $id;
/** @var string title under the link, by default equals to text */
var $title;
/** @var pix_icon representing the icon to open the menu */
var $icon;
/** @var array of action_link objects representing a list of menu items */
var $items;
/**
* Constructor
*
* @param string $title title for the menu. Should include the name of the item this menu relates to.
* @param string $icon pix_icon for the menu. Defaults to a menu icon.
* @param array $items a list of items to put in the menu.
*/
public function __construct($title, $icon = '', $items = array()) {
$this->id = html_writer::random_id('actionmenu');
$this->icon = $icon;
$this->title = $title;
$this->items = $items;
}
public function add(action_link $link) {
array_push($this->items, $link);
}
}
/**
* Stores one tab
*
+31 -15
View File
@@ -1031,17 +1031,25 @@ class core_renderer extends renderer_base {
* @param array $controls an array like {@link block_contents::$controls}.
* @return string HTML fragment.
*/
public function block_controls($controls) {
if (empty($controls)) {
return '';
public function block_controls($actions) {
$this->page->requires->yui_module('moodle-core-actionmenu', 'M.core.actionmenu.init');
$output = html_writer::start_tag('span', array('class' => 'actionmenu'));
$output .= html_writer::start_tag('span', array('class' => 'statuses'));
foreach ($actions as $action) {
if ($action->has_class('status')) {
$output .= $this->render($action);
}
}
$controlshtml = array();
foreach ($controls as $control) {
$controlshtml[] = html_writer::tag('a',
html_writer::empty_tag('img', array('src' => $this->pix_url($control['icon'])->out(false), 'alt' => $control['caption'])),
array('class' => 'icon ' . $control['class'],'title' => $control['caption'], 'href' => $control['url']));
$output .= html_writer::end_tag('span');
$output .= html_writer::start_tag('span', array('class' => 'actions'));
foreach ($actions as $action) {
if (!$action->has_class('status')) {
$output .= $this->render($action);
}
}
return html_writer::tag('div', implode('', $controlshtml), array('class' => 'commands'));
$output .= html_writer::end_tag('span');
$output .= html_writer::end_tag('span');
return $output;
}
/**
@@ -1284,13 +1292,14 @@ class core_renderer extends renderer_base {
* @param string $text HTML fragment
* @param component_action $action
* @param array $attributes associative array of html link attributes + disabled
* @param pix_icon optional pix icon to render with the link
* @return string HTML fragment
*/
public function action_link($url, $text, component_action $action = null, array $attributes=null) {
public function action_link($url, $text, component_action $action = null, array $attributes = null, $icon = null) {
if (!($url instanceof moodle_url)) {
$url = new moodle_url($url);
}
$link = new action_link($url, $text, $action, $attributes);
$link = new action_link($url, $text, $action, $attributes, $icon);
return $this->render($link);
}
@@ -1307,12 +1316,19 @@ class core_renderer extends renderer_base {
protected function render_action_link(action_link $link) {
global $CFG;
if ($link->text instanceof renderable) {
$text = $this->render($link->text);
} else {
$text = $link->text;
$text = '';
if ($link->icon) {
$text .= $this->render($link->icon);
}
$text .= html_writer::start_tag('span', array('class'=>'actionlinktext'));
if ($link->text instanceof renderable) {
$text .= $this->render($link->text);
} else {
$text .= $link->text;
}
$text .= html_writer::end_tag('span');
// A disabled link is rendered as formatted text
if (!empty($link->attributes['disabled'])) {
// do not use div here due to nesting restriction in xhtml strict
@@ -0,0 +1,135 @@
YUI.add('moodle-core-actionmenu', function (Y, NAME) {
/**
* Provides drop down menus for list of action links.
*
* @module moodle-core-actionmenu
*/
CSS = {
ACTIONS : '.actions'
};
RESOURCES = {
MENUICON : {
pix : 't/contextmenu',
component : 'moodle'
}
};
/**
* Action menu support.
* This converts a generic list of links into a drop down menu opened by hovering or clicking
* on a menu icon.
*
* @namespace M.core.actionmenu
* @class ActionMenu
* @constructor
* @extends Y.Base
*/
var ACTIONMENU = function() {
ACTIONMENU.superclass.constructor.apply(this, arguments);
};
Y.extend(ACTIONMENU, Y.Base, {
initializer : function() {
var rightalignpoints = [Y.WidgetPositionAlign.TR, Y.WidgetPositionAlign.BR];
var leftalignpoints = [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.BL];
Y.all(CSS.ACTIONS).each(function() {
// Prepend menu icon before the list.
var imgnode = Y.Node.create('<img/>');
var imgsrc = M.util.image_url(RESOURCES.MENUICON.pix, RESOURCES.MENUICON.component);
var linknode = Y.Node.create('<a/>');
var parentnode = this.get('parentNode');
var alignpoints = rightalignpoints;
var menux = 0;
if (!this.hasChildNodes()) {
return this;
}
linknode.setAttribute('href', '#');
linknode.addClass('actionmenu');
imgnode.setAttribute('src', imgsrc);
linknode.appendChild(imgnode);
parentnode.insertBefore(linknode, this);
menux = (parentnode.getX() + parseInt(parentnode.getComputedStyle('width'), 10));
if (menux < (this.get('winWidth') / 2)) {
alignpoints = leftalignpoints;
}
var overlay = new Y.Overlay({
bodyContent : this,
visible: false,
align: {node: imgnode, points: alignpoints},
zIndex: 10
});
overlay.render();
overlay.get('boundingBox').on('focusoutside', overlay.hide, overlay);
// Create bi-directional links between the menu and the open button.
linknode.setData('actionmenu-menu', overlay);
this.all('a').each(function () {
this.setData('actionmenu-link', linknode);
});
return this;
});
Y.one('body').delegate('click', this.toggleMenu, '.actionmenu');
Y.one('body').on('key', this.hideAllMenus, 'esc', this);
},
hideAllMenus : function() {
// Hide all actionmenus.
Y.all('.actionmenu').each(function() {
var overlay = this.getData('actionmenu-menu');
if (overlay) {
            overlay.hide();
}
});
},
toggleMenu : function(e) {
var overlay = this.getData('actionmenu-menu');
if (typeof overlay !== "undefined") {
if (overlay.get('visible')) {
            overlay.hide();
        } else {
M.core.actionmenu.instance.hideAllMenus();
            overlay.show();
        }
}
e.preventDefault();
}
}, {
NAME : 'moodle-core-actionmenu',
ATTRS : { }
});
/**
* Core namespace.
*/
M.core = M.core || {};
/**
* Actionmenu namespace.
*/
M.core.actionmenu = M.core.actionmenu || {};
/**
* Init function - will only ever create one instance of the actionmenu class.
*/
M.core.actionmenu.init = M.core.actionmenu.init || function(params) {
M.core.actionmenu.instance = M.core.actionmenu.instance || new ACTIONMENU(params);
};
}, '@VERSION@', {"requires": ["base", "node", "overlay", "event"]});
@@ -0,0 +1 @@
YUI.add("moodle-core-actionmenu",function(e,t){CSS={ACTIONS:".actions"},RESOURCES={MENUICON:{pix:"t/contextmenu",component:"moodle"}};var n=function(){n.superclass.constructor.apply(this,arguments)};e.extend(n,e.Base,{initializer:function(){var t=[e.WidgetPositionAlign.TR,e.WidgetPositionAlign.BR],n=[e.WidgetPositionAlign.TL,e.WidgetPositionAlign.BL];e.all(CSS.ACTIONS).each(function(){var r=e.Node.create("<img/>"),i=M.util.image_url(RESOURCES.MENUICON.pix,RESOURCES.MENUICON.component),s=e.Node.create("<a/>"),o=this.get("parentNode"),u=t,a=0;if(!this.hasChildNodes())return this;s.setAttribute("href","#"),s.addClass("actionmenu"),r.setAttribute("src",i),s.appendChild(r),o.insertBefore(s,this),a=o.getX()+parseInt(o.getComputedStyle("width"),10),a<this.get("winWidth")/2&&(u=n);var f=new e.Overlay({bodyContent:this,visible:!1,align:{node:r,points:u},zIndex:10});return f.render(),f.get("boundingBox").on("focusoutside",f.hide,f),s.setData("actionmenu-menu",f),this.all("a").each(function(){this.setData("actionmenu-link",s)}),this}),e.one("body").delegate("click",this.toggleMenu,".actionmenu"),e.one("body").on("key",this.hideAllMenus,"esc",this)},hideAllMenus:function(){e.all(".actionmenu").each(function(){var e=this.getData("actionmenu-menu");e&&e.hide()})},toggleMenu:function(e){var t=this.getData("actionmenu-menu");typeof t!="undefined"&&(t.get("visible")?t.hide():(M.core.actionmenu.instance.hideAllMenus(),t.show())),e.preventDefault()}},{NAME:"moodle-core-actionmenu",ATTRS:{}}),M.core=M.core||{},M.core.actionmenu=M.core.actionmenu||{},M.core.actionmenu.init=M.core.actionmenu.init||function(e){M.core.actionmenu.instance=M.core.actionmenu.instance||new n(e)}},"@VERSION@",{requires:["base","node","overlay","event"]});
@@ -0,0 +1,135 @@
YUI.add('moodle-core-actionmenu', function (Y, NAME) {
/**
* Provides drop down menus for list of action links.
*
* @module moodle-core-actionmenu
*/
CSS = {
ACTIONS : '.actions'
};
RESOURCES = {
MENUICON : {
pix : 't/contextmenu',
component : 'moodle'
}
};
/**
* Action menu support.
* This converts a generic list of links into a drop down menu opened by hovering or clicking
* on a menu icon.
*
* @namespace M.core.actionmenu
* @class ActionMenu
* @constructor
* @extends Y.Base
*/
var ACTIONMENU = function() {
ACTIONMENU.superclass.constructor.apply(this, arguments);
};
Y.extend(ACTIONMENU, Y.Base, {
initializer : function() {
var rightalignpoints = [Y.WidgetPositionAlign.TR, Y.WidgetPositionAlign.BR];
var leftalignpoints = [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.BL];
Y.all(CSS.ACTIONS).each(function() {
// Prepend menu icon before the list.
var imgnode = Y.Node.create('<img/>');
var imgsrc = M.util.image_url(RESOURCES.MENUICON.pix, RESOURCES.MENUICON.component);
var linknode = Y.Node.create('<a/>');
var parentnode = this.get('parentNode');
var alignpoints = rightalignpoints;
var menux = 0;
if (!this.hasChildNodes()) {
return this;
}
linknode.setAttribute('href', '#');
linknode.addClass('actionmenu');
imgnode.setAttribute('src', imgsrc);
linknode.appendChild(imgnode);
parentnode.insertBefore(linknode, this);
menux = (parentnode.getX() + parseInt(parentnode.getComputedStyle('width'), 10));
if (menux < (this.get('winWidth') / 2)) {
alignpoints = leftalignpoints;
}
var overlay = new Y.Overlay({
bodyContent : this,
visible: false,
align: {node: imgnode, points: alignpoints},
zIndex: 10
});
overlay.render();
overlay.get('boundingBox').on('focusoutside', overlay.hide, overlay);
// Create bi-directional links between the menu and the open button.
linknode.setData('actionmenu-menu', overlay);
this.all('a').each(function () {
this.setData('actionmenu-link', linknode);
});
return this;
});
Y.one('body').delegate('click', this.toggleMenu, '.actionmenu');
Y.one('body').on('key', this.hideAllMenus, 'esc', this);
},
hideAllMenus : function() {
// Hide all actionmenus.
Y.all('.actionmenu').each(function() {
var overlay = this.getData('actionmenu-menu');
if (overlay) {
            overlay.hide();
}
});
},
toggleMenu : function(e) {
var overlay = this.getData('actionmenu-menu');
if (typeof overlay !== "undefined") {
if (overlay.get('visible')) {
            overlay.hide();
        } else {
M.core.actionmenu.instance.hideAllMenus();
            overlay.show();
        }
}
e.preventDefault();
}
}, {
NAME : 'moodle-core-actionmenu',
ATTRS : { }
});
/**
* Core namespace.
*/
M.core = M.core || {};
/**
* Actionmenu namespace.
*/
M.core.actionmenu = M.core.actionmenu || {};
/**
* Init function - will only ever create one instance of the actionmenu class.
*/
M.core.actionmenu.init = M.core.actionmenu.init || function(params) {
M.core.actionmenu.instance = M.core.actionmenu.instance || new ACTIONMENU(params);
};
}, '@VERSION@', {"requires": ["base", "node", "overlay", "event"]});
+10
View File
@@ -0,0 +1,10 @@
{
"name": "moodle-core-actionmenu",
"builds": {
"moodle-core-actionmenu": {
"jsfiles": [
"actionmenu.js"
]
}
}
}
+130
View File
@@ -0,0 +1,130 @@
/**
* Provides drop down menus for list of action links.
*
* @module moodle-core-actionmenu
*/
CSS = {
ACTIONS : '.actions'
};
RESOURCES = {
MENUICON : {
pix : 't/contextmenu',
component : 'moodle'
}
};
/**
* Action menu support.
* This converts a generic list of links into a drop down menu opened by hovering or clicking
* on a menu icon.
*
* @namespace M.core.actionmenu
* @class ActionMenu
* @constructor
* @extends Y.Base
*/
var ACTIONMENU = function() {
ACTIONMENU.superclass.constructor.apply(this, arguments);
};
Y.extend(ACTIONMENU, Y.Base, {
initializer : function() {
var rightalignpoints = [Y.WidgetPositionAlign.TR, Y.WidgetPositionAlign.BR];
var leftalignpoints = [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.BL];
Y.all(CSS.ACTIONS).each(function() {
// Prepend menu icon before the list.
var imgnode = Y.Node.create('<img/>');
var imgsrc = M.util.image_url(RESOURCES.MENUICON.pix, RESOURCES.MENUICON.component);
var linknode = Y.Node.create('<a/>');
var parentnode = this.get('parentNode');
var alignpoints = rightalignpoints;
var menux = 0;
if (!this.hasChildNodes()) {
return this;
}
linknode.setAttribute('href', '#');
linknode.addClass('actionmenu');
imgnode.setAttribute('src', imgsrc);
linknode.appendChild(imgnode);
parentnode.insertBefore(linknode, this);
menux = (parentnode.getX() + parseInt(parentnode.getComputedStyle('width'), 10));
if (menux < (this.get('winWidth') / 2)) {
alignpoints = leftalignpoints;
}
var overlay = new Y.Overlay({
bodyContent : this,
visible: false,
align: {node: imgnode, points: alignpoints},
zIndex: 10
});
overlay.render();
overlay.get('boundingBox').on('focusoutside', overlay.hide, overlay);
// Create bi-directional links between the menu and the open button.
linknode.setData('actionmenu-menu', overlay);
this.all('a').each(function () {
this.setData('actionmenu-link', linknode);
});
return this;
});
Y.one('body').delegate('click', this.toggleMenu, '.actionmenu');
Y.one('body').on('key', this.hideAllMenus, 'esc', this);
},
hideAllMenus : function() {
// Hide all actionmenus.
Y.all('.actionmenu').each(function() {
var overlay = this.getData('actionmenu-menu');
if (overlay) {
            overlay.hide();
}
});
},
toggleMenu : function(e) {
var overlay = this.getData('actionmenu-menu');
if (typeof overlay !== "undefined") {
if (overlay.get('visible')) {
            overlay.hide();
        } else {
M.core.actionmenu.instance.hideAllMenus();
            overlay.show();
        }
}
e.preventDefault();
}
}, {
NAME : 'moodle-core-actionmenu',
ATTRS : { }
});
/**
* Core namespace.
*/
M.core = M.core || {};
/**
* Actionmenu namespace.
*/
M.core.actionmenu = M.core.actionmenu || {};
/**
* Init function - will only ever create one instance of the actionmenu class.
*/
M.core.actionmenu.init = M.core.actionmenu.init || function(params) {
M.core.actionmenu.instance = M.core.actionmenu.instance || new ACTIONMENU(params);
};
@@ -0,0 +1,10 @@
{
"moodle-core-actionmenu": {
"requires": [
"base",
"node",
"overlay",
"event"
]
}
}
+63
View File
@@ -1910,3 +1910,66 @@ div.badge .expireimage {
.breadcrumb-nav .breadcrumb {
margin: 0;
}
.jsenabled .actions {
visibility: hidden;
margin-top: 4px;
}
a.actionmenu {
padding-left: 4px;
padding-right: 4px;
/*
padding-right: 16px;
    background-image:url([[pix:t/expanded]]);
    background-position:100% 30%;
    background-repeat:no-repeat;
*/
}
.yui3-overlay .actions {
visibility: inherit;
background-color: @dropdownBackground;
border: 1px solid @dropdownBorder;
display: block;
padding: 4px;
.border-radius(4px);
}
.autocompletion,
a.status .iconsmall {
padding-left: 8px;
padding-right: 8px;
}
.statuses .actionlinktext {
display: none;
}
.yui3-overlay .actions a {
display: block;
color: @dropdownLinkColor;
padding-left: 4px;
padding-right: 4px;
}
.yui3-overlay .actions a.hidden {
display: none;
}
.yui3-overlay .actions img {
vertical-align: middle;
}
.yui3-overlay .actions a:hover,
.yui3-overlay .actions a:focus
{
color: @dropdownLinkColorHover;
background-color: @dropdownLinkBackgroundHover;
}
.block span.actionmenu {
display: block;
text-align: right;
}
.dir-rtl .block span.actionmenu {
text-align: left;
}
File diff suppressed because one or more lines are too long