Skip to content

Commit

Permalink
MDL-35819 AJAX Rewrite Moodle popup help to be more friendly and more…
Browse files Browse the repository at this point in the history
… efficient
  • Loading branch information
Andrew Robert Nicols committed Feb 10, 2013
1 parent 6319737 commit 238b8bc
Show file tree
Hide file tree
Showing 11 changed files with 598 additions and 178 deletions.
46 changes: 29 additions & 17 deletions help.php
Expand Up @@ -48,15 +48,15 @@

if ($ajax) {
@header('Content-Type: text/plain; charset=utf-8');
} else {
echo $OUTPUT->header();
}

if (!$sm->string_exists($identifier.'_help', $component)) {
// strings on-diskc cache may be dirty - try to rebuild it and check again
// strings on disk-cache may be dirty - try to rebuild it and check again
$sm->load_component_strings($component, current_language(), true);
}

$data = new stdClass();

if ($sm->string_exists($identifier.'_help', $component)) {
$options = new stdClass();
$options->trusted = false;
Expand All @@ -67,26 +67,38 @@
$options->newlines = false;
$options->overflowdiv = !$ajax;

if ($ajax) {
// When using AJAX, the header should be H2 as it is in the same DOM as the calling page.
echo $OUTPUT->heading(format_string(get_string($identifier, $component)), 2, 'helpheading');
} else {
// When not using AJAX, the header should be H1 as it is in it's own window.
echo $OUTPUT->heading(format_string(get_string($identifier, $component)), 1, 'helpheading');
}
$data->heading = format_string(get_string($identifier, $component));
// Should be simple wiki only MDL-21695
echo format_text(get_string($identifier.'_help', $component), FORMAT_MARKDOWN, $options);
$data->text = format_text(get_string($identifier.'_help', $component), FORMAT_MARKDOWN, $options);

if ($sm->string_exists($identifier.'_link', $component)) { // Link to further info in Moodle docs
$link = get_string($identifier.'_link', $component);
$helplink = $identifier . '_link';
if ($sm->string_exists($helplink, $component)) { // Link to further info in Moodle docs
$link = get_string($helplink, $component);
$linktext = get_string('morehelp');
echo '<div class="helpdoclink">'.$OUTPUT->doc_link($link, $linktext).'</div>';
}

$data->doclink = new stdClass();
$url = new moodle_url(get_docs_url($link));
$data->doclink->link = $url->out();
$data->doclink->linktext = $linktext;
$data->doclink->class = ($CFG->doctonewwindow) ? 'helplinkpopup' : '';

$completedoclink = html_writer::tag('div', $OUTPUT->doc_link($link, $linktext), array('class' => 'helpdoclink'));
}
} else {
echo "<p><strong>TODO</strong>: missing help string [{$identifier}_help, $component]</p>";
$data->text = html_writer::tag('p',
html_writer::tag('strong', 'TODO') . ": missing help string [{$identifier}_help, {$component}]");
}

if (!$ajax) {
if ($ajax) {
echo json_encode($data);
} else {
echo $OUTPUT->header();
if (isset($data->heading)) {
echo $OUTPUT->heading($data->heading, 1, 'helpheading');
}
echo $data->text;
if (isset($completedoclink)) {
echo $completedoclink;
}
echo $OUTPUT->footer();
}
1 change: 1 addition & 0 deletions lang/en/moodle.php
Expand Up @@ -924,6 +924,7 @@
$string['listfiles'] = 'List of files in {$a}';
$string['listofallpeople'] = 'List of all people';
$string['listofcourses'] = 'List of courses';
$string['loadinghelp'] = 'Loading...';
$string['local'] = 'Local';
$string['localplugindeleteconfirm'] = 'You are about to completely delete the local plugin \'{$a}\'. This will completely delete everything in the database associated with this plugin. Are you SURE you want to continue?';
$string['localplugins'] = 'Local plugins';
Expand Down
136 changes: 16 additions & 120 deletions lib/javascript-static.js
Expand Up @@ -1480,131 +1480,27 @@ M.util.help_popups = {
}
}

