From 098f7dd438ad142cd4cf8ef96322f4b0e005bcca Mon Sep 17 00:00:00 2001 From: Frederic Massart <fred@moodle.com> Date: Wed, 11 Jun 2014 18:11:01 +0800 Subject: [PATCH 1/3] MDL-45580 assignfeedback_editpdf: Store a readonly version of the PDF --- mod/assign/feedback/editpdf/ajax.php | 18 ++- mod/assign/feedback/editpdf/ajax_progress.php | 1 + .../editpdf/classes/document_services.php | 111 ++++++++++++++++-- mod/assign/feedback/editpdf/locallib.php | 3 +- ...dle-assignfeedback_editpdf-editor-debug.js | 3 +- ...oodle-assignfeedback_editpdf-editor-min.js | 6 +- .../moodle-assignfeedback_editpdf-editor.js | 3 +- .../editpdf/yui/src/editor/js/editor.js | 3 +- 8 files changed, 128 insertions(+), 20 deletions(-) diff --git a/mod/assign/feedback/editpdf/ajax.php b/mod/assign/feedback/editpdf/ajax.php index 1451d5a7b44..8baf3ffb7f8 100644 --- a/mod/assign/feedback/editpdf/ajax.php +++ b/mod/assign/feedback/editpdf/ajax.php @@ -37,6 +37,7 @@ $action = optional_param('action', '', PARAM_ALPHANUM); $assignmentid = required_param('assignmentid', PARAM_INT); $userid = required_param('userid', PARAM_INT); $attemptnumber = required_param('attemptnumber', PARAM_INT); +$readonly = optional_param('readonly', false, PARAM_BOOL); $cm = \get_coursemodule_from_instance('assign', $assignmentid, 0, false, MUST_EXIST); $context = \context_module::instance($cm->id); @@ -53,12 +54,19 @@ if ($action == 'loadallpages') { $draft = true; if (!has_capability('mod/assign:grade', $context)) { $draft = false; + $readonly = true; // A student always sees the readonly version. require_capability('mod/assign:submit', $context); } + // Whoever is viewing the readonly version should not use the drafts, but the actual annotations. + if ($readonly) { + $draft = false; + } + $pages = document_services::get_page_images_for_attempt($assignment, $userid, - $attemptnumber); + $attemptnumber, + $readonly); $response = new stdClass(); $response->pagecount = count($pages); @@ -66,13 +74,19 @@ if ($action == 'loadallpages') { $grade = $assignment->get_user_grade($userid, true); + // The readonly files are stored in a different file area. + $filearea = document_services::PAGE_IMAGE_FILEAREA; + if ($readonly) { + $filearea = document_services::PAGE_IMAGE_READONLY_FILEAREA; + } + foreach ($pages as $id => $pagefile) { $index = count($response->pages); $page = new stdClass(); $comments = page_editor::get_comments($grade->id, $index, $draft); $page->url = moodle_url::make_pluginfile_url($context->id, 'assignfeedback_editpdf', - document_services::PAGE_IMAGE_FILEAREA, + $filearea, $grade->id, '/', $pagefile->get_filename())->out(); diff --git a/mod/assign/feedback/editpdf/ajax_progress.php b/mod/assign/feedback/editpdf/ajax_progress.php index 184a957550c..815484818cd 100644 --- a/mod/assign/feedback/editpdf/ajax_progress.php +++ b/mod/assign/feedback/editpdf/ajax_progress.php @@ -49,6 +49,7 @@ try { throw new coding_exception('grade not found'); } + // No need to handle the readonly files here, the should be already generated. $component = 'assignfeedback_editpdf'; $filearea = document_services::PAGE_IMAGE_FILEAREA; $filepath = '/'; diff --git a/mod/assign/feedback/editpdf/classes/document_services.php b/mod/assign/feedback/editpdf/classes/document_services.php index 468b1032921..7ae0b30ef02 100644 --- a/mod/assign/feedback/editpdf/classes/document_services.php +++ b/mod/assign/feedback/editpdf/classes/document_services.php @@ -42,6 +42,8 @@ class document_services { const COMBINED_PDF_FILEAREA = 'combined'; /** File area for page images */ const PAGE_IMAGE_FILEAREA = 'pages'; + /** File area for readonly page images */ + const PAGE_IMAGE_READONLY_FILEAREA = 'readonlypages'; /** Filename for combined pdf */ const COMBINED_PDF_FILENAME = 'combined.pdf'; @@ -268,9 +270,10 @@ class document_services { * @param int|\assign $assignment * @param int $userid * @param int $attemptnumber (-1 means latest attempt) + * @param bool $readonly When true we get the number of pages for the readonly version. * @return int number of pages */ - public static function page_number_for_attempt($assignment, $userid, $attemptnumber) { + public static function page_number_for_attempt($assignment, $userid, $attemptnumber, $readonly = false) { global $CFG; require_once($CFG->libdir . '/pdflib.php'); @@ -281,6 +284,19 @@ class document_services { \print_error('nopermission'); } + // When in readonly we can return the number of images in the DB because they should already exist, + // if for some reason they do not, then we proceed as for the normal version. + if ($readonly) { + $grade = $assignment->get_user_grade($userid, true, $attemptnumber); + $fs = get_file_storage(); + $files = $fs->get_directory_files($assignment->get_context()->id, 'assignfeedback_editpdf', + self::PAGE_IMAGE_READONLY_FILEAREA, $grade->id, '/'); + $pagecount = count($files); + if ($pagecount > 0) { + return $pagecount; + } + } + // Get a combined pdf file from all submitted pdf files. $file = self::get_combined_pdf_for_attempt($assignment, $userid, $attemptnumber); if (!$file) { @@ -363,12 +379,25 @@ class document_services { /** * This function returns a list of the page images from a pdf. + * + * The readonly version is different than the normal one. The readonly version contains a copy + * of the pages in the state they were when the PDF was annotated, by doing so we prevent the + * the pages that are displayed to change as soon as the submission changes. + * + * Though there is an edge case, if the PDF was annotated before MDL-45580, then it is possible + * that we do not find any readonly version of the pages. In that case, we will get the normal + * pages and copy them to the readonly area. This ensures that the pages will remain in that + * state until the submission is updated. When the normal files do not exist, we throw an exception + * because the readonly pages should only ever be displayed after a teacher has annotated the PDF, + * they would not exist until they do. + * * @param int|\assign $assignment * @param int $userid * @param int $attemptnumber (-1 means latest attempt) + * @param bool $readonly If true, then we are requesting the readonly version. * @return array(stored_file) */ - public static function get_page_images_for_attempt($assignment, $userid, $attemptnumber) { + public static function get_page_images_for_attempt($assignment, $userid, $attemptnumber, $readonly = false) { $assignment = self::get_assignment_from_param($assignment); @@ -385,26 +414,37 @@ class document_services { $contextid = $assignment->get_context()->id; $component = 'assignfeedback_editpdf'; - $filearea = self::PAGE_IMAGE_FILEAREA; $itemid = $grade->id; $filepath = '/'; + $filearea = self::PAGE_IMAGE_FILEAREA; $fs = \get_file_storage(); + // If we are after the readonly pages... + $copytoreadonly = false; + if ($readonly) { + $filearea = self::PAGE_IMAGE_READONLY_FILEAREA; + if ($fs->is_area_empty($contextid, $component, $filearea, $itemid)) { + // We have a problem here, we were supposed to find the files... + // let's fallback on the other area, and copy the files to the readonly area. + $copytoreadonly = true; + $filearea = self::PAGE_IMAGE_FILEAREA; + } + } + $files = $fs->get_directory_files($contextid, $component, $filearea, $itemid, $filepath); + $pages = array(); if (!empty($files)) { $first = reset($files); - if ($first->get_timemodified() < $submission->timemodified) { - + if (!$readonly && $first->get_timemodified() < $submission->timemodified) { + // Image files are stale - regenerate them, except in readonly mode. $fs->delete_area_files($contextid, $component, $filearea, $itemid); - // Image files are stale - regenerate them. $files = array(); } else { // Need to reorder the files following their name. // because get_directory_files() return a different order than generate_page_images_for_attempt(). - $orderedfiles = array(); foreach($files as $file) { // Extract the page number from the file name image_pageXXXX.png. preg_match('/page([\d]+)\./', $file->get_filename(), $matches); @@ -415,14 +455,26 @@ class document_services { $pagenumber = (int)$matches[1]; // Save the page in the ordered array. - $orderedfiles[$pagenumber] = $file; + $pages[$pagenumber] = $file; } - ksort($orderedfiles); + ksort($pages); - return $orderedfiles; + if ($copytoreadonly) { + self::copy_pages_to_readonly_area($assignment, $grade); + } } } - return self::generate_page_images_for_attempt($assignment, $userid, $attemptnumber); + + if (empty($pages)) { + if ($readonly) { + // This should never happen, there should be a version of the pages available + // whenever we are requesting the readonly version. + throw new \moodle_exception('Could not find readonly pages for grade ' . $grade->id); + } + $pages = self::generate_page_images_for_attempt($assignment, $userid, $attemptnumber); + } + + return $pages; } /** @@ -465,6 +517,11 @@ class document_services { /** * This function takes the combined pdf and embeds all the comments and annotations. + * + * This also moves the annotations and comments from drafts to not drafts. And it will + * copy all the images stored to the readonly area, so that they can be viewed online, and + * not be overwritten when a new submission is sent. + * * @param int|\assign $assignment * @param int $userid * @param int $attemptnumber (-1 means latest attempt) @@ -567,9 +624,41 @@ class document_services { @unlink($combined); @rmdir($tmpdir); + self::copy_pages_to_readonly_area($assignment, $grade); + return $file; } + /** + * Copy the pages image to the readonly area. + * + * @param int|\assign $assignment The assignment. + * @param \stdClass $grade The grade record. + * @return void + */ + public static function copy_pages_to_readonly_area($assignment, $grade) { + $fs = get_file_storage(); + $assignment = self::get_assignment_from_param($assignment); + $contextid = $assignment->get_context()->id; + $component = 'assignfeedback_editpdf'; + $itemid = $grade->id; + + // Get all the pages. + $originalfiles = $fs->get_area_files($contextid, $component, self::PAGE_IMAGE_FILEAREA, $itemid); + if (empty($originalfiles)) { + // Nothing to do here... + return; + } + + // Delete the old readonly files. + $fs->delete_area_files($contextid, $component, self::PAGE_IMAGE_READONLY_FILEAREA, $itemid); + + // Do the copying. + foreach ($originalfiles as $originalfile) { + $fs->create_file_from_storedfile(array('filearea' => self::PAGE_IMAGE_READONLY_FILEAREA), $originalfile); + } + } + /** * This function returns the generated pdf (if it exists). * @param int|\assign $assignment diff --git a/mod/assign/feedback/editpdf/locallib.php b/mod/assign/feedback/editpdf/locallib.php index f98bee9cd2e..be4a8a83bd6 100644 --- a/mod/assign/feedback/editpdf/locallib.php +++ b/mod/assign/feedback/editpdf/locallib.php @@ -137,7 +137,8 @@ class assign_feedback_editpdf extends assign_feedback_plugin { // Retrieve total number of pages. $pagetotal = document_services::page_number_for_attempt($this->assignment->get_instance()->id, $userid, - $attempt); + $attempt, + $readonly); $widget = new assignfeedback_editpdf_widget($this->assignment->get_instance()->id, $userid, diff --git a/mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-debug.js b/mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-debug.js index 05b1dc5f200..3f4859a2a37 100644 --- a/mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-debug.js +++ b/mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-debug.js @@ -3351,7 +3351,8 @@ EDITOR.prototype = { action : 'loadallpages', userid : this.get('userid'), attemptnumber : this.get('attemptnumber'), - assignmentid : this.get('assignmentid') + assignmentid : this.get('assignmentid'), + readonly : this.get('readonly') ? 1 : 0 }, on: { success: function(tid, response) { diff --git a/mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-min.js b/mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-min.js index e03ddc03d0a..5e8c6bf3b2e 100644 --- a/mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-min.js +++ b/mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-min.js @@ -3,6 +3,6 @@ YUI.add("moodle-assignfeedback_editpdf-editor",function(e,t){var n=M.cfg.wwwroot ,this.endy)]),i=c[this.colour],i=i.replace("rgb","rgba"),i=i.replace(")",",0.5)"),n=this.editor.graphic.addShape({type:e.Rect,width:r.width,height:r.height,stroke:!1,fill:{color:i},x:r.x,y:r.y}),t.shapes.push(n),this.drawable=t,ANNOTATIONHIGHLIGHT.superclass.draw.apply(this)},draw_current_edit:function(t){var n=new M.assignfeedback_editpdf.drawable(this.editor),r,i,s;return i=new M.assignfeedback_editpdf.rect,i.bound([new M.assignfeedback_editpdf.point(t.start.x,t.start.y),new M.assignfeedback_editpdf.point(t.end.x,t.end.y)]),i.has_min_width()||i.set_min_width(),s=c[t.annotationcolour],s=s.replace("rgb","rgba"),s=s.replace(")",",0.5)"),r=this.editor.graphic.addShape({type:e.Rect,width:i.width,height:16,stroke:!1,fill:{color:s},x:i.x,y:t.start.y}),n.shapes.push(r),n},init_from_edit:function(e){var t=new M.assignfeedback_editpdf.rect;return t.bound([e.start,e.end]),this.gradeid=this.editor.get("gradeid"),this.pageno=this.editor.currentpage,this.x=t.x,this.y=e.start.y,this.endx=t.x+t.width,this.endy=e.start.y+16,this.colour=e.annotationcolour,this.page="",t.has_min_width()}}),M.assignfeedback_editpdf=M.assignfeedback_editpdf||{},M.assignfeedback_editpdf.annotationhighlight=ANNOTATIONHIGHLIGHT,ANNOTATIONSTAMP=function(e){ANNOTATIONSTAMP.superclass.constructor.apply(this,[e])},ANNOTATIONSTAMP.NAME="annotationstamp",ANNOTATIONSTAMP.ATTRS={},e.extend(ANNOTATIONSTAMP,M.assignfeedback_editpdf.annotation,{draw:function(){var t=new M.assignfeedback_editpdf.drawable(this.editor),n=e.one(o.DRAWINGREGION),r,i;return i=this.editor.get_window_coordinates(new M.assignfeedback_editpdf.point(this.x,this.y)),r=e.Node.create("<div/>"),r.setStyles({position:"absolute",display:"inline-block",backgroundImage:"url("+this.editor.get_stamp_image_url(this.path)+")",width:this.endx-this.x,height:this.endy-this.y,backgroundSize:"100% 100%",zIndex:50}),n.append(r),r.setX(i.x),r.setY(i.y),r.on("gesturemovestart",this.editor.edit_start,null,this.editor),r.on("gesturemove",this.editor.edit_move,null,this.editor),r.on("gesturemoveend",this.editor.edit_end,null,this.editor),t.nodes.push(r),this.drawable=t,ANNOTATIONSTAMP.superclass.draw.apply(this)},draw_current_edit:function(t){var n=new M.assignfeedback_editpdf.rect,r=new M.assignfeedback_editpdf.drawable(this.editor),i=e.one(o.DRAWINGREGION),s,u;return n.bound([t.start,t.end]),u=this.editor.get_window_coordinates(new M.assignfeedback_editpdf.point(n.x,n.y)),s=e.Node.create("<div/>"),s.setStyles({position:"absolute",display:"inline-block",backgroundImage:"url("+this.editor.get_stamp_image_url(t.stamp)+")",width:n.width,height:n.height,backgroundSize:"100% 100%",zIndex:50}),i.append(s),s.setX(u.x),s.setY(u.y),r.nodes.push(s),r},init_from_edit:function(e){var t=new M.assignfeedback_editpdf.rect;return t.bound([e.start,e.end]),t.width<40&&(t.width=40),t.height<40&&(t.height=40),this.gradeid=this.editor.get("gradeid"),this.pageno=this.editor.currentpage,this.x=t.x,this.y=t.y,this.endx=t.x+t.width,this.endy=t.y+t.height,this.colour=e.annotationcolour,this.path=e.stamp,!0},move:function(e,t){var n=e-this.x,r=t-this.y;this.x+=n,this.y+=r,this.endx+=n,this.endy+=r,this.drawable&&this.drawable.erase(),this.editor.drawables.push(this.draw())}}),M.assignfeedback_editpdf=M.assignfeedback_editpdf||{},M.assignfeedback_editpdf.annotationstamp=ANNOTATIONSTAMP;var v="Dropdown menu",m;m=function(e){e.draggable=!1,e.centered=!1,e.width="auto",e.visible=!1,e.footerContent="",m.superclass.constructor.apply(this,[e])},e.extend(m,M.core.dialogue,{initializer:function(t){var n,r,i,s;m.superclass.initializer.call(this,t),s=this.get("boundingBox"),s.addClass("assignfeedback_editpdf_dropdown"),n=this.get("buttonNode"),r=this.bodyNode,i=e.Node.create("<h3/>"),i.addClass("accesshide"),i.setHTML(this.get("headerText")),r.prepend(i),r.on("clickoutside",function(e){this.get("visible")&&e.target.get("id")!==n.get("id")&&e.target.ancestor().get("id")!==n.get("id")&&(e.preventDefault(),this.hide())},this),n.on("click",function(e){e.preventDefault(),this.show()},this),n.on("key",this.show,"enter,space",this)},show:function(){var t=this.get("buttonNode");result=m.superclass.show.call(this),this.align(t,[e.WidgetPositionAlign.TL,e.WidgetPositionAlign.BL])}},{NAME:v,ATTRS:{headerText:{value:""},buttonNode:{value:null}}}),e.Base.modifyAttrs(m,{modal:{getter:function(){return!1}}}),M.assignfeedback_editpdf=M.assignfeedback_editpdf||{},M.assignfeedback_editpdf.dropdown=m;var g="Colourpicker",b;b=function(e){b.superclass.constructor.apply(this,[e])},e.extend(b,M.assignfeedback_editpdf.dropdown,{initializer:function(t){var n=e.Node.create('<ul role="menu" class="assignfeedback_editpdf_menu"/>'),r;e.each(this.get("colours"),function(t,r){var i,s,o,u,a;o=M.util.get_string(r,"assignfeedback_editpdf"),a=this.get("iconprefix")+r,u=M.util.image_url(a,"assignfeedback_editpdf"),i=e.Node.create('<button><img alt="'+o+'" src="'+u+'"/></button>'),i.setAttribute("data-colour",r),i.setAttribute("data-rgb",t),i.setStyle("backgroundImage","none"),s=e.Node.create("<li/>"),s.append(i),n.append(s)},this),r=e.Node.create("<div/>"),n.delegate("click",this.callback_handler,"button",this),n.delegate("key",this.callback_handler,"down:13","button",this),this.set("headerText",M.util.get_string("colourpicker","assignfeedback_editpdf")),r.append(n),this.set("bodyContent",r),b.superclass.initializer.call(this,t)},callback_handler:function(t){t.preventDefault();var n=this.get("callback"),r=this.get("context"),i;this.hide(),i=e.bind(n,r,t),i()}},{NAME:g,ATTRS:{colours:{value:{}},callback:{value:null},context:{value:null},iconprefix:{value:"colour_"}}}),M.assignfeedback_editpdf=M.assignfeedback_editpdf||{},M.assignfeedback_editpdf.colourpicker=b;var w="Colourpicker",E;E=function(e){E.superclass.constructor.apply(this,[e])},e.extend(E,M.assignfeedback_editpdf.dropdown,{initializer:function(t){var n=e.Node.create('<ul role="menu" class="assignfeedback_editpdf_menu"/>');e.each(this.get("stamps"),function(t){var r,i,s;s=M.util.get_string("stamp","assignfeedback_editpdf" ),r=e.Node.create('<button><img height="16" width="16" alt="'+s+'" src="'+t+'"/></button>'),r.setAttribute("data-stamp",t),r.setStyle("backgroundImage","none"),i=e.Node.create("<li/>"),i.append(r),n.append(i)},this),n.delegate("click",this.callback_handler,"button",this),n.delegate("key",this.callback_handler,"down:13","button",this),this.set("headerText",M.util.get_string("stamppicker","assignfeedback_editpdf")),this.set("bodyContent",n),E.superclass.initializer.call(this,t)},callback_handler:function(t){t.preventDefault();var n=this.get("callback"),r=this.get("context"),i;this.hide(),i=e.bind(n,r,t),i()}},{NAME:w,ATTRS:{stamps:{value:[]},callback:{value:null},context:{value:null}}}),M.assignfeedback_editpdf=M.assignfeedback_editpdf||{},M.assignfeedback_editpdf.stamppicker=E;var S="Commentmenu",T;T=function(e){T.superclass.constructor.apply(this,[e])},e.extend(T,M.assignfeedback_editpdf.dropdown,{initializer:function(t){var n,r,i,s;s=this.get("comment"),n=e.Node.create('<ul role="menu" class="assignfeedback_editpdf_menu"/>'),r=e.Node.create('<li><a tabindex="-1" href="#">'+M.util.get_string("addtoquicklist","assignfeedback_editpdf")+"</a></li>"),r.on("click",s.add_to_quicklist,s),r.on("key",s.add_to_quicklist,"enter,space",s),n.append(r),r=e.Node.create('<li><a tabindex="-1" href="#">'+M.util.get_string("deletecomment","assignfeedback_editpdf")+"</a></li>"),r.on("click",function(e){e.preventDefault(),this.menu.hide(),this.remove()},s),r.on("key",function(){s.menu.hide(),s.remove()},"enter,space",s),n.append(r),r=e.Node.create("<li><hr/></li>"),n.append(r),this.set("headerText",M.util.get_string("commentcontextmenu","assignfeedback_editpdf")),i=e.Node.create("<div/>"),i.append(n),this.set("bodyContent",i),T.superclass.initializer.call(this,t)},show:function(){var t=this.get("boundingBox").one("ul");t.all(".quicklist_comment").remove(!0),comment=this.get("comment"),comment.deleteme=!1,e.each(comment.editor.quicklist.comments,function(n){var r=e.Node.create('<li class="quicklist_comment"></li>'),i=e.Node.create('<a href="#" tabindex="-1">'+n.rawtext+"</a>"),s=e.Node.create('<a href="#" tabindex="-1" class="delete_quicklist_comment"><img src="'+M.util.image_url("t/delete","core")+'" '+'alt="'+M.util.get_string("deletecomment","assignfeedback_editpdf")+'"/>'+"</a>");r.append(i),r.append(s),t.append(r),i.on("click",comment.set_from_quick_comment,comment,n),i.on("key",comment.set_from_quick_comment,"space,enter",comment,n),s.on("click",comment.remove_from_quicklist,comment,n),s.on("key",comment.remove_from_quicklist,"space,enter",comment,n)},this),T.superclass.show.call(this)}},{NAME:S,ATTRS:{comment:{value:null}}}),M.assignfeedback_editpdf=M.assignfeedback_editpdf||{},M.assignfeedback_editpdf.commentmenu=T;var N="commentsearch",C;C=function(e){e.draggable=!1,e.centered=!0,e.width="400px",e.visible=!1,e.headerContent=M.util.get_string("searchcomments","assignfeedback_editpdf"),e.footerContent="",C.superclass.constructor.apply(this,[e])},e.extend(C,M.core.dialogue,{initializer:function(t){var n,r,i,s,o,u;u=this.get("boundingBox"),u.addClass("assignfeedback_editpdf_commentsearch"),n=this.get("editor"),r=e.Node.create("<div/>"),i=M.util.get_string("filter","assignfeedback_editpdf"),s=e.Node.create('<input type="text" size="20" placeholder="'+i+'"/>'),r.append(s),o=e.Node.create('<ul role="menu" class="assignfeedback_editpdf_menu"/>'),r.append(o),s.on("keyup",this.filter_search_comments,null,this),o.delegate("click",this.focus_on_comment,"a",this),o.delegate("key",this.focus_on_comment,"enter,space","a",this),this.set("bodyContent",r),C.superclass.initializer.call(this,t)},filter_search_comments:function(){var t,n,r;t=e.one(o.SEARCHFILTER),n=e.one(o.SEARCHCOMMENTSLIST),r=t.get("value"),n.all("li").each(function(e){e.get("text").indexOf(r)!==-1?e.show():e.hide()})},focus_on_comment:function(e){e.preventDefault();var t=e.target.ancestor("li"),n=t.getData("comment"),r=this.get("editor");this.hide(),n.pageno===r.currentpage?n.drawable.nodes[0].one("textarea").focus():(r.currentpage=n.pageno,r.change_page(),n.drawable.nodes[0].one("textarea").focus())},show:function(){var t=this.get("boundingBox").one("ul"),n=this.get("editor");t.all("li").remove(!0),e.each(n.pages,function(n){e.each(n.comments,function(n){var r=e.Node.create('<li><a href="#" tabindex="-1"><pre>'+n.rawtext+"</pre></a></li>");t.append(r),r.setData("comment",n)},this)},this),this.centerDialogue(),C.superclass.show.call(this)}},{NAME:N,ATTRS:{editor:{value:null}}}),e.Base.modifyAttrs(C,{modal:{getter:function(){return!0}}}),M.assignfeedback_editpdf=M.assignfeedback_editpdf||{},M.assignfeedback_editpdf.commentsearch=C,COMMENT=function(t,n,r,i,s,u,a,c){this.editor=t,this.gradeid=n||0,this.x=parseInt(i,10)||0,this.y=parseInt(s,10)||0,this.width=parseInt(u,10)||0,this.rawtext=c||"",this.pageno=r||0,this.colour=a||"yellow",this.drawable=!1,this.deleteme=!1,this.menulink=null,this.menu=null,this.clean=function(){return{gradeid:this.gradeid,x:parseInt(this.x,10),y:parseInt(this.y,10),width:parseInt(this.width,10),rawtext:this.rawtext,pageno:this.currentpage,colour:this.colour}},this.draw=function(t){var n=new M.assignfeedback_editpdf.drawable(this.editor),r,i=e.one(o.DRAWINGREGION),s,u,a,c;return r=e.Node.create("<textarea/>"),s=e.Node.create('<div class="commentdrawable"/>'),u=e.Node.create('<a href="#"><img src="'+M.util.image_url("t/contextmenu","core")+'"/></a>'),this.menulink=u,s.append(r),this.editor.get("readonly")?r.setAttribute("readonly","readonly"):s.append(u),this.width<100&&(this.width=100),a=this.editor.get_window_coordinates(new M.assignfeedback_editpdf.point(this.x,this.y)),r.setStyles({width:this.width+"px",backgroundColor:l[this.colour],color:f}),i.append(s),s.setStyle("position","absolute"),s.setX(a.x),s.setY(a.y),n.nodes.push(s),r.set("value",this.rawtext),c=r.get("scrollHeight"),r.setStyles({height:c+"px",overflow:"hidden"}),this.editor.get("readonly")||this.attach_events(r,u),t&&r.focus(),this.drawable=n,n},this.delete_comment_later= function(){this.deleteme&&this.remove()},this.attach_events=function(t,n){t.on("blur",function(){this.rawtext=t.get("value"),this.width=parseInt(t.getStyle("width"),10),this.rawtext.replace(/^\s+|\s+$/g,"")===""&&(this.deleteme=!0,e.later(400,this,this.delete_comment_later)),this.editor.save_current_page(),this.editor.editingcomment=!1},this),n.setData("comment",this),t.on("keyup",function(){var e=t.get("scrollHeight"),n=parseInt(t.getStyle("height"),10);e===n+8&&(e-=8),t.setStyle("height",e+"px")}),t.on("gesturemovestart",function(e){t.setData("dragging",!0),t.setData("offsetx",e.clientX-t.getX()),t.setData("offsety",e.clientY-t.getY())}),t.on("gesturemoveend",function(){t.setData("dragging",!1),this.editor.save_current_page()},null,this),t.on("gesturemove",function(e){var n=e.clientX-t.getData("offsetx"),r=e.clientY-t.getData("offsety"),i,s,o,u,a;i=parseInt(t.getStyle("width"),10),s=parseInt(t.getStyle("height"),10),o=this.editor.get_canvas_coordinates(new M.assignfeedback_editpdf.point(n,r)),a=this.editor.get_canvas_bounds(!0),a.x=0,a.y=0,a.width-=i+42,a.height-=s+8,o.clip(a),this.x=o.x,this.y=o.y,u=this.editor.get_window_coordinates(o),t.ancestor().setX(u.x),t.ancestor().setY(u.y)},null,this),this.menu=new M.assignfeedback_editpdf.commentmenu({buttonNode:this.menulink,comment:this})},this.remove=function(){var e=0,t;t=this.editor.pages[this.editor.currentpage].comments;for(e=0;e<t.length;e++)if(t[e]===this){t.splice(e,1),this.drawable.erase(),this.editor.save_current_page();return}},this.remove_from_quicklist=function(e,t){e.preventDefault(),this.menu.hide(),this.editor.quicklist.remove(t)},this.set_from_quick_comment=function(e,t){e.preventDefault(),this.menu.hide(),this.rawtext=t.rawtext,this.width=t.width,this.colour=t.colour,this.editor.save_current_page(),this.editor.redraw()},this.add_to_quicklist=function(e){e.preventDefault(),this.menu.hide(),this.editor.quicklist.add(this)},this.draw_current_edit=function(t){var n=new M.assignfeedback_editpdf.drawable(this.editor),r,i;return i=new M.assignfeedback_editpdf.rect,i.bound([t.start,t.end]),r=this.editor.graphic.addShape({type:e.Rect,width:i.width,height:i.height,fill:{color:l[t.commentcolour]},x:i.x,y:i.y}),n.shapes.push(r),n},this.init_from_edit=function(e){var t=new M.assignfeedback_editpdf.rect;return t.bound([e.start,e.end]),t.width<100&&(t.width=100),this.gradeid=this.editor.get("gradeid"),this.pageno=this.editor.currentpage,this.x=t.x,this.y=t.y,this.width=t.width,this.colour=e.commentcolour,this.rawtext="",t.has_min_width()&&t.has_min_height()}},M.assignfeedback_editpdf=M.assignfeedback_editpdf||{},M.assignfeedback_editpdf.comment=COMMENT,QUICKCOMMENT=function(e,t,n,r){this.rawtext=t||"",this.id=e||0,this.width=n||100,this.colour=r||"yellow"},M.assignfeedback_editpdf=M.assignfeedback_editpdf||{},M.assignfeedback_editpdf.quickcomment=QUICKCOMMENT,QUICKCOMMENTLIST=function(t){this.editor=t,this.comments=[],this.add=function(t){var r=n,i;if(t.rawtext==="")return;i={method:"post",context:this,sync:!1,data:{sesskey:M.cfg.sesskey,action:"addtoquicklist",userid:this.editor.get("userid"),commenttext:t.rawtext,width:t.width,colour:t.colour,attemptnumber:this.editor.get("attemptnumber"),assignmentid:this.editor.get("assignmentid")},on:{success:function(t,n){var r,i;try{r=e.JSON.parse(n.responseText);if(r.error)return new M.core.ajaxException(r);i=new M.assignfeedback_editpdf.quickcomment(r.id,r.rawtext,r.width,r.colour),this.comments.push(i)}catch(s){return new M.core.exception(s)}},failure:function(e,t){return M.core.exception(t.responseText)}}},e.io(r,i)},this.remove=function(t){var r=n,i;if(!t)return;i={method:"post",context:this,sync:!1,data:{sesskey:M.cfg.sesskey,action:"removefromquicklist",userid:this.editor.get("userid"),commentid:t.id,attemptnumber:this.editor.get("attemptnumber"),assignmentid:this.editor.get("assignmentid")},on:{success:function(){var e;e=this.comments.indexOf(t),e>=0&&this.comments.splice(e,1)},failure:function(e,t){return M.core.exception(t.responseText)}}},e.io(r,i)},this.load=function(){var t=n,r;r={method:"get",context:this,sync:!1,data:{sesskey:M.cfg.sesskey,action:"loadquicklist",userid:this.editor.get("userid"),attemptnumber:this.editor.get("attemptnumber"),assignmentid:this.editor.get("assignmentid")},on:{success:function(t,n){var r;try{r=e.JSON.parse(n.responseText);if(r.error)return new M.core.ajaxException(r);e.each(r,function(e){var t=new M.assignfeedback_editpdf.quickcomment(e.id,e.rawtext,e.width,e.colour);this.comments.push(t)},this)}catch(i){return new M.core.exception(i)}},failure:function(e,t){return M.core.exception(t.responseText)}}},e.io(t,r)}},M.assignfeedback_editpdf=M.assignfeedback_editpdf||{},M.assignfeedback_editpdf.quickcommentlist=QUICKCOMMENTLIST,EDITOR=function(){EDITOR.superclass.constructor.apply(this,arguments)},EDITOR.prototype={dialogue:null,pagecount:0,currentpage:0,pages:[],loadingicon:null,pageimage:null,graphic:null,currentedit:new M.assignfeedback_editpdf.edit,currentdrawable:!1,drawables:[],currentcomment:null,currentannotation:null,lastanntationtool:"pen",quicklist:null,searchcommentswindow:null,currentstamp:null,stamps:[],editingcomment:!1,initializer:function(){var t;t=e.one("#"+this.get("linkid")),t&&(t.on("click",this.link_handler,this),t.on("key",this.link_handler,"down:13",this),this.currentedit.start=!1,this.currentedit.end=!1,this.get("readonly")||(this.quicklist=new M.assignfeedback_editpdf.quickcommentlist(this)))},refresh_button_state:function(){var t,n,r;t=e.one(o.COMMENTCOLOURBUTTON),r=M.util.image_url("background_colour_"+this.currentedit.commentcolour,"assignfeedback_editpdf"),t.one("img").setAttribute("src",r),this.currentedit.commentcolour==="clear"?t.one("img").setStyle("borderStyle","dashed"):t.one("img").setStyle("borderStyle","solid"),t=e.one(o.ANNOTATIONCOLOURBUTTON),r=M.util.image_url("colour_"+this.currentedit.annotationcolour,"assignfeedback_editpdf"),t.one("img").setAttribute("src",r),n=e.one(p[this.currentedit.tool]),n.addClass("assignfeedback_editpdf_selectedbutton" -),n.setAttribute("aria-pressed","true"),t=e.one(o.STAMPSBUTTON),t.one("img").setAttrs({src:this.get_stamp_image_url(this.currentedit.stamp),height:"16",width:"16"})},get_canvas_bounds:function(){var t=e.one(o.DRAWINGCANVAS),n=t.getXY(),r=n[0],i=n[1],s=parseInt(t.getStyle("width"),10),u=parseInt(t.getStyle("height"),10);return new M.assignfeedback_editpdf.rect(r,i,s,u)},get_canvas_coordinates:function(e){var t=this.get_canvas_bounds(),n=new M.assignfeedback_editpdf.point(e.x-t.x,e.y-t.y);return t.x=t.y=0,n.clip(t),n},get_window_coordinates:function(e){var t=this.get_canvas_bounds(),n=new M.assignfeedback_editpdf.point(e.x+t.x,e.y+t.y);return n},link_handler:function(t){var n;t.preventDefault(),this.dialogue||(this.dialogue=new M.core.dialogue({headerContent:this.get("header"),bodyContent:this.get("body"),footerContent:this.get("footer"),modal:!0,width:"840px",visible:!1,draggable:!0}),this.dialogue.get("boundingBox").addClass(s.DIALOGUE),this.loadingicon=e.one(o.LOADINGICON),n=e.one(o.DRAWINGCANVAS),this.graphic=new e.Graphic({render:o.DRAWINGCANVAS}),this.get("readonly")||(n.on("gesturemovestart",this.edit_start,null,this),n.on("gesturemove",this.edit_move,null,this),n.on("gesturemoveend",this.edit_end,null,this),this.refresh_button_state()),this.load_all_pages()),this.dialogue.centerDialogue(),this.dialogue.show()},load_all_pages:function(){var t=n,i,s,u;i={method:"get",context:this,sync:!1,data:{sesskey:M.cfg.sesskey,action:"loadallpages",userid:this.get("userid"),attemptnumber:this.get("attemptnumber"),assignmentid:this.get("assignmentid")},on:{success:function(e,t){this.all_pages_loaded(t.responseText)},failure:function(e,t){return new M.core.exception(t.responseText)}}},e.io(t,i),this.pagecount<=0&&(s={method:"get",context:this,sync:!1,data:{sesskey:M.cfg.sesskey,action:"conversionstatus",userid:this.get("userid"),attemptnumber:this.get("attemptnumber"),assignmentid:this.get("assignmentid")},on:{success:function(t,n){u=0;if(this.pagecount===0){var i=this.get("pagetotal"),a=e.one(o.PROGRESSBARCONTAINER),f=a.one(".bar");if(f){var l=n.response/i*100;f.setStyle("width",l+"%"),a.setAttribute("aria-valuenow",l)}e.later(1e3,this,function(){e.io(r,s)})}},failure:function(t,n){return u+=1,this.pagecount===0&&u<5&&e.later(1e3,this,function(){e.io(r,s)}),new M.core.exception(n.responseText)}}},e.later(1e3,this,function(){u=0,e.io(r,s)}))},all_pages_loaded:function(t){var n,r,i,s,o;try{n=e.JSON.parse(t);if(n.error||!n.pagecount){this.dialogue.hide(),o=new M.core.alert({message:M.util.get_string("cannotopenpdf","assignfeedback_editpdf")}),o.show();return}}catch(u){this.dialogue.hide(),o=new M.core.alert({title:M.util.get_string("cannotopenpdf","assignfeedback_editpdf")}),o.show();return}this.pagecount=n.pagecount,this.pages=n.pages;for(r=0;r<this.pages.length;r++){for(i=0;i<this.pages[r].comments.length;i++)s=this.pages[r].comments[i],this.pages[r].comments[i]=new M.assignfeedback_editpdf.comment(this,s.gradeid,s.pageno,s.x,s.y,s.width,s.colour,s.rawtext);for(i=0;i<this.pages[r].annotations.length;i++)n=this.pages[r].annotations[i],this.pages[r].annotations[i]=this.create_annotation(n.type,n)}this.quicklist&&this.quicklist.load(),this.setup_navigation(),this.setup_toolbar(),this.change_page()},get_stamp_image_url:function(t){var n=this.get("stampfiles"),r="";return e.Array.each(n,function(e){e.indexOf(t)>0&&(r=e)},this),r},setup_toolbar:function(){var t,n,r,i,s,u,a,f;i=e.one(o.SEARCHCOMMENTSBUTTON),i.on("click",this.open_search_comments,this),i.on("key",this.open_search_comments,"down:13",this);if(this.get("readonly"))return;e.each(p,function(n,r){t=e.one(n),t.on("click",this.handle_tool_button,this,r),t.on("key",this.handle_tool_button,"down:13",this,r),t.setAttribute("aria-pressed","false")},this),n=e.one(o.COMMENTCOLOURBUTTON),a=new M.assignfeedback_editpdf.colourpicker({buttonNode:n,colours:l,iconprefix:"background_colour_",callback:function(e){var t=e.target.getAttribute("data-colour");t||(t=e.target.ancestor().getAttribute("data-colour")),this.currentedit.commentcolour=t,this.handle_tool_button(e,"comment")},context:this}),r=e.one(o.ANNOTATIONCOLOURBUTTON),a=new M.assignfeedback_editpdf.colourpicker({buttonNode:r,iconprefix:"colour_",colours:c,callback:function(e){var t=e.target.getAttribute("data-colour");t||(t=e.target.ancestor().getAttribute("data-colour")),this.currentedit.annotationcolour=t,this.lastannotationtool?this.handle_tool_button(e,this.lastannotationtool):this.handle_tool_button(e,"pen")},context:this}),u=this.get("stampfiles"),u.length<=0?e.one(p.stamp).ancestor().hide():(f=u[0].substr(u[0].lastIndexOf("/")+1),this.currentedit.stamp=f,s=e.one(o.STAMPSBUTTON),a=new M.assignfeedback_editpdf.stamppicker({buttonNode:s,stamps:u,callback:function(e){var t=e.target.getAttribute("data-stamp"),n;t||(t=e.target.ancestor().getAttribute("data-stamp")),n=t.substr(t.lastIndexOf("/")),this.currentedit.stamp=n,this.handle_tool_button(e,"stamp")},context:this}),this.refresh_button_state())},handle_tool_button:function(t,n){var r;t.preventDefault(),r=e.one(p[this.currentedit.tool]),r.removeClass("assignfeedback_editpdf_selectedbutton"),r.setAttribute("aria-pressed","false"),this.currentedit.tool=n,n!=="comment"&&n!=="select"&&n!=="stamp"&&(this.lastannotationtool=n),this.refresh_button_state()},stringify_current_page:function(){var t=[],n=[],r,i=0;for(i=0;i<this.pages[this.currentpage].comments.length;i++)t[i]=this.pages[this.currentpage].comments[i].clean();for(i=0;i<this.pages[this.currentpage].annotations.length;i++)n[i]=this.pages[this.currentpage].annotations[i].clean();return r={comments:t,annotations:n},e.JSON.stringify(r)},get_current_drawable:function(){var e,t,n=!1;return!this.currentedit.start||!this.currentedit.end?!1:(this.currentedit.tool==="comment"?(e=new M.assignfeedback_editpdf.comment(this),n=e.draw_current_edit(this.currentedit)):(t=this.create_annotation(this.currentedit.tool,{}),t&&(n=t.draw_current_edit(this.currentedit))),n)},redraw_current_edit:function(){this.currentdrawable&& -this.currentdrawable.erase(),this.currentdrawable=this.get_current_drawable()},edit_start:function(t){var n=e.one(o.DRAWINGCANVAS),r=n.getXY(),i=n.get("docScrollY"),s=n.get("docScrollX"),u={x:t.clientX-r[0]+s,y:t.clientY-r[1]+i},a=!1,f;if(t.button===3)return;if(this.currentedit.starttime)return;if(this.editingcomment)return;this.currentedit.starttime=(new Date).getTime(),this.currentedit.start=u,this.currentedit.end={x:u.x,y:u.y},this.currentedit.tool==="select"&&(x=this.currentedit.end.x,y=this.currentedit.end.y,annotations=this.pages[this.currentpage].annotations,e.each(annotations,function(e){(x-e.x)*(x-e.endx)<=0&&(y-e.y)*(y-e.endy)<=0&&(a=e)}),a&&(f=this.currentannotation,this.currentannotation=a,f&&f!==a&&f.drawable&&(f.drawable.erase(),this.drawables.push(f.draw())),this.currentannotation.drawable&&this.currentannotation.drawable.erase(),this.drawables.push(this.currentannotation.draw()))),this.currentannotation&&(this.currentedit.annotationstart={x:this.currentannotation.x,y:this.currentannotation.y})},edit_move:function(t){var n=this.get_canvas_bounds(),r=e.one(o.DRAWINGCANVAS),i=new M.assignfeedback_editpdf.point(t.clientX+r.get("docScrollX"),t.clientY+r.get("docScrollY")),s=this.get_canvas_coordinates(i);if(s.x<0||s.x>n.width||s.y<0||s.y>n.height)return;this.currentedit.tool==="pen"&&this.currentedit.path.push(s),this.currentedit.tool==="select"?this.currentannotation&&this.currentedit&&this.currentannotation.move(this.currentedit.annotationstart.x+s.x-this.currentedit.start.x,this.currentedit.annotationstart.y+s.y-this.currentedit.start.y):this.currentedit.start&&(this.currentedit.end=s,this.redraw_current_edit())},edit_end:function(){var e,t,n;e=(new Date).getTime()-this.currentedit.start;if(e<h||this.currentedit.start===!1)return;this.currentedit.tool==="comment"?(this.currentdrawable&&this.currentdrawable.erase(),this.currentdrawable=!1,t=new M.assignfeedback_editpdf.comment(this),t.init_from_edit(this.currentedit)&&(this.pages[this.currentpage].comments.push(t),this.drawables.push(t.draw(!0)),this.editingcomment=!0)):(n=this.create_annotation(this.currentedit.tool,{}),n&&(this.currentdrawable&&this.currentdrawable.erase(),this.currentdrawable=!1,n.init_from_edit(this.currentedit)&&(this.pages[this.currentpage].annotations.push(n),this.drawables.push(n.draw())))),this.save_current_page(),this.currentedit.starttime=0,this.currentedit.start=!1,this.currentedit.end=!1,this.currentedit.path=[]},create_annotation:function(e,t){return t.type=e,t.editor=this,e==="line"?new M.assignfeedback_editpdf.annotationline(t):e==="rectangle"?new M.assignfeedback_editpdf.annotationrectangle(t):e==="oval"?new M.assignfeedback_editpdf.annotationoval(t):e==="pen"?new M.assignfeedback_editpdf.annotationpen(t):e==="highlight"?new M.assignfeedback_editpdf.annotationhighlight(t):e==="stamp"?new M.assignfeedback_editpdf.annotationstamp(t):!1},save_current_page:function(){var t=n,r;r={method:"post",context:this,sync:!1,data:{sesskey:M.cfg.sesskey,action:"savepage",index:this.currentpage,userid:this.get("userid"),attemptnumber:this.get("attemptnumber"),assignmentid:this.get("assignmentid"),page:this.stringify_current_page()},on:{success:function(t,n){var r;try{r=e.JSON.parse(n.responseText);if(r.error)return new M.core.ajaxException(r);e.one(o.UNSAVEDCHANGESDIV).addClass("haschanges")}catch(i){return new M.core.exception(i)}},failure:function(e,t){return new M.core.exception(t.responseText)}}},e.io(t,r)},open_search_comments:function(e){this.searchcommentswindow||(this.searchcommentswindow=new M.assignfeedback_editpdf.commentsearch({editor:this})),this.searchcommentswindow.show(),e.preventDefault()},redraw:function(){var e,t;t=this.pages[this.currentpage];while(this.drawables.length>0)this.drawables.pop().erase();for(e=0;e<t.annotations.length;e++)this.drawables.push(t.annotations[e].draw());for(e=0;e<t.comments.length;e++)this.drawables.push(t.comments[e].draw(!1))},change_page:function(){var t=e.one(o.DRAWINGCANVAS),n,r,i;r=e.one(o.PREVIOUSBUTTON),i=e.one(o.NEXTBUTTON),this.currentpage>0?r.removeAttribute("disabled"):r.setAttribute("disabled","true"),this.currentpage<this.pagecount-1?i.removeAttribute("disabled"):i.setAttribute("disabled","true"),n=this.pages[this.currentpage],this.loadingicon.hide(),t.setStyle("backgroundImage",'url("'+n.url+'")'),e.one(o.PAGESELECT).set("value",this.currentpage),this.redraw()},setup_navigation:function(){var t,n,r,i,s;t=e.one(o.PAGESELECT),options=t.all("option");if(options.size()<=1)for(n=0;n<this.pages.length;n++)r=e.Node.create("<option/>"),r.setAttribute("value",n),r.setHTML(M.util.get_string("pagenumber","assignfeedback_editpdf",n+1)),t.append(r);t.removeAttribute("disabled"),t.on("change",function(){this.currentpage=t.get("value"),this.change_page()},this),i=e.one(o.PREVIOUSBUTTON),s=e.one(o.NEXTBUTTON),i.on("click",this.previous_page,this),i.on("key",this.previous_page,"down:13",this),s.on("click",this.next_page,this),s.on("key",this.next_page,"down:13",this)},previous_page:function(e){e.preventDefault(),this.currentpage--,this.currentpage<0&&(this.currentpage=0),this.change_page()},next_page:function(e){e.preventDefault(),this.currentpage++,this.currentpage>=this.pages.length&&(this.currentpage=this.pages.length-1),this.change_page()}},e.extend(EDITOR,e.Base,EDITOR.prototype,{NAME:"moodle-assignfeedback_editpdf-editor",ATTRS:{userid:{validator:e.Lang.isInteger,value:0},assignmentid:{validator:e.Lang.isInteger,value:0},attemptnumber:{validator:e.Lang.isInteger,value:0},header:{validator:e.Lang.isString,value:""},body:{validator:e.Lang.isString,value:""},footer:{validator:e.Lang.isString,value:""},linkid:{validator:e.Lang.isString,value:""},deletelinkid:{validator:e.Lang.isString,value:""},readonly:{validator:e.Lang.isBoolean,value:!0},stampfiles:{validator:e.Lang.isArray,value:""},pagetotal:{validator:e.Lang.isInteger,value:0}}}),M.assignfeedback_editpdf=M.assignfeedback_editpdf||{},M.assignfeedback_editpdf.editor=M.assignfeedback_editpdf.editor||{},M.assignfeedback_editpdf -.editor.init=M.assignfeedback_editpdf.editor.init||function(e){return new EDITOR(e)}},"@VERSION@",{requires:["base","event","node","io","graphics","json","event-move","querystring-stringify-simple","moodle-core-notification-dialog","moodle-core-notification-exception","moodle-core-notification-ajaxexception"]}); +),n.setAttribute("aria-pressed","true"),t=e.one(o.STAMPSBUTTON),t.one("img").setAttrs({src:this.get_stamp_image_url(this.currentedit.stamp),height:"16",width:"16"})},get_canvas_bounds:function(){var t=e.one(o.DRAWINGCANVAS),n=t.getXY(),r=n[0],i=n[1],s=parseInt(t.getStyle("width"),10),u=parseInt(t.getStyle("height"),10);return new M.assignfeedback_editpdf.rect(r,i,s,u)},get_canvas_coordinates:function(e){var t=this.get_canvas_bounds(),n=new M.assignfeedback_editpdf.point(e.x-t.x,e.y-t.y);return t.x=t.y=0,n.clip(t),n},get_window_coordinates:function(e){var t=this.get_canvas_bounds(),n=new M.assignfeedback_editpdf.point(e.x+t.x,e.y+t.y);return n},link_handler:function(t){var n;t.preventDefault(),this.dialogue||(this.dialogue=new M.core.dialogue({headerContent:this.get("header"),bodyContent:this.get("body"),footerContent:this.get("footer"),modal:!0,width:"840px",visible:!1,draggable:!0}),this.dialogue.get("boundingBox").addClass(s.DIALOGUE),this.loadingicon=e.one(o.LOADINGICON),n=e.one(o.DRAWINGCANVAS),this.graphic=new e.Graphic({render:o.DRAWINGCANVAS}),this.get("readonly")||(n.on("gesturemovestart",this.edit_start,null,this),n.on("gesturemove",this.edit_move,null,this),n.on("gesturemoveend",this.edit_end,null,this),this.refresh_button_state()),this.load_all_pages()),this.dialogue.centerDialogue(),this.dialogue.show()},load_all_pages:function(){var t=n,i,s,u;i={method:"get",context:this,sync:!1,data:{sesskey:M.cfg.sesskey,action:"loadallpages",userid:this.get("userid"),attemptnumber:this.get("attemptnumber"),assignmentid:this.get("assignmentid"),readonly:this.get("readonly")?1:0},on:{success:function(e,t){this.all_pages_loaded(t.responseText)},failure:function(e,t){return new M.core.exception(t.responseText)}}},e.io(t,i),this.pagecount<=0&&(s={method:"get",context:this,sync:!1,data:{sesskey:M.cfg.sesskey,action:"conversionstatus",userid:this.get("userid"),attemptnumber:this.get("attemptnumber"),assignmentid:this.get("assignmentid")},on:{success:function(t,n){u=0;if(this.pagecount===0){var i=this.get("pagetotal"),a=e.one(o.PROGRESSBARCONTAINER),f=a.one(".bar");if(f){var l=n.response/i*100;f.setStyle("width",l+"%"),a.setAttribute("aria-valuenow",l)}e.later(1e3,this,function(){e.io(r,s)})}},failure:function(t,n){return u+=1,this.pagecount===0&&u<5&&e.later(1e3,this,function(){e.io(r,s)}),new M.core.exception(n.responseText)}}},e.later(1e3,this,function(){u=0,e.io(r,s)}))},all_pages_loaded:function(t){var n,r,i,s,o;try{n=e.JSON.parse(t);if(n.error||!n.pagecount){this.dialogue.hide(),o=new M.core.alert({message:M.util.get_string("cannotopenpdf","assignfeedback_editpdf")}),o.show();return}}catch(u){this.dialogue.hide(),o=new M.core.alert({title:M.util.get_string("cannotopenpdf","assignfeedback_editpdf")}),o.show();return}this.pagecount=n.pagecount,this.pages=n.pages;for(r=0;r<this.pages.length;r++){for(i=0;i<this.pages[r].comments.length;i++)s=this.pages[r].comments[i],this.pages[r].comments[i]=new M.assignfeedback_editpdf.comment(this,s.gradeid,s.pageno,s.x,s.y,s.width,s.colour,s.rawtext);for(i=0;i<this.pages[r].annotations.length;i++)n=this.pages[r].annotations[i],this.pages[r].annotations[i]=this.create_annotation(n.type,n)}this.quicklist&&this.quicklist.load(),this.setup_navigation(),this.setup_toolbar(),this.change_page()},get_stamp_image_url:function(t){var n=this.get("stampfiles"),r="";return e.Array.each(n,function(e){e.indexOf(t)>0&&(r=e)},this),r},setup_toolbar:function(){var t,n,r,i,s,u,a,f;i=e.one(o.SEARCHCOMMENTSBUTTON),i.on("click",this.open_search_comments,this),i.on("key",this.open_search_comments,"down:13",this);if(this.get("readonly"))return;e.each(p,function(n,r){t=e.one(n),t.on("click",this.handle_tool_button,this,r),t.on("key",this.handle_tool_button,"down:13",this,r),t.setAttribute("aria-pressed","false")},this),n=e.one(o.COMMENTCOLOURBUTTON),a=new M.assignfeedback_editpdf.colourpicker({buttonNode:n,colours:l,iconprefix:"background_colour_",callback:function(e){var t=e.target.getAttribute("data-colour");t||(t=e.target.ancestor().getAttribute("data-colour")),this.currentedit.commentcolour=t,this.handle_tool_button(e,"comment")},context:this}),r=e.one(o.ANNOTATIONCOLOURBUTTON),a=new M.assignfeedback_editpdf.colourpicker({buttonNode:r,iconprefix:"colour_",colours:c,callback:function(e){var t=e.target.getAttribute("data-colour");t||(t=e.target.ancestor().getAttribute("data-colour")),this.currentedit.annotationcolour=t,this.lastannotationtool?this.handle_tool_button(e,this.lastannotationtool):this.handle_tool_button(e,"pen")},context:this}),u=this.get("stampfiles"),u.length<=0?e.one(p.stamp).ancestor().hide():(f=u[0].substr(u[0].lastIndexOf("/")+1),this.currentedit.stamp=f,s=e.one(o.STAMPSBUTTON),a=new M.assignfeedback_editpdf.stamppicker({buttonNode:s,stamps:u,callback:function(e){var t=e.target.getAttribute("data-stamp"),n;t||(t=e.target.ancestor().getAttribute("data-stamp")),n=t.substr(t.lastIndexOf("/")),this.currentedit.stamp=n,this.handle_tool_button(e,"stamp")},context:this}),this.refresh_button_state())},handle_tool_button:function(t,n){var r;t.preventDefault(),r=e.one(p[this.currentedit.tool]),r.removeClass("assignfeedback_editpdf_selectedbutton"),r.setAttribute("aria-pressed","false"),this.currentedit.tool=n,n!=="comment"&&n!=="select"&&n!=="stamp"&&(this.lastannotationtool=n),this.refresh_button_state()},stringify_current_page:function(){var t=[],n=[],r,i=0;for(i=0;i<this.pages[this.currentpage].comments.length;i++)t[i]=this.pages[this.currentpage].comments[i].clean();for(i=0;i<this.pages[this.currentpage].annotations.length;i++)n[i]=this.pages[this.currentpage].annotations[i].clean();return r={comments:t,annotations:n},e.JSON.stringify(r)},get_current_drawable:function(){var e,t,n=!1;return!this.currentedit.start||!this.currentedit.end?!1:(this.currentedit.tool==="comment"?(e=new M.assignfeedback_editpdf.comment(this),n=e.draw_current_edit(this.currentedit)):(t=this.create_annotation(this.currentedit.tool,{}),t&&(n=t.draw_current_edit(this.currentedit))),n)},redraw_current_edit +:function(){this.currentdrawable&&this.currentdrawable.erase(),this.currentdrawable=this.get_current_drawable()},edit_start:function(t){var n=e.one(o.DRAWINGCANVAS),r=n.getXY(),i=n.get("docScrollY"),s=n.get("docScrollX"),u={x:t.clientX-r[0]+s,y:t.clientY-r[1]+i},a=!1,f;if(t.button===3)return;if(this.currentedit.starttime)return;if(this.editingcomment)return;this.currentedit.starttime=(new Date).getTime(),this.currentedit.start=u,this.currentedit.end={x:u.x,y:u.y},this.currentedit.tool==="select"&&(x=this.currentedit.end.x,y=this.currentedit.end.y,annotations=this.pages[this.currentpage].annotations,e.each(annotations,function(e){(x-e.x)*(x-e.endx)<=0&&(y-e.y)*(y-e.endy)<=0&&(a=e)}),a&&(f=this.currentannotation,this.currentannotation=a,f&&f!==a&&f.drawable&&(f.drawable.erase(),this.drawables.push(f.draw())),this.currentannotation.drawable&&this.currentannotation.drawable.erase(),this.drawables.push(this.currentannotation.draw()))),this.currentannotation&&(this.currentedit.annotationstart={x:this.currentannotation.x,y:this.currentannotation.y})},edit_move:function(t){var n=this.get_canvas_bounds(),r=e.one(o.DRAWINGCANVAS),i=new M.assignfeedback_editpdf.point(t.clientX+r.get("docScrollX"),t.clientY+r.get("docScrollY")),s=this.get_canvas_coordinates(i);if(s.x<0||s.x>n.width||s.y<0||s.y>n.height)return;this.currentedit.tool==="pen"&&this.currentedit.path.push(s),this.currentedit.tool==="select"?this.currentannotation&&this.currentedit&&this.currentannotation.move(this.currentedit.annotationstart.x+s.x-this.currentedit.start.x,this.currentedit.annotationstart.y+s.y-this.currentedit.start.y):this.currentedit.start&&(this.currentedit.end=s,this.redraw_current_edit())},edit_end:function(){var e,t,n;e=(new Date).getTime()-this.currentedit.start;if(e<h||this.currentedit.start===!1)return;this.currentedit.tool==="comment"?(this.currentdrawable&&this.currentdrawable.erase(),this.currentdrawable=!1,t=new M.assignfeedback_editpdf.comment(this),t.init_from_edit(this.currentedit)&&(this.pages[this.currentpage].comments.push(t),this.drawables.push(t.draw(!0)),this.editingcomment=!0)):(n=this.create_annotation(this.currentedit.tool,{}),n&&(this.currentdrawable&&this.currentdrawable.erase(),this.currentdrawable=!1,n.init_from_edit(this.currentedit)&&(this.pages[this.currentpage].annotations.push(n),this.drawables.push(n.draw())))),this.save_current_page(),this.currentedit.starttime=0,this.currentedit.start=!1,this.currentedit.end=!1,this.currentedit.path=[]},create_annotation:function(e,t){return t.type=e,t.editor=this,e==="line"?new M.assignfeedback_editpdf.annotationline(t):e==="rectangle"?new M.assignfeedback_editpdf.annotationrectangle(t):e==="oval"?new M.assignfeedback_editpdf.annotationoval(t):e==="pen"?new M.assignfeedback_editpdf.annotationpen(t):e==="highlight"?new M.assignfeedback_editpdf.annotationhighlight(t):e==="stamp"?new M.assignfeedback_editpdf.annotationstamp(t):!1},save_current_page:function(){var t=n,r;r={method:"post",context:this,sync:!1,data:{sesskey:M.cfg.sesskey,action:"savepage",index:this.currentpage,userid:this.get("userid"),attemptnumber:this.get("attemptnumber"),assignmentid:this.get("assignmentid"),page:this.stringify_current_page()},on:{success:function(t,n){var r;try{r=e.JSON.parse(n.responseText);if(r.error)return new M.core.ajaxException(r);e.one(o.UNSAVEDCHANGESDIV).addClass("haschanges")}catch(i){return new M.core.exception(i)}},failure:function(e,t){return new M.core.exception(t.responseText)}}},e.io(t,r)},open_search_comments:function(e){this.searchcommentswindow||(this.searchcommentswindow=new M.assignfeedback_editpdf.commentsearch({editor:this})),this.searchcommentswindow.show(),e.preventDefault()},redraw:function(){var e,t;t=this.pages[this.currentpage];while(this.drawables.length>0)this.drawables.pop().erase();for(e=0;e<t.annotations.length;e++)this.drawables.push(t.annotations[e].draw());for(e=0;e<t.comments.length;e++)this.drawables.push(t.comments[e].draw(!1))},change_page:function(){var t=e.one(o.DRAWINGCANVAS),n,r,i;r=e.one(o.PREVIOUSBUTTON),i=e.one(o.NEXTBUTTON),this.currentpage>0?r.removeAttribute("disabled"):r.setAttribute("disabled","true"),this.currentpage<this.pagecount-1?i.removeAttribute("disabled"):i.setAttribute("disabled","true"),n=this.pages[this.currentpage],this.loadingicon.hide(),t.setStyle("backgroundImage",'url("'+n.url+'")'),e.one(o.PAGESELECT).set("value",this.currentpage),this.redraw()},setup_navigation:function(){var t,n,r,i,s;t=e.one(o.PAGESELECT),options=t.all("option");if(options.size()<=1)for(n=0;n<this.pages.length;n++)r=e.Node.create("<option/>"),r.setAttribute("value",n),r.setHTML(M.util.get_string("pagenumber","assignfeedback_editpdf",n+1)),t.append(r);t.removeAttribute("disabled"),t.on("change",function(){this.currentpage=t.get("value"),this.change_page()},this),i=e.one(o.PREVIOUSBUTTON),s=e.one(o.NEXTBUTTON),i.on("click",this.previous_page,this),i.on("key",this.previous_page,"down:13",this),s.on("click",this.next_page,this),s.on("key",this.next_page,"down:13",this)},previous_page:function(e){e.preventDefault(),this.currentpage--,this.currentpage<0&&(this.currentpage=0),this.change_page()},next_page:function(e){e.preventDefault(),this.currentpage++,this.currentpage>=this.pages.length&&(this.currentpage=this.pages.length-1),this.change_page()}},e.extend(EDITOR,e.Base,EDITOR.prototype,{NAME:"moodle-assignfeedback_editpdf-editor",ATTRS:{userid:{validator:e.Lang.isInteger,value:0},assignmentid:{validator:e.Lang.isInteger,value:0},attemptnumber:{validator:e.Lang.isInteger,value:0},header:{validator:e.Lang.isString,value:""},body:{validator:e.Lang.isString,value:""},footer:{validator:e.Lang.isString,value:""},linkid:{validator:e.Lang.isString,value:""},deletelinkid:{validator:e.Lang.isString,value:""},readonly:{validator:e.Lang.isBoolean,value:!0},stampfiles:{validator:e.Lang.isArray,value:""},pagetotal:{validator:e.Lang.isInteger,value:0}}}),M.assignfeedback_editpdf=M.assignfeedback_editpdf||{},M.assignfeedback_editpdf.editor=M.assignfeedback_editpdf +.editor||{},M.assignfeedback_editpdf.editor.init=M.assignfeedback_editpdf.editor.init||function(e){return new EDITOR(e)}},"@VERSION@",{requires:["base","event","node","io","graphics","json","event-move","querystring-stringify-simple","moodle-core-notification-dialog","moodle-core-notification-exception","moodle-core-notification-ajaxexception"]}); diff --git a/mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor.js b/mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor.js index 05b1dc5f200..3f4859a2a37 100644 --- a/mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor.js +++ b/mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor.js @@ -3351,7 +3351,8 @@ EDITOR.prototype = { action : 'loadallpages', userid : this.get('userid'), attemptnumber : this.get('attemptnumber'), - assignmentid : this.get('assignmentid') + assignmentid : this.get('assignmentid'), + readonly : this.get('readonly') ? 1 : 0 }, on: { success: function(tid, response) { diff --git a/mod/assign/feedback/editpdf/yui/src/editor/js/editor.js b/mod/assign/feedback/editpdf/yui/src/editor/js/editor.js index 898179b0e91..a0366ba906e 100644 --- a/mod/assign/feedback/editpdf/yui/src/editor/js/editor.js +++ b/mod/assign/feedback/editpdf/yui/src/editor/js/editor.js @@ -346,7 +346,8 @@ EDITOR.prototype = { action : 'loadallpages', userid : this.get('userid'), attemptnumber : this.get('attemptnumber'), - assignmentid : this.get('assignmentid') + assignmentid : this.get('assignmentid'), + readonly : this.get('readonly') ? 1 : 0 }, on: { success: function(tid, response) { From 7dbbb848c632f3cbd752fc5cd0cb0e534c2f1890 Mon Sep 17 00:00:00 2001 From: Frederic Massart <fred@moodle.com> Date: Fri, 13 Jun 2014 10:41:45 +0800 Subject: [PATCH 2/3] MDL-45580 assignfeedback_editpdf: Delete draft content on new submission --- .../editpdf/classes/document_services.php | 4 +++- .../feedback/editpdf/classes/page_editor.php | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/mod/assign/feedback/editpdf/classes/document_services.php b/mod/assign/feedback/editpdf/classes/document_services.php index 7ae0b30ef02..912bd8addff 100644 --- a/mod/assign/feedback/editpdf/classes/document_services.php +++ b/mod/assign/feedback/editpdf/classes/document_services.php @@ -438,8 +438,10 @@ class document_services { if (!empty($files)) { $first = reset($files); if (!$readonly && $first->get_timemodified() < $submission->timemodified) { - // Image files are stale - regenerate them, except in readonly mode. + // Image files are stale, we need to regenerate them, except in readonly mode. + // We also need to remove the draft annotations and comments associated with this attempt. $fs->delete_area_files($contextid, $component, $filearea, $itemid); + page_editor::delete_draft_content($itemid); $files = array(); } else { diff --git a/mod/assign/feedback/editpdf/classes/page_editor.php b/mod/assign/feedback/editpdf/classes/page_editor.php index 1b1447ac0df..3dfa0716871 100644 --- a/mod/assign/feedback/editpdf/classes/page_editor.php +++ b/mod/assign/feedback/editpdf/classes/page_editor.php @@ -340,4 +340,21 @@ class page_editor { return true; } + + /** + * Delete the draft annotations and comments. + * + * This is intended to be used when the version of the PDF has changed and the annotations + * might not be relevant any more, therefore we should delete them. + * + * @param int $gradeid The grade ID. + * @return bool + */ + public static function delete_draft_content($gradeid) { + global $DB; + $conditions = array('gradeid' => $gradeid, 'draft' => 1); + $result = $DB->delete_records('assignfeedback_editpdf_annot', $conditions); + $result = $result && $DB->delete_records('assignfeedback_editpdf_cmnt', $conditions); + return $result; + } } From b4148e46884d96b018c81a358aaf585587608454 Mon Sep 17 00:00:00 2001 From: Frederic Massart <fred@moodle.com> Date: Mon, 23 Jun 2014 14:46:18 +0800 Subject: [PATCH 3/3] MDL-45580 assignfeedback_editpdf: Save readonly version in backup --- .../backup_assignfeedback_editpdf_subplugin.class.php | 7 +++++-- .../restore_assignfeedback_editpdf_subplugin.class.php | 5 ++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/mod/assign/feedback/editpdf/backup/moodle2/backup_assignfeedback_editpdf_subplugin.class.php b/mod/assign/feedback/editpdf/backup/moodle2/backup_assignfeedback_editpdf_subplugin.class.php index 546b8769ecb..4fe8cc66a0a 100644 --- a/mod/assign/feedback/editpdf/backup/moodle2/backup_assignfeedback_editpdf_subplugin.class.php +++ b/mod/assign/feedback/editpdf/backup/moodle2/backup_assignfeedback_editpdf_subplugin.class.php @@ -61,8 +61,11 @@ class backup_assignfeedback_editpdf_subplugin extends backup_subplugin { $subpluginelementfiles->set_source_sql('SELECT id AS gradeid from {assign_grades} where id = :gradeid', array('gradeid' => backup::VAR_PARENTID)); $subpluginelementannotation->set_source_table('assignfeedback_editpdf_annot', array('gradeid' => backup::VAR_PARENTID)); $subpluginelementcomment->set_source_table('assignfeedback_editpdf_cmnt', array('gradeid' => backup::VAR_PARENTID)); - // We only need to backup the files in the final pdf area - all the others can be regenerated. - $subpluginelementfiles->annotate_files('assignfeedback_editpdf', 'download', 'gradeid'); + // We only need to backup the files in the final pdf area, and the readonly page images - the others can be regenerated. + $subpluginelementfiles->annotate_files('assignfeedback_editpdf', + \assignfeedback_editpdf\document_services::FINAL_PDF_FILEAREA, 'gradeid'); + $subpluginelementfiles->annotate_files('assignfeedback_editpdf', + \assignfeedback_editpdf\document_services::PAGE_IMAGE_READONLY_FILEAREA, 'gradeid'); $subpluginelementfiles->annotate_files('assignfeedback_editpdf', 'stamps', 'gradeid'); return $subplugin; } diff --git a/mod/assign/feedback/editpdf/backup/moodle2/restore_assignfeedback_editpdf_subplugin.class.php b/mod/assign/feedback/editpdf/backup/moodle2/restore_assignfeedback_editpdf_subplugin.class.php index cb7923ea237..c520e2b7dc1 100644 --- a/mod/assign/feedback/editpdf/backup/moodle2/restore_assignfeedback_editpdf_subplugin.class.php +++ b/mod/assign/feedback/editpdf/backup/moodle2/restore_assignfeedback_editpdf_subplugin.class.php @@ -68,7 +68,10 @@ class restore_assignfeedback_editpdf_subplugin extends restore_subplugin { $data = (object)$data; // In this case the id is the old gradeid which will be mapped. - $this->add_related_files('assignfeedback_editpdf', 'download', 'grade', null, $data->gradeid); + $this->add_related_files('assignfeedback_editpdf', + \assignfeedback_editpdf\document_services::FINAL_PDF_FILEAREA, 'grade', null, $data->gradeid); + $this->add_related_files('assignfeedback_editpdf', + \assignfeedback_editpdf\document_services::PAGE_IMAGE_READONLY_FILEAREA, 'grade', null, $data->gradeid); $this->add_related_files('assignfeedback_editpdf', 'stamps', 'grade', null, $data->gradeid); }