diff --git a/amd/build/dialogue.min.js b/amd/build/dialogue.min.js
new file mode 100644
index 0000000..7538d6a
--- /dev/null
+++ b/amd/build/dialogue.min.js
@@ -0,0 +1 @@
+define(["core/yui"],function(a){var b=function(b,c,d,e,f){this.yuiDialogue=null;var g=this;"undefined"==typeof f&&(f=!1),a.use("moodle-core-notification","timers",function(){var h="480px";f&&(h="800px"),g.yuiDialogue=new M.core.dialogue({headerContent:b,bodyContent:c,draggable:!0,visible:!1,center:!0,modal:!0,width:h}),g.yuiDialogue.after("visibleChange",function(b){b.newVal?"undefined"!=typeof d&&a.soon(function(){d(g),g.yuiDialogue.centerDialogue()}):"undefined"!=typeof e&&a.soon(function(){e(g)})}),g.yuiDialogue.show()})};return b.prototype.close=function(){this.yuiDialogue.hide(),this.yuiDialogue.destroy()},b.prototype.getContent=function(){return this.yuiDialogue.bodyNode.getDOMNode()},b});
\ No newline at end of file
diff --git a/amd/build/rearrange-area.min.js b/amd/build/rearrange-area.min.js
new file mode 100644
index 0000000..22a0898
--- /dev/null
+++ b/amd/build/rearrange-area.min.js
@@ -0,0 +1 @@
+define(["jquery","core/yui","core/fragment","mod_customcert/dialogue","core/notification","core/str","core/templates","core/ajax"],function(a,b,c,d,e,f,g,h){var i=function(b){this._node=a(b),this._setEvents()};return i.prototype.CUSTOMCERT_REF_POINT_TOPLEFT=0,i.prototype.CUSTOMCERT_REF_POINT_TOPCENTER=1,i.prototype.CUSTOMCERT_REF_POINT_TOPRIGHT=2,i.prototype._setEvents=function(){this._node.on("click",".element",this._editElement.bind(this))},i.prototype._editElement=function(a){var g=a.currentTarget.id.substr(8),h=this._node.attr("data-contextid"),i={elementid:g};c.loadFragment("mod_customcert","editelement",h,i).done(function(a,c){f.get_string("editelement","mod_customcert").done(function(e){b.use("moodle-core-formchangechecker",function(){new d(e,"
",this._editElementDialogueConfig.bind(this,g,a,c),void 0,!0)}.bind(this))}.bind(this))}.bind(this)).fail(e.exception)},i.prototype._editElementDialogueConfig=function(b,c,d,e){g.replaceNode("#elementcontent",c,d);var f=a(e.getContent());f.on("click","#id_submitbutton",function(c){M.core_formchangechecker.reset_form_dirty_state(),this._saveElement(b).always(function(){this._getElementHTML(b).always(function(c){var d=this._node.find("#element-"+b),f=a("#id_refpoint").val(),g="";f==this.CUSTOMCERT_REF_POINT_TOPLEFT?g="refpoint-left":f==this.CUSTOMCERT_REF_POINT_TOPCENTER?g="refpoint-center":f==this.CUSTOMCERT_REF_POINT_TOPRIGHT&&(g="refpoint-right"),d.empty().append(c),d.removeClass(),d.addClass("element "+g),e.close()}.bind(this))}.bind(this)),c.preventDefault()}.bind(this)),f.on("click","#id_cancel",function(a){e.close(),a.preventDefault()}.bind(this))},i.prototype._getElementHTML=function(a){var b=this._node.attr("data-templateid"),c=h.call([{methodname:"mod_customcert_get_element_html",args:{templateid:b,elementid:a}}]);return c[0]},i.prototype._saveElement=function(b){var c=this._node.attr("data-templateid"),d=a("#editelementform").serializeArray(),e=h.call([{methodname:"mod_customcert_save_element",args:{templateid:c,elementid:b,values:d}}]);return e[0]},{init:function(a){new i(a)}}});
\ No newline at end of file
diff --git a/amd/src/dialogue.js b/amd/src/dialogue.js
new file mode 100644
index 0000000..88da5ac
--- /dev/null
+++ b/amd/src/dialogue.js
@@ -0,0 +1,100 @@
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see .
+
+/**
+ * Wrapper for the YUI M.core.notification class. Allows us to
+ * use the YUI version in AMD code until it is replaced.
+ *
+ * @module mod_customcert/dialogue
+ * @package mod_customcert
+ * @copyright 2016 Mark Nelson
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define(['core/yui'], function(Y) {
+
+ /**
+ * Constructor
+ *
+ * @param {String} title Title for the window.
+ * @param {String} content The content for the window.
+ * @param {function} afterShow Callback executed after the window is opened.
+ * @param {function} afterHide Callback executed after the window is closed.
+ * @param {Boolean} wide Specify we want an extra wide dialogue (the size is standard, but wider than the default).
+ */
+ var dialogue = function(title, content, afterShow, afterHide, wide) {
+ this.yuiDialogue = null;
+ var parent = this;
+
+ // Default for wide is false.
+ if (typeof wide == 'undefined') {
+ wide = false;
+ }
+
+ Y.use('moodle-core-notification', 'timers', function () {
+ var width = '480px';
+ if (wide) {
+ width = '800px';
+ }
+
+ parent.yuiDialogue = new M.core.dialogue({
+ headerContent: title,
+ bodyContent: content,
+ draggable: true,
+ visible: false,
+ center: true,
+ modal: true,
+ width: width
+ });
+
+ parent.yuiDialogue.after('visibleChange', function(e) {
+ if (e.newVal) {
+ // Delay the callback call to the next tick, otherwise it can happen that it is
+ // executed before the dialogue constructor returns.
+ if ((typeof afterShow !== 'undefined')) {
+ Y.soon(function() {
+ afterShow(parent);
+ parent.yuiDialogue.centerDialogue();
+ });
+ }
+ } else {
+ if ((typeof afterHide !== 'undefined')) {
+ Y.soon(function() {
+ afterHide(parent);
+ });
+ }
+ }
+ });
+
+ parent.yuiDialogue.show();
+ });
+ };
+
+ /**
+ * Close this window.
+ */
+ dialogue.prototype.close = function() {
+ this.yuiDialogue.hide();
+ this.yuiDialogue.destroy();
+ };
+
+ /**
+ * Get content.
+ */
+ dialogue.prototype.getContent = function() {
+ return this.yuiDialogue.bodyNode.getDOMNode();
+ };
+
+ return /** @alias module:mod_customcert/dialogue */ dialogue;
+});
diff --git a/amd/src/rearrange-area.js b/amd/src/rearrange-area.js
new file mode 100644
index 0000000..034c2d7
--- /dev/null
+++ b/amd/src/rearrange-area.js
@@ -0,0 +1,148 @@
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see .
+
+/**
+ * AMD module used when rearranging a custom certificate.
+ *
+ * @module mod_customcert/rearrange-area
+ * @package mod_customcert
+ * @copyright 2016 Mark Nelson
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define(['jquery', 'core/yui', 'core/fragment', 'mod_customcert/dialogue', 'core/notification',
+ 'core/str', 'core/templates', 'core/ajax'],
+ function($, Y, fragment, Dialogue, notification, str, template, ajax) {
+
+ /**
+ * RearrangeArea class.
+ *
+ * @param {String} selector The rearrange PDF selector
+ */
+ var RearrangeArea = function(selector) {
+ this._node = $(selector);
+ this._setEvents();
+ };
+
+ RearrangeArea.prototype.CUSTOMCERT_REF_POINT_TOPLEFT = 0;
+ RearrangeArea.prototype.CUSTOMCERT_REF_POINT_TOPCENTER = 1;
+ RearrangeArea.prototype.CUSTOMCERT_REF_POINT_TOPRIGHT = 2;
+
+ RearrangeArea.prototype._setEvents = function() {
+ this._node.on('click', '.element', this._editElement.bind(this));
+ };
+
+ RearrangeArea.prototype._editElement = function(event) {
+ var elementid = event.currentTarget.id.substr(8);
+ var contextid = this._node.attr('data-contextid');
+ var params = {
+ 'elementid' : elementid
+ };
+
+ fragment.loadFragment('mod_customcert', 'editelement', contextid, params).done(function(html, js) {
+ str.get_string('editelement', 'mod_customcert').done(function(title) {
+ Y.use('moodle-core-formchangechecker', function () {
+ new Dialogue(
+ title,
+ '',
+ this._editElementDialogueConfig.bind(this, elementid, html, js),
+ undefined,
+ true
+ );
+ }.bind(this));
+ }.bind(this));
+ }.bind(this)).fail(notification.exception);
+ };
+
+ RearrangeArea.prototype._editElementDialogueConfig = function(elementid, html, js, popup) {
+ // Place the content in the dialogue.
+ template.replaceNode('#elementcontent', html, js);
+
+ // Add events for when we save, close and cancel the page.
+ var body = $(popup.getContent());
+ body.on('click', '#id_submitbutton', function(e) {
+ // Do not want to ask the user if they wish to stay on page after saving.
+ M.core_formchangechecker.reset_form_dirty_state();
+ // Save the data.
+ this._saveElement(elementid).always(function() {
+ // Update the DOM to reflect the adjusted value.
+ this._getElementHTML(elementid).always(function(html) {
+ var elementNode = this._node.find('#element-' + elementid);
+ var refpoint = $('#id_refpoint').val();
+ var refpointClass = '';
+ if (refpoint == this.CUSTOMCERT_REF_POINT_TOPLEFT) {
+ refpointClass = 'refpoint-left';
+ } else if (refpoint == this.CUSTOMCERT_REF_POINT_TOPCENTER) {
+ refpointClass = 'refpoint-center';
+ } else if (refpoint == this.CUSTOMCERT_REF_POINT_TOPRIGHT) {
+ refpointClass = 'refpoint-right';
+ }
+ elementNode.empty().append(html);
+ // Update the ref point.
+ elementNode.removeClass();
+ elementNode.addClass('element ' + refpointClass);
+ popup.close();
+ }.bind(this));
+ }.bind(this));
+ e.preventDefault();
+ }.bind(this));
+
+ body.on('click', '#id_cancel', function(e) {
+ popup.close();
+ e.preventDefault();
+ }.bind(this));
+ };
+
+ RearrangeArea.prototype._getElementHTML = function(elementid) {
+ // Get the variables we need.
+ var templateid = this._node.attr('data-templateid');
+
+ // Call the web service to get the updated element.
+ var promises = ajax.call([{
+ methodname: 'mod_customcert_get_element_html',
+ args: {
+ templateid : templateid,
+ elementid : elementid
+ }
+ }]);
+
+ // Return the promise.
+ return promises[0];
+ };
+
+ RearrangeArea.prototype._saveElement = function(elementid) {
+ // Get the variables we need.
+ var templateid = this._node.attr('data-templateid');
+ var inputs = $('#editelementform').serializeArray();
+
+ // Call the web service to save the element.
+ var promises = ajax.call([{
+ methodname: 'mod_customcert_save_element',
+ args: {
+ templateid : templateid,
+ elementid : elementid,
+ values : inputs
+ }
+ }]);
+
+ // Return the promise.
+ return promises[0];
+ };
+
+ return {
+ init : function(selector) {
+ new RearrangeArea(selector);
+ }
+ };
+});
diff --git a/classes/edit_element_form.php b/classes/edit_element_form.php
index a2df501..7a38033 100644
--- a/classes/edit_element_form.php
+++ b/classes/edit_element_form.php
@@ -44,14 +44,19 @@ class edit_element_form extends \moodleform {
public function definition() {
$mform =& $this->_form;
+ $mform->updateAttributes(array('id' => 'editelementform'));
+
$element = $this->_customdata['element'];
- // Add the field for the name of the element, this is required for all elements.
- $mform->addElement('text', 'name', get_string('elementname', 'customcert'));
- $mform->setType('name', PARAM_TEXT);
- $mform->setDefault('name', get_string('pluginname', 'customcertelement_' . $element->element));
- $mform->addRule('name', get_string('required'), 'required', null, 'client');
- $mform->addHelpButton('name', 'elementname', 'customcert');
+ // Do not display the name if we are on the rearrange page.
+ if (!isset($this->_customdata['rearrange'])) {
+ // Add the field for the name of the element, this is required for all elements.
+ $mform->addElement('text', 'name', get_string('elementname', 'customcert'));
+ $mform->setType('name', PARAM_TEXT);
+ $mform->setDefault('name', get_string('pluginname', 'customcertelement_' . $element->element));
+ $mform->addRule('name', get_string('required'), 'required', null, 'client');
+ $mform->addHelpButton('name', 'elementname', 'customcert');
+ }
$this->element = \mod_customcert\element::instance($element);
$this->element->render_form_elements($mform);
diff --git a/classes/element.php b/classes/element.php
index 5f85ab2..a0a5a18 100644
--- a/classes/element.php
+++ b/classes/element.php
@@ -101,6 +101,7 @@ abstract class element {
* Can be overridden if more functionality is needed.
*
* @param \stdClass $data the form data
+ * @return bool true of success, false otherwise.
*/
public function save_form_elements($data) {
global $DB;
@@ -119,13 +120,13 @@ abstract class element {
// Check if we are updating, or inserting a new element.
if (!empty($this->element->id)) { // Must be updating a record in the database.
$element->id = $this->element->id;
- $DB->update_record('customcert_elements', $element);
+ return $DB->update_record('customcert_elements', $element);
} else { // Must be adding a new one.
$element->element = $data->element;
$element->pageid = $data->pageid;
$element->sequence = \mod_customcert\element_helper::get_element_sequence($element->pageid);
$element->timecreated = time();
- $DB->insert_record('customcert_elements', $element);
+ return $DB->insert_record('customcert_elements', $element, false);
}
}
diff --git a/classes/external.php b/classes/external.php
new file mode 100644
index 0000000..5e993b3
--- /dev/null
+++ b/classes/external.php
@@ -0,0 +1,164 @@
+.
+
+/**
+ * This is the external API for this tool.
+ *
+ * @package mod_customcert
+ * @copyright 2016 Mark Nelson
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace mod_customcert;
+defined('MOODLE_INTERNAL') || die();
+
+require_once("$CFG->libdir/externallib.php");
+
+/**
+* This is the external API for this tool.
+*
+* @copyright 2016 Mark Nelson
+* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+*/
+class external extends \external_api {
+
+ /**
+ * Returns the save_element() parameters.
+ *
+ * @return \external_function_parameters
+ */
+ public static function save_element_parameters() {
+ return new \external_function_parameters(
+ array(
+ 'templateid' => new \external_value(PARAM_INT, 'The template id'),
+ 'elementid' => new \external_value(PARAM_INT, 'The element id'),
+ 'values' => new \external_multiple_structure(
+ new \external_single_structure(
+ array(
+ 'name' => new \external_value(PARAM_ALPHANUMEXT, 'The field to update'),
+ 'value' => new \external_value(PARAM_RAW, 'The value of the field'),
+ )
+ )
+ )
+ )
+ );
+ }
+
+ /**
+ * Handles saving element data.
+ *
+ * @param int $templateid The template id.
+ * @param int $elementid The element id.
+ * @param array $values The values to save
+ * @return array
+ */
+ public static function save_element($templateid, $elementid, $values) {
+ global $DB;
+
+ $template = $DB->get_record('customcert_templates', array('id' => $templateid), '*', MUST_EXIST);
+ $element = $DB->get_record('customcert_elements', array('id' => $elementid), '*', MUST_EXIST);
+
+ // Set the template.
+ $template = new \mod_customcert\template($template);
+
+ // Perform checks.
+ if ($cm = $template->get_cm()) {
+ require_login($cm->course, false, $cm);
+ } else {
+ require_login();
+ }
+ // Make sure the user has the required capabilities.
+ $template->require_manage();
+
+ // Set the values we are going to save.
+ $data = new \stdClass();
+ $data->id = $element->id;
+ $data->name = $element->name;
+ foreach ($values as $value) {
+ $field = $value['name'];
+ $data->$field = $value['value'];
+ }
+
+ // Get an instance of the element class.
+ if ($e = \mod_customcert\element::instance($element)) {
+ return $e->save_form_elements($data);
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the save_element result value.
+ *
+ * @return \external_value
+ */
+ public static function save_element_returns() {
+ return new \external_value(PARAM_BOOL, 'True if successful, false otherwise');
+ }
+
+ /**
+ * Returns get_element() parameters.
+ *
+ * @return \external_function_parameters
+ */
+ public static function get_element_html_parameters() {
+ return new \external_function_parameters(
+ array(
+ 'templateid' => new \external_value(PARAM_INT, 'The template id'),
+ 'elementid' => new \external_value(PARAM_INT, 'The element id'),
+ )
+ );
+ }
+
+ /**
+ * Handles return the element's HTML.
+ *
+ * @param int $templateid The template id
+ * @param int $elementid The element id.
+ * @return string
+ */
+ public static function get_element_html($templateid, $elementid) {
+ global $DB;
+
+ $template = $DB->get_record('customcert_templates', array('id' => $templateid), '*', MUST_EXIST);
+ $element = $DB->get_record('customcert_elements', array('id' => $elementid), '*', MUST_EXIST);
+
+ // Set the template.
+ $template = new \mod_customcert\template($template);
+
+ // Perform checks.
+ if ($cm = $template->get_cm()) {
+ require_login($cm->course, false, $cm);
+ } else {
+ require_login();
+ }
+
+ // Get an instance of the element class.
+ if ($e = \mod_customcert\element::instance($element)) {
+ return $e->render_html();
+ }
+
+ return '';
+ }
+
+ /**
+ * Returns the get_element result value.
+ *
+ * @return \external_value
+ */
+ public static function get_element_html_returns() {
+ return new \external_value(PARAM_RAW, 'The HTML');
+ }
+}
diff --git a/db/services.php b/db/services.php
new file mode 100644
index 0000000..1a74560
--- /dev/null
+++ b/db/services.php
@@ -0,0 +1,42 @@
+.
+
+/**
+ * Web service for mod customcert.
+ *
+ * @package mod_customcert
+ * @copyright 2016 Mark Nelson
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$functions = array(
+ 'mod_customcert_save_element' => array(
+ 'classname' => 'mod_customcert\external',
+ 'methodname' => 'save_element',
+ 'classpath' => '',
+ 'description' => 'Saves data for an element',
+ 'type' => 'write',
+ 'ajax' => true
+ ),
+ 'mod_customcert_get_element_html' => array(
+ 'classname' => 'mod_customcert\external',
+ 'methodname' => 'get_element_html',
+ 'classpath' => '',
+ 'description' => 'Returns the HTML to display for an element',
+ 'type' => 'read',
+ 'ajax' => true
+ ),
+);
diff --git a/element/image/classes/element.php b/element/image/classes/element.php
index 39b817a..5fe6706 100644
--- a/element/image/classes/element.php
+++ b/element/image/classes/element.php
@@ -99,6 +99,7 @@ class element extends \mod_customcert\element {
* Can be overridden if more functionality is needed.
*
* @param \stdClass $data the form data
+ * @return bool true of success, false otherwise.
*/
public function save_form_elements($data) {
global $COURSE;
@@ -106,7 +107,7 @@ class element extends \mod_customcert\element {
// Handle file uploads.
\mod_customcert\certificate::upload_imagefiles($data->customcertimage, \context_course::instance($COURSE->id)->id);
- parent::save_form_elements($data);
+ return parent::save_form_elements($data);
}
/**
diff --git a/lib.php b/lib.php
index 5c633f7..33c01ee 100644
--- a/lib.php
+++ b/lib.php
@@ -303,6 +303,24 @@ function customcert_cron() {
return true;
}
+/**
+ * Serve the edit element as a fragment.
+ *
+ * @param array $args List of named arguments for the fragment loader.
+ * @return string
+ */
+function mod_customcert_output_fragment_editelement($args) {
+ global $DB;
+
+ // Get the element.
+ $element = $DB->get_record('customcert_elements', array('id' => $args['elementid']), '*', MUST_EXIST);
+
+ $pageurl = new moodle_url('/mod/customcert/rearrange.php', array('pid' => $element->pageid));
+ $form = new \mod_customcert\edit_element_form($pageurl, array('element' => $element, 'rearrange' => true));
+
+ return $form->render();
+}
+
/**
* This function extends the settings navigation block for the site.
*
diff --git a/rearrange.php b/rearrange.php
index a8bebb4..4f6def5 100644
--- a/rearrange.php
+++ b/rearrange.php
@@ -65,7 +65,12 @@ $html .= html_writer::end_tag('div');
// Create the div that represents the PDF.
$style = 'height: ' . $page->height . 'mm; line-height: normal; width: ' . $page->width . 'mm;';
$marginstyle = 'height: ' . $page->height . 'mm; width:1px; float:left; position:relative;';
-$html .= html_writer::start_tag('div', array('id' => 'pdf', 'style' => $style));
+$html .= html_writer::start_tag('div', array(
+ 'data-templateid' => $template->get_id(),
+ 'data-contextid' => $template->get_contextid(),
+ 'id' => 'pdf',
+ 'style' => $style)
+);
if ($page->leftmargin) {
$position = 'left:' . $page->leftmargin . 'mm;';
$html .= "";
@@ -83,7 +88,7 @@ if ($elements) {
break;
case \mod_customcert\element_helper::CUSTOMCERT_REF_POINT_TOPLEFT:
default:
- $class = 'element refpoint-left';
+ $class = 'element refpoint-left';
}
$html .= html_writer::tag('div', $e->render_html(), array('class' => $class, 'id' => 'element-' . $element->id));
}
@@ -98,4 +103,5 @@ $html .= html_writer::end_tag('div');
echo $OUTPUT->header();
echo $OUTPUT->heading(get_string('rearrangeelementsheading', 'customcert'), 4);
echo $html;
-echo $OUTPUT->footer();
\ No newline at end of file
+$PAGE->requires->js_call_amd('mod_customcert/rearrange-area', 'init', array('#pdf'));
+echo $OUTPUT->footer();
diff --git a/styles.css b/styles.css
index 3c4aa5c..238cae6 100644
--- a/styles.css
+++ b/styles.css
@@ -65,3 +65,11 @@
#page-mod-customcert-rearrange div#rightmargin {
border-right: 1px dotted black;
}
+
+/*
+ This is a hack - the fieldset being returned by the edit_element_form class contains
+ the CSS class 'hidden' which when shown in a dialogue hides the fieldset.
+*/
+.moodle-dialogue #editelementform fieldset.hidden {
+ display: block;
+}
diff --git a/version.php b/version.php
index 8b6a4d8..1ef024a 100644
--- a/version.php
+++ b/version.php
@@ -24,10 +24,10 @@
defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.');
-$plugin->version = 2016032200; // The current module version (Date: YYYYMMDDXX).
-$plugin->requires = 2015051100; // Requires this Moodle version (2.9).
+$plugin->version = 2016052300; // The current module version (Date: YYYYMMDDXX).
+$plugin->requires = 2016052300; // Requires this Moodle version (3.1).
$plugin->cron = 0; // Period for cron to check this module (secs).
$plugin->component = 'mod_customcert';
$plugin->maturity = MATURITY_BETA;
-$plugin->release = "Beta release (Build: 2016021900)"; // User-friendly version number.
+$plugin->release = "3.1 beta release (Build: 2016052300)"; // User-friendly version number.