diff --git a/edit_form.php b/edit_form.php index 769758c..511e4b0 100644 --- a/edit_form.php +++ b/edit_form.php @@ -238,8 +238,12 @@ class mod_customcert_edit_form extends moodleform { $row->cells[] = $icons; $table->data[] = $row; } + // Create link to order the elements. + $link = html_writer::link(new moodle_url('/mod/customcert/rearrange.php', array('id' => $page->id)), + get_string('rearrangeelements', 'customcert')); // Add the table to the form. - $mform->addElement('static', 'elements_' . $page->id, get_string('elements', 'customcert'), html_writer::table($table)); + $mform->addElement('static', 'elements_' . $page->id, get_string('elements', 'customcert'), html_writer::table($table) + . html_writer::tag( 'div', $link, array('style' => 'text-align:right'))); $mform->addHelpButton('elements_' . $page->id, 'elements', 'customcert'); } diff --git a/element/border/lib.php b/element/border/lib.php index b603c7a..718413e 100644 --- a/element/border/lib.php +++ b/element/border/lib.php @@ -57,6 +57,16 @@ class customcert_element_border extends customcert_element_base { $pdf->Line(0, 0, 0, $pdf->getPageHeight()); } + /** + * Render the element in html. + * + * This function is used to render the element when we are using the + * drag and drop interface to position it. + */ + public function render_html() { + return ''; + } + /** * Performs validation on the element values. * diff --git a/element/categoryname/lib.php b/element/categoryname/lib.php index 194cb40..18dcd1a 100644 --- a/element/categoryname/lib.php +++ b/element/categoryname/lib.php @@ -40,4 +40,18 @@ class customcert_element_categoryname extends customcert_element_base { parent::render_content($pdf, $categoryname); } + + /** + * Render the element in html. + * + * This function is used to render the element when we are using the + * drag and drop interface to position it. + */ + public function render_html() { + global $DB, $COURSE; + + $categoryname = $DB->get_field('course_categories', 'name', array('id' => $COURSE->category), MUST_EXIST); + + return parent::render_html_content($categoryname); + } } diff --git a/element/code/lib.php b/element/code/lib.php index 8ea019f..b375e16 100644 --- a/element/code/lib.php +++ b/element/code/lib.php @@ -48,4 +48,16 @@ class customcert_element_code extends customcert_element_base { parent::render_content($pdf, $code); } + + /** + * Render the element in html. + * + * This function is used to render the element when we are using the + * drag and drop interface to position it. + */ + public function render_html() { + $code = customcert_generate_code(); + + return parent::render_html_content($code); + } } diff --git a/element/coursename/lib.php b/element/coursename/lib.php index 1a16f89..39abcf7 100644 --- a/element/coursename/lib.php +++ b/element/coursename/lib.php @@ -38,4 +38,16 @@ class customcert_element_coursename extends customcert_element_base { parent::render_content($pdf, $COURSE->fullname); } + + /** + * Render the element in html. + * + * This function is used to render the element when we are using the + * drag and drop interface to position it. + */ + public function render_html() { + global $COURSE; + + return parent::render_html_content($COURSE->fullname); + } } diff --git a/element/date/lib.php b/element/date/lib.php index 00c5342..25e07d1 100644 --- a/element/date/lib.php +++ b/element/date/lib.php @@ -152,6 +152,44 @@ class customcert_element_date extends customcert_element_base { } } + /** + * Render the element in html. + * + * This function is used to render the element when we are using the + * drag and drop interface to position it. + */ + public function render_html() { + // If there is no element data, we have nothing to display. + if (empty($this->element->data)) { + return; + } + + // Decode the information stored in the database. + $dateinfo = json_decode($this->element->data); + $dateformat = $dateinfo->dateformat; + + $date = time(); + switch ($dateformat) { + case 1: + $certificatedate = userdate($date, '%B %d, %Y'); + break; + case 2: + $suffix = $this->get_ordinal_number_suffix(userdate($date, '%d')); + $certificatedate = userdate($date, '%B %d' . $suffix . ', %Y'); + break; + case 3: + $certificatedate = userdate($date, '%d %B %Y'); + break; + case 4: + $certificatedate = userdate($date, '%B %Y'); + break; + default: + $certificatedate = userdate($date, get_string('strftimedate', 'langconfig')); + } + + return parent::render_html_content($certificatedate); + } + /** * Sets the data on the form when editing an element. * diff --git a/element/element.class.php b/element/element.class.php index 4ba4999..d39dd63 100644 --- a/element/element.class.php +++ b/element/element.class.php @@ -183,7 +183,8 @@ abstract class customcert_element_base { * @param string $content the content to render */ public function render_content($pdf, $content) { - $this->set_font($pdf); + list($font, $attr) = $this->get_font(); + $pdf->setFont($font, $attr, $this->element->size); $fontcolour = TCPDF_COLORS::convertHTMLColorToDec($this->element->colour, $fontcolour); $pdf->SetTextColor($fontcolour['R'], $fontcolour['G'], $fontcolour['B']); @@ -226,6 +227,35 @@ abstract class customcert_element_base { $pdf->writeHTMLCell($w, 0, $x, $y, $content, 0, 0, false, true, $align); } + /** + * Render the element in html. + * + * Must be overridden. + * + * This function is used to render the element when we are using the + * drag and drop interface to position it. + */ + public abstract function render_html(); + + /** + * Common behaviour for rendering specified content on the drag and drop page. + * + * @param string $content the content to render + * @return string the html + */ + public function render_html_content($content) { + list($font, $attr) = $this->get_font(); + $fontstyle = 'font-family: ' . $font; + if (strpos($attr, 'B') !== false) { + $fontstyle .= ': font-weight: bold'; + } + if (strpos($attr, 'I') !== false) { + $fontstyle .= ': font-style: italic'; + } + $style = $fontstyle . '; color: ' . $this->element->colour . '; font-size: ' . $this->element->size . 'pt'; + return html_writer::tag('span', $content, array('style' => $style)); + } + /** * Handles deleting any data this element may have introduced. * Can be overridden if more functionality is needed. @@ -379,11 +409,11 @@ abstract class customcert_element_base { } /** - * Sets the font for the element. + * Returns the font used for this element. * - * @param pdf $pdf the pdf object + * @return array the font and font attributes */ - public function set_font($pdf) { + public function get_font() { // Variable for the font. $font = $this->element->font; // Get the last two characters of the font name. @@ -408,7 +438,7 @@ abstract class customcert_element_base { $font = substr($font, 0, -1); $attr .= 'B'; } - $pdf->setFont($font, $attr, $this->element->size); + return array($font, $attr); } /** diff --git a/element/grade/lib.php b/element/grade/lib.php index 3a45a81..4c3f830 100644 --- a/element/grade/lib.php +++ b/element/grade/lib.php @@ -106,6 +106,29 @@ class customcert_element_grade extends customcert_element_base { parent::render_content($pdf, $grade); } + /** + * Render the element in html. + * + * This function is used to render the element when we are using the + * drag and drop interface to position it. + */ + public function render_html() { + global $COURSE; + + // If there is no element data, we have nothing to display. + if (empty($this->element->data)) { + return; + } + + // Decode the information stored in the database. + $gradeinfo = json_decode($this->element->data); + + $courseitem = grade_item::fetch_course_item($COURSE->id); + $grade = grade_format_gradevalue('100', $courseitem, true, $gradeinfo->gradeformat, 2); + + return parent::render_html_content($grade); + } + /** * Sets the data on the form when editing an element. * diff --git a/element/gradeitemname/lib.php b/element/gradeitemname/lib.php index 7e79328..46dfe5b 100644 --- a/element/gradeitemname/lib.php +++ b/element/gradeitemname/lib.php @@ -78,6 +78,28 @@ class customcert_element_gradeitemname extends customcert_element_base { } } + /** + * Render the element in html. + * + * This function is used to render the element when we are using the + * drag and drop interface to position it. + */ + public function render_html() { + global $DB; + + // Check that the grade item is not empty. + if (!empty($this->element->data)) { + // Get the course module information. + $cm = $DB->get_record('course_modules', array('id' => $this->element->data), '*', MUST_EXIST); + $module = $DB->get_record('modules', array('id' => $cm->module), '*', MUST_EXIST); + + // Get the name of the item. + $itemname = $DB->get_field($module->name, 'name', array('id' => $cm->instance), MUST_EXIST); + + return parent::render_html_content($itemname); + } + } + /** * Sets the data on the form when editing an element. * diff --git a/element/image/lib.php b/element/image/lib.php index fc6a0bb..4be25d8 100644 --- a/element/image/lib.php +++ b/element/image/lib.php @@ -151,6 +151,49 @@ class customcert_element_image extends customcert_element_base { } } + /** + * Render the element in html. + * + * This function is used to render the element when we are using the + * drag and drop interface to position it. + */ + public function render_html() { + // If there is no element data, we have nothing to display. + if (empty($this->element->data)) { + return; + } + + $imageinfo = json_decode($this->element->data); + + // Get the image. + $fs = get_file_storage(); + if ($file = $fs->get_file_by_hash($imageinfo->pathnamehash)) { + $url = moodle_url::make_pluginfile_url($file->get_contextid(), 'mod_customcert', 'image', $file->get_itemid(), + $file->get_filepath(), $file->get_filename()); + $fileimageinfo = $file->get_imageinfo(); + $whratio = $fileimageinfo['width'] / $fileimageinfo['height']; + // The size of the images to use in the CSS style. + $style = ''; + if ($imageinfo->width === 0 && $imageinfo->height === 0) { + $style .= 'width: ' . $fileimageinfo['width'] . 'px; '; + $style .= 'height: ' . $fileimageinfo['height'] . 'px'; + } else if ($imageinfo->width === 0) { // Then the height must be set. + // We must get the width based on the height to keep the ratio. + $style .= 'width: ' . ($imageinfo->height * $whratio) . 'mm; '; + $style .= 'height: ' . $imageinfo->height . 'mm'; + } else if ($imageinfo->height === 0) { // Then the width must be set. + $style .= 'width: ' . $imageinfo->width . 'mm; '; + // We must get the height based on the width to keep the ratio. + $style .= 'height: ' . ($imageinfo->width / $whratio) . 'mm'; + } else { // Must both be set. + $style .= 'width: ' . $imageinfo->width . 'mm; '; + $style .= 'height: ' . $imageinfo->height . 'mm'; + } + + return html_writer::tag('img', '', array('src' => $url, 'style' => $style)); + } + } + /** * Sets the data on the form when editing an element. * diff --git a/element/studentname/lib.php b/element/studentname/lib.php index f39306c..dfbe013 100644 --- a/element/studentname/lib.php +++ b/element/studentname/lib.php @@ -38,4 +38,16 @@ class customcert_element_studentname extends customcert_element_base { parent::render_content($pdf, fullname($USER)); } + + /** + * Render the element in html. + * + * This function is used to render the element when we are using the + * drag and drop interface to position it. + */ + public function render_html() { + global $USER; + + return parent::render_html_content(fullname($USER)); + } } diff --git a/element/teachername/lib.php b/element/teachername/lib.php index 2306a67..f16e770 100644 --- a/element/teachername/lib.php +++ b/element/teachername/lib.php @@ -66,6 +66,21 @@ class customcert_element_teachername extends customcert_element_base { parent::render_content($pdf, $teachername); } + /** + * Render the element in html. + * + * This function is used to render the element when we are using the + * drag and drop interface to position it. + */ + public function render_html() { + global $DB; + + $teacher = $DB->get_record('user', array('id' => $this->element->data)); + $teachername = fullname($teacher); + + return parent::render_html_content($teachername); + } + /** * Helper function to return the teachers for this course. * diff --git a/element/text/lib.php b/element/text/lib.php index 3c707ce..bc6c5ff 100644 --- a/element/text/lib.php +++ b/element/text/lib.php @@ -61,6 +61,16 @@ class customcert_element_text extends customcert_element_base { parent::render_content($pdf, $this->element->data); } + /** + * Render the element in html. + * + * This function is used to render the element when we are using the + * drag and drop interface to position it. + */ + public function render_html() { + return parent::render_html_content($this->element->data); + } + /** * Sets the data on the form when editing an element. * diff --git a/element/userfield/lib.php b/element/userfield/lib.php index 3f80b98..b2b637c 100644 --- a/element/userfield/lib.php +++ b/element/userfield/lib.php @@ -106,6 +106,30 @@ class customcert_element_userfield extends customcert_element_base { parent::render_content($pdf, $value); } + /** + * Render the element in html. + * + * This function is used to render the element when we are using the + * drag and drop interface to position it. + */ + public function render_html() { + global $DB, $USER; + + // The user field to display. + $field = $this->element->data; + // The value to display on the PDF. + $value = ''; + if (is_number($field)) { // Must be a custom user profile field. + if ($field = $DB->get_record('user_info_field', array('id' => $field))) { + $value = $USER->profile[$field->shortname]; + } + } else if (!empty($USER->$field)) { // Field in the user table. + $value = $USER->$field; + } + + return parent::render_html_content($value); + } + /** * Sets the data on the form when editing an element. * diff --git a/lang/en/customcert.php b/lang/en/customcert.php index c2bde64..0c401f7 100644 --- a/lang/en/customcert.php +++ b/lang/en/customcert.php @@ -30,6 +30,7 @@ $string['aligncenter'] = 'Center'; $string['alignleft'] = 'Left align'; $string['alignnone'] = 'No alignment'; $string['alignright'] = 'Right align'; +$string['applypositions'] = 'Save positions & continue'; $string['awardedto'] = 'Awarded to'; $string['code'] = 'Code'; $string['copy'] = 'Copy'; @@ -97,6 +98,8 @@ $string['posx'] = 'Position X'; $string['posx_help'] = 'This is the position in mm from the top left corner you wish the element\'s reference point to locate in the x direction.'; $string['posy'] = 'Postion Y'; $string['posy_help'] = 'This is the position in mm from the top left corner you wish the element\'s reference point to locate in the y direction.'; +$string['rearrangeelements'] = 'Rearrange elements'; +$string['rearrangeelementsheading'] = 'Drag and drop elements to change where they are positioned on the certificate.'; $string['receiveddate'] = 'Received date'; $string['refpoint'] = 'Reference point location'; $string['refpoint_help'] = 'This specifies which location of the element to be located at position X and position Y.'; @@ -104,6 +107,7 @@ $string['replacetemplate'] = 'Replace'; $string['report'] = 'Report'; $string['save'] = 'Save'; $string['savechangespreview'] = 'Save changes and preview'; +$string['savepositions'] = 'Save positions'; $string['savetemplate'] = 'Save template'; $string['setprotection'] = 'Set protection'; $string['setprotection_help'] = 'Choose the actions you wish to prevent users from performing on this certificate.'; diff --git a/lib.php b/lib.php index c23bdd5..6bf75fb 100644 --- a/lib.php +++ b/lib.php @@ -226,6 +226,42 @@ function customcert_user_complete($course, $user, $mod, $customcert) { } } +/** + * Serves certificate issues and other files. + * + * @param stdClass $course + * @param stdClass $cm + * @param context $context + * @param string $filearea + * @param array $args + * @param bool $forcedownload + * @return bool|nothing false if file not found, does not return anything if found - just send the file + */ +function customcert_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) { + global $CFG; + + require_once($CFG->libdir . '/filelib.php'); + + // We are positioning the elements. + if ($filearea === 'image') { + if ($context->contextlevel == CONTEXT_MODULE) { + require_login($course, false, $cm); + } else if ($context->contextlevel == CONTEXT_SYSTEM && !has_capability('mod/certificate:manage', $context)) { + return false; + } + + $relativepath = implode('/', $args); + $fullpath = '/' . $context->id . '/mod_customcert/image/' . $relativepath; + + $fs = get_file_storage(); + if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) { + return false; + } + + send_stored_file($file, 0, 0, $forcedownload); + } +} + /** * @uses FEATURE_GROUPS * @uses FEATURE_GROUPINGS diff --git a/pix/dash.gif b/pix/dash.gif new file mode 100644 index 0000000..c535af8 Binary files /dev/null and b/pix/dash.gif differ diff --git a/pix/target.gif b/pix/target.gif new file mode 100644 index 0000000..df7d575 Binary files /dev/null and b/pix/target.gif differ diff --git a/rearrange.php b/rearrange.php new file mode 100644 index 0000000..b7d41cf --- /dev/null +++ b/rearrange.php @@ -0,0 +1,103 @@ +. + +/** + * Handles position elements on the PDF via drag and drop. + * + * @package mod_customcert + * @copyright 2013 Mark Nelson + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once('../../config.php'); +require_once($CFG->dirroot . '/mod/customcert/locallib.php'); + +// The page of the customcert we are editing. +$pid = required_param('id', PARAM_INT); + +$page = $DB->get_record('customcert_pages', array('id' => $pid), '*', MUST_EXIST); +$elements = $DB->get_records('customcert_elements', array('pageid' => $pid), 'sequence'); +$cm = get_coursemodule_from_instance('customcert', $page->customcertid, 0, false, MUST_EXIST); +$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST); +$context = context_module::instance($cm->id); + +require_login($course, false, $cm); + +require_capability('mod/customcert:manage', $context); + +// Set the $PAGE settings. +$PAGE->set_url(new moodle_url('/mod/customcert/rearrange.php', array('id' => $pid))); +$PAGE->set_pagetype('mod-customcert-position'); +$PAGE->set_title(get_string('rearrangeelements', 'customcert')); +$PAGE->set_heading($course->fullname); + +// Include the JS we need. +$module = array( + 'name' => 'mod_customcert', + 'fullpath' => '/mod/customcert/yui/src/rearrange.js', + 'requires' => array('dd-delegate', 'dd-drag') +); +$PAGE->requires->js_init_call('M.mod_customcert.rearrange.init', array($cm->id, $elements), false, $module); + +// Create the buttons to save the position of the elements. +$html = html_writer::start_tag('div', array('class' => 'buttons')); +$html .= $OUTPUT->single_button(new moodle_url('/mod/customcert/edit.php', array('cmid' => $cm->id)), + get_string('savepositions', 'customcert'), 'get', array('class' => 'savepositionsbtn')); +$html .= $OUTPUT->single_button(new moodle_url('/mod/customcert/rearrange.php', array('id' => $pid)), + get_string('applypositions', 'customcert'), 'get', array('class' => 'applypositionsbtn')); +$html .= $OUTPUT->single_button(new moodle_url('/mod/customcert/edit.php', array('cmid' => $cm->id)), + get_string('cancel'), 'get', array('class' => 'cancelbtn')); +$html .= html_writer::end_tag('div'); + +$style = 'height: ' . $page->height . 'mm; line-height: normal;'; +if ($page->margin) { + $style .= 'width: ' . ($page->width - $page->margin) . 'mm;'; + $style .= 'background-image: url(' . new moodle_url('/mod/customcert/pix/dash') . ');'; + $style .= 'background-repeat: repeat-y;'; + $style .= 'background-position-x: ' . ($page->width - $page->margin) . 'mm;'; + $style .= 'padding-right: ' . $page->margin . 'mm;'; +} else { + $style .= 'width: ' . $page->width . 'mm;'; +} + +// Create the div that represents the PDF. +$html .= html_writer::start_tag('div', array('id' => 'pdf', 'style' => $style)); +if ($elements) { + foreach ($elements as $element) { + // Get an instance of the element class. + if ($e = customcert_get_element_instance($element)) { + switch ($element->refpoint) { + case CUSTOMCERT_REF_POINT_TOPRIGHT: + $class = 'element refpoint-right'; + break; + case CUSTOMCERT_REF_POINT_TOPCENTER: + $class = 'element refpoint-center'; + break; + case CUSTOMCERT_REF_POINT_TOPLEFT: + default: + $class = 'element refpoint-left'; + } + $html .= html_writer::tag('div', $e->render_html(), array('class' => $class, 'id' => 'element-' . $element->id)); + } + } +} +$html .= html_writer::end_tag('div'); + +echo $OUTPUT->header(); +echo $OUTPUT->heading(get_string('editcustomcert', 'customcert')); +echo $OUTPUT->heading(get_string('rearrangeelementsheading', 'customcert'), 4); +echo $html; +echo $OUTPUT->footer(); \ No newline at end of file diff --git a/rest.php b/rest.php new file mode 100644 index 0000000..0c4d425 --- /dev/null +++ b/rest.php @@ -0,0 +1,56 @@ +. + +/** + * Handles AJAX requests for the customcert module. + * + * @package mod_customcert + * @copyright 2013 Mark Nelson + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +if (!defined('AJAX_SCRIPT')) { + define('AJAX_SCRIPT', true); +} + +require_once(__DIR__ . '/../../config.php'); + +$cmid = required_param('cmid', PARAM_INT); +$values = required_param('values', PARAM_RAW); +$values = json_decode($values); + +$cm = get_coursemodule_from_id('customcert', $cmid, 0, false, MUST_EXIST); +$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST); +$context = context_module::instance($cm->id); +$elements = $DB->get_records_sql('SELECT * FROM {customcert_elements} e + JOIN {customcert_pages} p ON e.pageid = p.id + WHERE p.customcertid = ?', array($cm->instance)); + +// Check that the user is able to perform the change. +require_login($course, false, $cm); +require_capability('mod/customcert:manage', $context); + +// Loop through the data +foreach ($values as $value) { +// if (array_key_exists($value->id, $elements)) { + // Perform the update. + $element = new stdClass(); + $element->id = $value->id; + $element->posx = $value->posx; + $element->posy = $value->posy; + $DB->update_record('customcert_elements', $element); +// } +} \ No newline at end of file diff --git a/styles.css b/styles.css index 13edcb2..c84261a 100644 --- a/styles.css +++ b/styles.css @@ -10,3 +10,50 @@ margin-left: auto; margin-right: auto; } + +#page-mod-customcert-position .savepositionsbtn, +#page-mod-customcert-position .applypositionsbtn, +#page-mod-customcert-position .cancelbtn { + float:left +} + +#page-mod-customcert-position .element { + display:inline-block; + position: absolute; + word-wrap: break-word; +} + +#page-mod-customcert-position .element:before { + content: ""; + display: block; + background-image: url([[pix:mod_customcert|target]]); + background-repeat: no-repeat; + width: 100%; + height: 9px; + float: left; +} + +#page-mod-customcert-position .element:hover { + cursor:move; +} + +#page-mod-customcert-position .element.refpoint-left:before { + background-position: left top; + margin: -4px -5px -5px -4px; +} + +#page-mod-customcert-position .element.refpoint-center:before { + background-position: center top; + margin: -4px 0 -5px 0; +} + +#page-mod-customcert-position .element.refpoint-right:before { + background-position: right top; + margin: -4px -5px -5px 4px; +} + +#page-mod-customcert-position #pdf { + clear:both; + border-style:solid; + border-width:1px; +} \ No newline at end of file diff --git a/yui/src/rearrange.js b/yui/src/rearrange.js new file mode 100644 index 0000000..ff0188c --- /dev/null +++ b/yui/src/rearrange.js @@ -0,0 +1,333 @@ +M.mod_customcert = {}; + + +M.mod_customcert.rearrange = { + + /** + * The course module id. + */ + cmid : 0, + + /** + * The custom certificate elements to display. + */ + elements : Array(), + + /** + * Store the X coordinates of the top left of the pdf div. + */ + pdfx : 0, + + /** + * Store the Y coordinates of the top left of the pdf div. + */ + pdfy : 0, + + /** + * Store the width of the pdf div. + */ + pdfwidth : 0, + + /** + * Store the height of the pdf div. + */ + pdfheight : 0, + + /** + * Store the location of the element before we move. + */ + elementxy : 0, + + /** + * The number of pixels in a mm. + */ + pixelsinmm : 3.779527559055, //'3.779528', + + /** + * Initialise. + * + * @param Y + * @param elements + */ + init : function(Y, cmid, elements) { + // Set the course module id. + this.cmid = cmid; + // Set the elements. + this.elements = elements; + + // Set the PDF dimensions. + this.pdfx = Y.one('#pdf').getX(); + this.pdfy = Y.one('#pdf').getY(); + this.pdfwidth = parseFloat(Y.one('#pdf').getComputedStyle('width'), 10); + this.pdfheight = parseFloat(Y.one('#pdf').getComputedStyle('height'), 10); + + this.set_data(Y); + this.set_positions(Y); + this.create_events(Y); + // this.set_positions(Y); // move here + }, + + /** + * Sets the additional data for the elements. + * + * @param Y + */ + set_data : function(Y) { + // Go through the elements and set their reference points. + for (var key in this.elements) { + var element = this.elements[key]; + Y.one('#element-' + element['id']).setData('refpoint', element['refpoint']); + Y.one('#element-' + element['id']).setData('width', element['width']); + } + }, + + /** + * Sets the current position of the elements. + * + * @param Y + */ + set_positions : function(Y) { + // Go through the elements and set their positions. + for (var key in this.elements) { + var element = this.elements[key]; + var posx = this.pdfx + element['posx'] * this.pixelsinmm; + var posy = this.pdfy + element['posy'] * this.pixelsinmm; + var nodewidth = parseFloat(Y.one('#element-' + element['id']).getComputedStyle('width'), 10); + var maxwidth = element['width'] * this.pixelsinmm; + + if (maxwidth && (nodewidth > maxwidth)) { + nodewidth = maxwidth; + } + Y.one('#element-' + element['id']).setStyle('width', nodewidth + 'px'); + + switch (element['refpoint']) { + case '1': // Top-center + posx -= nodewidth / 2; + break; + case '2': // Top-right + posx = posx - nodewidth + 2; + break; + } + + Y.one('#element-' + element['id']).setX(posx); + Y.one('#element-' + element['id']).setY(posy); + + this.resize_if_required(Y.one('#element-' + element['id'])); + } + }, + + /** + * Creates the JS events for changing element positions. + * + * @param Y + */ + create_events : function(Y) { + // Trigger a save event when save button is pushed. + Y.one('.savepositionsbtn input[type=submit]').on('click', function(e) { + this.save_positions(e); + }, this); + + // Trigger a save event when apply button is pushed. + Y.one('.applypositionsbtn input[type=submit]').on('click', function(e) { + this.save_positions(e); + e.preventDefault(); + }, this); + + // Define the container and the elements that are draggable. + var del = new Y.DD.Delegate({ + container: '#pdf', + nodes: '.element' + }); + + // When we start dragging keep track of it's position as we may set it back. + del.on('drag:start', function() { + var node = del.get('currentNode'); + this.elementxy = node.getXY(); + this.elementwidth = node.getComputedStyle('width'); + }, this); + + // When we finish the dragging action check that the node is in bounds, + // if not, set it back to where it was. + del.on('drag:end', function() { + var node = del.get('currentNode'); + this.resize_if_required(node); + if (this.is_out_of_bounds(node)) { + node.setXY(this.elementxy); + node.setStyle('width', this.elementwidth); + } + }, this); + }, + + /** + * Resizes the element if required. + * + * @param node + * @returns {boolean} + */ + resize_if_required : function(node) { + var refpoint = node.getData('refpoint'); + var maxwidth = node.getData('width') * this.pixelsinmm; + var maxallowedwidth = 0; + var oldwidth = 0; + + // Get the width and height of the node. + var nodewidth = parseFloat(node.getComputedStyle('width'), 10); + var nodeheight = parseFloat(node.getComputedStyle('height'), 10); + + // Store the positions of each edge of the node. + var left = node.getX(); + var right = left + nodewidth; + var top = node.getY(); + var bottom = top + nodeheight; + + node.setStyle('width', 'initial'); + + oldwidth = nodewidth; + nodewidth = parseFloat(node.getComputedStyle('width'), 10); + + switch (refpoint) { + case '1': // Top-center + left = left + (oldwidth - nodewidth) / 2; + if (maxwidth && nodewidth > maxwidth) { + left = left + (nodewidth - maxwidth) / 2; + nodewidth = maxwidth; + node.setStyle('width', nodewidth + 'px'); + } + maxallowedwidth = 2 * Math.min(left + nodewidth / 2 - this.pdfx, this.pdfx + this.pdfwidth - (left + nodewidth / 2)); + if (maxallowedwidth > 0 && nodewidth > maxallowedwidth) { + left = left + (nodewidth - maxallowedwidth) / 2; + nodewidth = maxallowedwidth; + node.setStyle('width', nodewidth + 'px'); + } + break; + case '2': // Top-right + left = left + oldwidth - nodewidth; + if (maxwidth && nodewidth > maxwidth) { + left = left + nodewidth - maxwidth; + nodewidth = maxwidth; + node.setStyle('width', nodewidth + 'px'); + } + maxallowedwidth = left + nodewidth - this.pdfx; + if (maxallowedwidth > 0 && nodewidth > maxallowedwidth) { + left = this.pdfx; + nodewidth = maxallowedwidth; + node.setStyle('width', nodewidth + 'px'); + } + break; + case '0': // Top-left + default: + if (maxwidth && nodewidth > maxwidth) { + nodewidth = maxwidth; + node.setStyle('width', nodewidth + 'px'); + } + maxallowedwidth = this.pdfx + this.pdfwidth - left; + if (maxallowedwidth > 0 && nodewidth > maxallowedwidth) { + nodewidth = maxallowedwidth; + node.setStyle('width', nodewidth + 'px'); + } + } + + node.setX(left); + }, + + /** + * Returns true if any part of the element is placed outside of the PDF div, false otherwise. + * + * @param node + * @returns {boolean} + */ + is_out_of_bounds : function(node) { + // Get the width and height of the node. + var nodewidth = parseFloat(node.getComputedStyle('width'), 10); + var nodeheight = parseFloat(node.getComputedStyle('height'), 10); + + // Store the positions of each edge of the node. + var left = node.getX(); + var right = left + nodewidth; + var top = node.getY(); + var bottom = top + nodeheight; + + // Check if it is out of bounds horizontally. + if ((left < this.pdfx) || (right > (this.pdfx + this.pdfwidth))) { + return true; + } + + // Check if it is out of bounds vertically. + if ((top < this.pdfy) || (bottom > (this.pdfy + this.pdfheight))) { + return true; + } + + return false; + }, + + /** + * Perform an AJAX call and save the positions of the elements. + * + * @param e + */ + save_positions : function(e) { + // The parameters to send the AJAX call. + var params = { + cmid: this.cmid, + values: [] + }; + + // Go through the elements and save their positions. + for (var key in this.elements) { + var element = this.elements[key]; + var node = Y.one('#element-' + element['id']); + + // Get the current X and Y positions for this element. + var posx = node.getX() - this.pdfx; + var posy = node.getY() - this.pdfy; + + var nodewidth = parseFloat(node.getComputedStyle('width'), 10); + + switch (element['refpoint']) { + case '1': // Top-center + posx += nodewidth / 2; + break; + case '2': // Top-right + posx += nodewidth; + break; + } + + // Set the parameters to pass to the AJAX request. + params.values.push({ + id: element['id'], + posx: Math.round(parseFloat(posx / this.pixelsinmm, 10)), + posy: Math.round(parseFloat(posy / this.pixelsinmm, 10)) + }); + } + + params.values = JSON.stringify(params.values); + + // Save these positions. + Y.io(M.cfg.wwwroot + '/mod/customcert/rest.php', { + method: 'POST', + data: params, + on: { + failure: function(tid, response) { + this.ajax_failure(response); + e.preventDefault(); + } + }, + context: this + }) + + }, + + /** + * Handles any failures during an AJAX call. + * + * @param response + * @returns {M.core.exception} + */ + ajax_failure : function(response) { + var e = { + name: response.status + ' ' + response.statusText, + message: response.responseText + }; + return new M.core.exception(e); + } +} \ No newline at end of file