From 1ce2ce7cb08ed7acf4ddf23e15d84c46b4860da5 Mon Sep 17 00:00:00 2001 From: Davo Smith Date: Fri, 27 Sep 2013 14:40:10 +0800 Subject: [PATCH 1/4] MDL-42023 assign: Add Edit PDF plugin to core. This plugin is heavily based on the uploadpdf plugin from Davo Smith . This is a fantastic plugin that provides a great benefit for teachers marking assignments. A big thankyou to Davo for writing the plugin and helping us to get it integrated to core. The plugin provides a pdf editing interface in the teachers browser that lets them add comments and annotations to a students assignment submission. The comment and annotations are then added to a pdf version of the students submission which is send back to the student so they can download it, or read it online. This plugin has been primarily worked on by Damyon Wiese, Jerome Mouneyrac and Barbara Ramiro. We have also had great feedback and suggestions from others including the entire frontend team, Martin and Frederic Massart. --- mod/assign/feedback/editpdf/version.php | 30 +++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 mod/assign/feedback/editpdf/version.php diff --git a/mod/assign/feedback/editpdf/version.php b/mod/assign/feedback/editpdf/version.php new file mode 100644 index 00000000000..5b6716d7930 --- /dev/null +++ b/mod/assign/feedback/editpdf/version.php @@ -0,0 +1,30 @@ +. + +/** + * This file contains the version information for the comments feedback plugin + * + * @package assignfeedback_editpdf + * @copyright 2012 Davo Smith + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$plugin->version = 2013050103; +$plugin->requires = 2013050100; +$plugin->component = 'assignfeedback_editpdf'; + From e0cc92c650a4f1558435edbe3fc3d71671dc0fc4 Mon Sep 17 00:00:00 2001 From: Jerome Mouneyrac Date: Fri, 27 Sep 2013 14:47:12 +0800 Subject: [PATCH 2/4] MDL-42023 assign: Edit PDF plugin - Jerome's contributions --- .../yui/src/editor/js/annotationline.js | 112 +++++++++++++ .../yui/src/editor/js/annotationoval.js | 100 +++++++++++ .../yui/src/editor/js/annotationpen.js | 152 +++++++++++++++++ .../yui/src/editor/js/annotationrectangle.js | 100 +++++++++++ .../yui/src/editor/js/annotationstamp.js | 158 ++++++++++++++++++ .../editpdf/yui/src/editor/js/stamppicker.js | 105 ++++++++++++ 6 files changed, 727 insertions(+) create mode 100644 mod/assign/feedback/editpdf/yui/src/editor/js/annotationline.js create mode 100644 mod/assign/feedback/editpdf/yui/src/editor/js/annotationoval.js create mode 100644 mod/assign/feedback/editpdf/yui/src/editor/js/annotationpen.js create mode 100644 mod/assign/feedback/editpdf/yui/src/editor/js/annotationrectangle.js create mode 100644 mod/assign/feedback/editpdf/yui/src/editor/js/annotationstamp.js create mode 100644 mod/assign/feedback/editpdf/yui/src/editor/js/stamppicker.js diff --git a/mod/assign/feedback/editpdf/yui/src/editor/js/annotationline.js b/mod/assign/feedback/editpdf/yui/src/editor/js/annotationline.js new file mode 100644 index 00000000000..407e3e324da --- /dev/null +++ b/mod/assign/feedback/editpdf/yui/src/editor/js/annotationline.js @@ -0,0 +1,112 @@ +// 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 . + +/** + * Class representing a line. + * + * @namespace M.assignfeedback_editpdf + * @class annotationline + * @extends annotation + * @module moodle-assignfeedback_editpdf-editor + */ +ANNOTATIONLINE = function(config) { + ANNOTATIONLINE.superclass.constructor.apply(this, [config]); +}; + +ANNOTATIONLINE.NAME = "annotationline"; +ANNOTATIONLINE.ATTRS = {}; + +Y.extend(ANNOTATIONLINE, M.assignfeedback_editpdf.annotation, { + /** + * Draw a line annotation + * @protected + * @method draw + * @return M.assignfeedback_editpdf.drawable + */ + draw : function() { + var drawable, + shape; + + drawable = new M.assignfeedback_editpdf.drawable(this.editor); + + shape = this.editor.graphic.addShape({ + type: Y.Path, + fill: false, + stroke: { + weight: STROKEWEIGHT, + color: ANNOTATIONCOLOUR[this.colour] + } + }); + + shape.moveTo(this.x, this.y); + shape.lineTo(this.endx, this.endy); + shape.end(); + drawable.shapes.push(shape); + this.drawable = drawable; + + return ANNOTATIONLINE.superclass.draw.apply(this); + }, + + /** + * Draw the in progress edit. + * + * @public + * @method draw_current_edit + * @param M.assignfeedback_editpdf.edit edit + */ + draw_current_edit : function(edit) { + var drawable = new M.assignfeedback_editpdf.drawable(this.editor), + shape; + + shape = this.editor.graphic.addShape({ + type: Y.Path, + fill: false, + stroke: { + weight: STROKEWEIGHT, + color: ANNOTATIONCOLOUR[edit.annotationcolour] + } + }); + + shape.moveTo(edit.start.x, edit.start.y); + shape.lineTo(edit.end.x, edit.end.y); + shape.end(); + + drawable.shapes.push(shape); + + return drawable; + }, + + /** + * Promote the current edit to a real annotation. + * + * @public + * @method init_from_edit + * @param M.assignfeedback_editpdf.edit edit + */ + init_from_edit : function(edit) { + this.gradeid = this.editor.get('gradeid'); + this.pageno = this.editor.currentpage; + this.x = edit.start.x; + this.y = edit.start.y; + this.endx = edit.end.x; + this.endy = edit.end.y; + this.colour = edit.annotationcolour; + this.path = ''; + } + +}); + +M.assignfeedback_editpdf = M.assignfeedback_editpdf || {}; +M.assignfeedback_editpdf.annotationline = ANNOTATIONLINE; diff --git a/mod/assign/feedback/editpdf/yui/src/editor/js/annotationoval.js b/mod/assign/feedback/editpdf/yui/src/editor/js/annotationoval.js new file mode 100644 index 00000000000..dcca29fa05b --- /dev/null +++ b/mod/assign/feedback/editpdf/yui/src/editor/js/annotationoval.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 . + +/** + * Class representing a oval. + * + * @namespace M.assignfeedback_editpdf + * @class annotationoval + * @extends annotation + * @module moodle-assignfeedback_editpdf-editor + */ +ANNOTATIONOVAL = function(config) { + ANNOTATIONOVAL.superclass.constructor.apply(this, [config]); +}; + +ANNOTATIONOVAL.NAME = "annotationoval"; +ANNOTATIONOVAL.ATTRS = {}; + +Y.extend(ANNOTATIONOVAL, M.assignfeedback_editpdf.annotation, { + /** + * Draw a oval annotation + * @protected + * @method draw + * @return M.assignfeedback_editpdf.drawable + */ + draw : function() { + var drawable, + shape; + + drawable = new M.assignfeedback_editpdf.drawable(this.editor); + + bounds = new M.assignfeedback_editpdf.rect(); + bounds.bound([new M.assignfeedback_editpdf.point(this.x, this.y), + new M.assignfeedback_editpdf.point(this.endx, this.endy)]); + + shape = this.editor.graphic.addShape({ + type: Y.Ellipse, + width: bounds.width, + height: bounds.height, + stroke: { + weight: STROKEWEIGHT, + color: ANNOTATIONCOLOUR[this.colour] + }, + x: bounds.x, + y: bounds.y + }); + drawable.shapes.push(shape); + this.drawable = drawable; + + return ANNOTATIONOVAL.superclass.draw.apply(this); + }, + + /** + * Draw the in progress edit. + * + * @public + * @method draw_current_edit + * @param M.assignfeedback_editpdf.edit edit + */ + draw_current_edit : function(edit) { + var drawable = new M.assignfeedback_editpdf.drawable(this.editor), + shape, + bounds; + + bounds = new M.assignfeedback_editpdf.rect(); + bounds.bound([new M.assignfeedback_editpdf.point(edit.start.x, edit.start.y), + new M.assignfeedback_editpdf.point(edit.end.x, edit.end.y)]); + + shape = this.editor.graphic.addShape({ + type: Y.Ellipse, + width: bounds.width, + height: bounds.height, + stroke: { + weight: STROKEWEIGHT, + color: ANNOTATIONCOLOUR[edit.annotationcolour] + }, + x: bounds.x, + y: bounds.y + }); + + drawable.shapes.push(shape); + + return drawable; + } +}); + +M.assignfeedback_editpdf = M.assignfeedback_editpdf || {}; +M.assignfeedback_editpdf.annotationoval = ANNOTATIONOVAL; diff --git a/mod/assign/feedback/editpdf/yui/src/editor/js/annotationpen.js b/mod/assign/feedback/editpdf/yui/src/editor/js/annotationpen.js new file mode 100644 index 00000000000..9915a52aa1e --- /dev/null +++ b/mod/assign/feedback/editpdf/yui/src/editor/js/annotationpen.js @@ -0,0 +1,152 @@ +// 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 . + +/** + * Class representing a pen. + * + * @namespace M.assignfeedback_editpdf + * @class annotationpen + * @extends annotation + * @module moodle-assignfeedback_editpdf-editor + */ +ANNOTATIONPEN = function(config) { + ANNOTATIONPEN.superclass.constructor.apply(this, [config]); +}; + +ANNOTATIONPEN.NAME = "annotationpen"; +ANNOTATIONPEN.ATTRS = {}; + +Y.extend(ANNOTATIONPEN, M.assignfeedback_editpdf.annotation, { + /** + * Draw a pen annotation + * @protected + * @method draw + * @return M.assignfeedback_editpdf.drawable + */ + draw : function() { + var drawable, + shape, + first, + positions, + xy; + + drawable = new M.assignfeedback_editpdf.drawable(this.editor); + + shape = this.editor.graphic.addShape({ + type: Y.Path, + fill: false, + stroke: { + weight: STROKEWEIGHT, + color: ANNOTATIONCOLOUR[this.colour] + } + }); + + first = true; + // Recreate the pen path array. + positions = this.path.split(':'); + // Redraw all the lines. + Y.each(positions, function(position) { + xy = position.split(','); + if (first) { + shape.moveTo(xy[0], xy[1]); + first = false; + } else { + shape.lineTo(xy[0], xy[1]); + } + }, this); + + shape.end(); + + drawable.shapes.push(shape); + this.drawable = drawable; + + return ANNOTATIONPEN.superclass.draw.apply(this); + }, + + /** + * Draw the in progress edit. + * + * @public + * @method draw_current_edit + * @param M.assignfeedback_editpdf.edit edit + */ + draw_current_edit : function(edit) { + var drawable = new M.assignfeedback_editpdf.drawable(this.editor), + shape, + first; + + shape = this.editor.graphic.addShape({ + type: Y.Path, + fill: false, + stroke: { + weight: STROKEWEIGHT, + color: ANNOTATIONCOLOUR[edit.annotationcolour] + } + }); + + first = true; + // Recreate the pen path array. + // Redraw all the lines. + Y.each(edit.path, function(position) { + if (first) { + shape.moveTo(position.x, position.y); + first = false; + } else { + shape.lineTo(position.x, position.y); + } + }, this); + + shape.end(); + + drawable.shapes.push(shape); + + return drawable; + }, + + + /** + * Promote the current edit to a real annotation. + * + * @public + * @method init_from_edit + * @param M.assignfeedback_editpdf.edit edit + */ + init_from_edit : function(edit) { + var bounds = new M.assignfeedback_editpdf.rect(), + pathlist = [], + i = 0; + + // This will get the boundaries of all points in the path. + bounds.bound(edit.path); + + for (i = 0; i < edit.path.length; i++) { + pathlist.push(parseInt(edit.path[i].x, 10) + ',' + parseInt(edit.path[i].y, 10)); + } + + this.gradeid = this.editor.get('gradeid'); + this.pageno = this.editor.currentpage; + this.x = bounds.x; + this.y = bounds.y; + this.endx = bounds.x + bounds.width; + this.endy = bounds.y + bounds.height; + this.colour = edit.annotationcolour; + this.path = pathlist.join(':'); + } + + +}); + +M.assignfeedback_editpdf = M.assignfeedback_editpdf || {}; +M.assignfeedback_editpdf.annotationpen = ANNOTATIONPEN; diff --git a/mod/assign/feedback/editpdf/yui/src/editor/js/annotationrectangle.js b/mod/assign/feedback/editpdf/yui/src/editor/js/annotationrectangle.js new file mode 100644 index 00000000000..368a74c64e4 --- /dev/null +++ b/mod/assign/feedback/editpdf/yui/src/editor/js/annotationrectangle.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 . + +/** + * Class representing a rectangle. + * + * @namespace M.assignfeedback_editpdf + * @class annotationrectangle + * @extends annotation + * @module moodle-assignfeedback_editpdf-editor + */ +ANNOTATIONRECTANGLE = function(config) { + ANNOTATIONRECTANGLE.superclass.constructor.apply(this, [config]); +}; + +ANNOTATIONRECTANGLE.NAME = "annotationrectangle"; +ANNOTATIONRECTANGLE.ATTRS = {}; + +Y.extend(ANNOTATIONRECTANGLE, M.assignfeedback_editpdf.annotation, { + /** + * Draw a rectangle annotation + * @protected + * @method draw + * @return M.assignfeedback_editpdf.drawable + */ + draw : function() { + var drawable, + shape; + + drawable = new M.assignfeedback_editpdf.drawable(this.editor); + + bounds = new M.assignfeedback_editpdf.rect(); + bounds.bound([new M.assignfeedback_editpdf.point(this.x, this.y), + new M.assignfeedback_editpdf.point(this.endx, this.endy)]); + + shape = this.editor.graphic.addShape({ + type: Y.Rect, + width: bounds.width, + height: bounds.height, + stroke: { + weight: STROKEWEIGHT, + color: ANNOTATIONCOLOUR[this.colour] + }, + x: bounds.x, + y: bounds.y + }); + drawable.shapes.push(shape); + this.drawable = drawable; + + return ANNOTATIONRECTANGLE.superclass.draw.apply(this); + }, + + /** + * Draw the in progress edit. + * + * @public + * @method draw_current_edit + * @param M.assignfeedback_editpdf.edit edit + */ + draw_current_edit : function(edit) { + var drawable = new M.assignfeedback_editpdf.drawable(this.editor), + shape, + bounds; + + bounds = new M.assignfeedback_editpdf.rect(); + bounds.bound([new M.assignfeedback_editpdf.point(edit.start.x, edit.start.y), + new M.assignfeedback_editpdf.point(edit.end.x, edit.end.y)]); + + shape = this.editor.graphic.addShape({ + type: Y.Rect, + width: bounds.width, + height: bounds.height, + stroke: { + weight: STROKEWEIGHT, + color: ANNOTATIONCOLOUR[edit.annotationcolour] + }, + x: bounds.x, + y: bounds.y + }); + + drawable.shapes.push(shape); + + return drawable; + } +}); + +M.assignfeedback_editpdf = M.assignfeedback_editpdf || {}; +M.assignfeedback_editpdf.annotationrectangle = ANNOTATIONRECTANGLE; diff --git a/mod/assign/feedback/editpdf/yui/src/editor/js/annotationstamp.js b/mod/assign/feedback/editpdf/yui/src/editor/js/annotationstamp.js new file mode 100644 index 00000000000..5b816f52719 --- /dev/null +++ b/mod/assign/feedback/editpdf/yui/src/editor/js/annotationstamp.js @@ -0,0 +1,158 @@ +// 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 . + +/** + * Class representing a stamp. + * + * @namespace M.assignfeedback_editpdf + * @class annotationstamp + * @extends annotation + * @module moodle-assignfeedback_editpdf-editor + */ +ANNOTATIONSTAMP = function(config) { + ANNOTATIONSTAMP.superclass.constructor.apply(this, [config]); +}; + +ANNOTATIONSTAMP.NAME = "annotationstamp"; +ANNOTATIONSTAMP.ATTRS = {}; + +Y.extend(ANNOTATIONSTAMP, M.assignfeedback_editpdf.annotation, { + /** + * Draw a stamp annotation + * @protected + * @method draw + * @return M.assignfeedback_editpdf.drawable + */ + draw : function() { + var drawable = new M.assignfeedback_editpdf.drawable(this.editor), + drawingregion = Y.one(SELECTOR.DRAWINGREGION), + node, + position; + + position = this.editor.get_window_coordinates(new M.assignfeedback_editpdf.point(this.x, this.y)); + node = Y.Node.create('
'); + node.setStyles({ + 'display': 'inline-block', + 'backgroundImage': 'url(' + this.path + ')', + 'width': (this.endx - this.x), + 'height': (this.endy - this.y), + 'backgroundSize': '100%', + 'zIndex': 50 + }); + + drawingregion.append(node); + node.setX(position.x); + node.setY(position.y); + + // Pass throught the event handlers on the div. + node.on('gesturemovestart', this.editor.edit_start, null, this.editor); + node.on('gesturemove', this.editor.edit_move, null, this.editor); + node.on('gesturemoveend', this.editor.edit_end, null, this.editor); + + drawable.nodes.push(node); + + this.drawable = drawable; + return ANNOTATIONSTAMP.superclass.draw.apply(this); + }, + + /** + * Draw the in progress edit. + * + * @public + * @method draw_current_edit + * @param M.assignfeedback_editpdf.edit edit + */ + draw_current_edit : function(edit) { + var bounds = new M.assignfeedback_editpdf.rect(), + drawable = new M.assignfeedback_editpdf.drawable(this.editor), + drawingregion = Y.one(SELECTOR.DRAWINGREGION), + node, + position; + + bounds.bound([edit.start, edit.end]); + position = this.editor.get_window_coordinates(new M.assignfeedback_editpdf.point(bounds.x, bounds.y)); + + node = Y.Node.create('
'); + node.setStyles({ + 'display': 'inline-block', + 'backgroundImage': 'url(' + edit.stamp + ')', + 'width': bounds.width, + 'height': bounds.height, + 'backgroundSize': '100%', + 'zIndex': 50 + }); + + drawingregion.append(node); + node.setX(position.x); + node.setY(position.y); + + drawable.nodes.push(node); + + return drawable; + }, + + /** + * Promote the current edit to a real annotation. + * + * @public + * @method init_from_edit + * @param M.assignfeedback_editpdf.edit edit + */ + init_from_edit : function(edit) { + var bounds = new M.assignfeedback_editpdf.rect(); + bounds.bound([edit.start, edit.end]); + + if (bounds.width < 40) { + bounds.width = 40; + } + if (bounds.height < 40) { + bounds.height = 40; + } + this.gradeid = this.editor.get('gradeid'); + this.pageno = this.editor.currentpage; + this.x = bounds.x; + this.y = bounds.y; + this.endx = bounds.x + bounds.width; + this.endy = bounds.y + bounds.height; + this.colour = edit.annotationcolour; + this.path = edit.stamp; + }, + + /** + * Move an annotation to a new location. + * @public + * @param int newx + * @param int newy + * @method move_annotation + */ + move : function(newx, newy) { + var diffx = newx - this.x, + diffy = newy - this.y; + + this.x += diffx; + this.y += diffy; + this.endx += diffx; + this.endy += diffy; + + if (this.drawable) { + this.drawable.erase(); + } + this.editor.drawables.push(this.draw()); + } + +}); + +M.assignfeedback_editpdf = M.assignfeedback_editpdf || {}; +M.assignfeedback_editpdf.annotationstamp = ANNOTATIONSTAMP; diff --git a/mod/assign/feedback/editpdf/yui/src/editor/js/stamppicker.js b/mod/assign/feedback/editpdf/yui/src/editor/js/stamppicker.js new file mode 100644 index 00000000000..71aad62843f --- /dev/null +++ b/mod/assign/feedback/editpdf/yui/src/editor/js/stamppicker.js @@ -0,0 +1,105 @@ +var STAMPPICKER_NAME = "Colourpicker", + STAMPPICKER; + +/** + * STAMPPICKER + * This is a drop down list of stamps. + * + * @namespace M.assignfeedback_editpdf.stamppicker + * @class dropdown + * @constructor + * @extends Y.Base + */ +STAMPPICKER = function(config) { + STAMPPICKER.superclass.constructor.apply(this, [config]); +}; + +Y.extend(STAMPPICKER, M.assignfeedback_editpdf.dropdown, { + + /** + * Initialise the menu. + * + * @method initializer + * @return void + */ + initializer : function(config) { + var stamplist = Y.Node.create('