/**
* This code bas been deprecated and will be removed from Moodle 2.7
*
* Please see lib/yui/popuphelp/popuphelp.js for its replacement
*/
M.util.help_icon = {
Y : null,
instance : null,
initialised : false,
setup : function(Y) {
if (this.initialised) {
// Exit early if we have already completed setup
return;
}
this.Y = Y;
Y.one('body').delegate('click', this.display, 'span.helplink a.tooltip', this);
this.initialised = true;
},
add : function(Y, properties) {
this.setup(Y);
setup : function(Y, properties) {
this.add(Y, properties);
},
display : function(event) {
event.preventDefault();
if (M.util.help_icon.instance === null) {
var Y = M.util.help_icon.Y;
Y.use('overlay', 'io-base', 'event-mouseenter', 'node', 'event-key', 'escape', function(Y) {
var help_content_overlay = {
helplink : null,
overlay : null,
init : function() {

var strclose = Y.Escape.html(M.str.form.close);
var footerbtn = Y.Node.create('<button class="closebtn">'+strclose+'</button>');
// Create an overlay from markup
this.overlay = new Y.Overlay({
footerContent: footerbtn,
bodyContent: '',
id: 'helppopupbox',
width:'400px',
visible : false,
constrain : true
});
this.overlay.render(Y.one(document.body));

footerbtn.on('click', this.close, this);

var boundingBox = this.overlay.get("boundingBox");

// Hide the menu if the user clicks outside of its content
boundingBox.get("ownerDocument").on("mousedown", function (event) {
var oTarget = event.target;
var menuButton = this.helplink;

if (!oTarget.compareTo(menuButton) &&
!menuButton.contains(oTarget) &&
!oTarget.compareTo(boundingBox) &&
!boundingBox.contains(oTarget)) {
this.overlay.hide();
}
}, this);
},

close : function(e) {
e.preventDefault();
this.helplink.focus();
this.overlay.hide();
},

display : function(event) {
var overlayPosition;
this.helplink = event.target.ancestor('span.helplink a', true);
if (Y.one('html').get('dir') === 'rtl') {
overlayPosition = [Y.WidgetPositionAlign.TR, Y.WidgetPositionAlign.LC];
} else {
overlayPosition = [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.RC];
}

this.overlay.set('bodyContent', Y.Node.create('<img src="'+M.cfg.loadingicon+'" class="spinner" />'));
this.overlay.set("align", {node:this.helplink, points: overlayPosition});

var cfg = {
method: 'get',
context : this,
data : {
ajax : 1
},
on: {
success: function(id, o, node) {
this.display_callback(o.responseText);
},
failure: function(id, o, node) {
var debuginfo = o.statusText;
if (M.cfg.developerdebug) {
o.statusText += ' (' + ajaxurl + ')';
}
this.display_callback('bodyContent',debuginfo);
}
}
};

Y.io(this.helplink.get('href'), cfg);
this.overlay.show();
},

display_callback : function(content) {
var contentnode, heading;
contentnode = Y.Node.create('<div role="alert">' + content + '</div>');
this.overlay.set('bodyContent', contentnode);
heading = contentnode.one('h2');
if (heading) {
heading.set('tabIndex', 0);
heading.focus();
}
},

hideContent : function() {
help = this;
help.overlay.hide();
}
};
help_content_overlay.init();
M.util.help_icon.instance = help_content_overlay;
M.util.help_icon.instance.display(event);
add : function(Y) {
if (M.cfg.developerdebug) {
Y.log("You are using a deprecated function call (M.util.help_icon.add). " +
"Please look at rewriting your call to support lib/yui/popuphelp/popuphelp.js");
}
if (!this.initialised) {
YUI().use('moodle-core-popuphelp', function() {
M.core.init_popuphelp([]);
});
} else {
M.util.help_icon.instance.display(event);
}
},
init : function(Y) {
this.Y = Y;
this.initialised = true;
}
};

Expand Down
18 changes: 11 additions & 7 deletions lib/outputrenderers.php
Expand Up @@ -364,9 +364,16 @@ public function standard_head_html() {
// flow player embedding support
$this->page->requires->js_function_call('M.util.load_flowplayer');

// Set up help link popups for all links with the helplinkpopup class
// Set up help link popups for all links with the helptooltip class
$this->page->requires->js_init_call('M.util.help_popups.setup');

// Setup help icon overlays.
$this->page->requires->yui_module('moodle-core-popuphelp', 'M.core.init_popuphelp');
$this->page->requires->strings_for_js(array(
'morehelp',
'loadinghelp',
), 'moodle');

$this->page->requires->js_function_call('setTimeout', array('fix_column_widths()', 20));

$focus = $this->page->focuscontrol;
Expand Down Expand Up @@ -1932,7 +1939,7 @@ protected function render_old_help_icon(old_help_icon $helpicon) {
$this->page->requires->string_for_js('close', 'form');

// and finally span
return html_writer::tag('span', $output, array('class' => 'helplink'));
return html_writer::tag('span', $output, array('class' => 'helptooltip'));
}

/**
Expand Down Expand Up @@ -1989,14 +1996,11 @@ protected function render_help_icon(help_icon $helpicon) {
// note: this title is displayed only if JS is disabled, otherwise the link will have the new ajax tooltip
$title = get_string('helpprefix2', '', trim($title, ". \t"));

$attributes = array('href'=>$url, 'title'=>$title, 'aria-haspopup' => 'true', 'class' => 'tooltip');
$attributes = array('href' => $url, 'title' => $title, 'aria-haspopup' => 'true');
$output = html_writer::tag('a', $output, $attributes);

$this->page->requires->js_init_call('M.util.help_icon.setup');
$this->page->requires->string_for_js('close', 'form');

// and finally span
return html_writer::tag('span', $output, array('class' => 'helplink'));
return html_writer::tag('span', $output, array('class' => 'helptooltip'));
}

/**
Expand Down
5 changes: 5 additions & 0 deletions lib/upgrade.txt
Expand Up @@ -15,6 +15,11 @@ information provided here is intended especially for developers.
perform the whole deletion process. The function course_delete_module now takes care
of the whole process.

YUI changes:
* M.util.help_icon has been deprecated. Code should be updated to use moodle-core-popuphelp
instead. To do so, remove any existing JS calls to M.util.help_icon from your PHP and ensure
that your help link is placed in a span which has the class 'helplink'.

=== 2.4 ===

* Pagelib: Numerous deprecated functions were removed as classes page_base, page_course
Expand Down
86 changes: 86 additions & 0 deletions lib/yui/popuphelp/popuphelp.js
@@ -0,0 +1,86 @@
YUI.add('moodle-core-popuphelp', function(Y) {
function POPUPHELP() {
POPUPHELP.superclass.constructor.apply(this, arguments);
}

var SELECTORS = {
CLICKABLELINKS: 'span.helptooltip > a',
FOOTER: 'div.moodle-dialogue-ft'
},

CSS = {
ICON: 'icon',
ICONPRE: 'icon-pre'
},
ATTRS = {};

// Set the modules base properties.
POPUPHELP.NAME = 'moodle-core-popuphelp';
POPUPHELP.ATTRS = ATTRS;

Y.extend(POPUPHELP, Y.Base, {
panel: null,

initializer: function() {
Y.one('body').delegate('click', this.display_panel, SELECTORS.CLICKABLELINKS, this);
},

display_panel: function(e) {
if (!this.panel) {
this.panel = new M.core.tooltip({
bodyhandler: this.set_body_content,
footerhandler: this.set_footer,
initialheadertext: M.util.get_string('loadinghelp', 'moodle'),
initialfootertext: ''
});
}

// Call the tooltip setup.
this.panel.display_panel(e);
},

/**
* Override the footer handler to add a 'More help' link where relevant.
*
* @param {Object} helpobject The object returned from the AJAX call.
*/
set_footer: function(helpobject) {
// Check for an optional link to documentation on moodle.org.
if (helpobject.doclink) {
// Wrap a help icon and the morehelp text in an anchor. The class of the anchor should
// determine whether it's opened in a new window or not.
doclink = Y.Node.create('<a />')
.setAttrs({
'href': helpobject.doclink.link
})
.addClass(helpobject.doclink['class']);
helpicon = Y.Node.create('<img />')
.setAttrs({
'src': M.util.image_url('docs', 'core')
})
.addClass(CSS.ICON)
.addClass(CSS.ICONPRE);
doclink.appendChild(helpicon);
doclink.appendChild(helpobject.doclink.linktext);

// Set the footerContent to the contents of the doclink.
this.set('footerContent', doclink);
this.bb.one(SELECTORS.FOOTER).show();
} else {
this.bb.one(SELECTORS.FOOTER).hide();
}
}
});
M.core = M.core || {};
M.core.popuphelp = M.core.popuphelp || null;
M.core.init_popuphelp = M.core.init_popuphelp || function(config) {
// Only set up a single instance of the popuphelp.
if (!M.core.popuphelp) {
M.core.popuphelp = new POPUPHELP(config);
}
return M.core.popuphelp;
};
},
'@VERSION@', {
requires: ['moodle-core-tooltip']
});

0 comments on commit 238b8bc

Please sign in to comment.