You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1570 lines
60 KiB
1570 lines
60 KiB
<?php
|
|
// This file is part of Moodle - http://moodle.org/
|
|
//
|
|
// Moodle is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Moodle is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
/**
|
|
* @package mod_pdfannotator
|
|
* @copyright 2018 CiL RWTH Aachen
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
defined('MOODLE_INTERNAL') || die;
|
|
|
|
require_once("$CFG->libdir/filelib.php");
|
|
require_once("$CFG->libdir/resourcelib.php");
|
|
require_once("$CFG->dirroot/mod/pdfannotator/lib.php");
|
|
|
|
/**
|
|
* Display embedded pdfannotator file.
|
|
* @param object $pdfannotator
|
|
* @param object $cm
|
|
* @param object $course
|
|
* @param stored_file $file main file
|
|
* @return does not return
|
|
*/
|
|
function pdfannotator_display_embed($pdfannotator, $cm, $course, $file, $page = 1, $annoid = null, $commid = null) {
|
|
global $CFG, $PAGE, $OUTPUT, $USER;
|
|
require_once($CFG->dirroot . '/mod/pdfannotator/classes/output/index.php');
|
|
|
|
// The revision attribute's existance is demanded by moodle for versioning and could be saved in the pdfannotator table in the future.
|
|
// Note, however, that we forbid file replacement in order to prevent a change of meaning in other people's comments.
|
|
$pdfannotator->revision = 1;
|
|
|
|
$context = context_module::instance($cm->id);
|
|
$path = '/' . $context->id . '/mod_pdfannotator/content/' . $pdfannotator->revision . $file->get_filepath() . $file->get_filename();
|
|
$fullurl = file_encode_url($CFG->wwwroot . '/pluginfile.php', $path, false);
|
|
|
|
$documentobject = new stdClass();
|
|
$documentobject->annotatorid = $pdfannotator->id;
|
|
$documentobject->fullurl = $fullurl;
|
|
|
|
$stringman = get_string_manager();
|
|
// With this method you get the strings of the language-Files.
|
|
$strings = $stringman->load_component_strings('pdfannotator', 'en');
|
|
// Method to use the language-strings in javascript.
|
|
$PAGE->requires->strings_for_js(array_keys($strings), 'pdfannotator');
|
|
// Load and execute the javascript files.
|
|
$PAGE->requires->js(new moodle_url("/mod/pdfannotator/shared/pdf.js"));
|
|
$PAGE->requires->js(new moodle_url("/mod/pdfannotator/shared/pdf_viewer.js"));
|
|
$PAGE->requires->js(new moodle_url("/mod/pdfannotator/shared/textclipper.js"));
|
|
$PAGE->requires->js(new moodle_url("/mod/pdfannotator/shared/index.js?ver=00011"));
|
|
$PAGE->requires->js(new moodle_url("/mod/pdfannotator/shared/locallib.js?ver=00002"));
|
|
|
|
if (has_capability('mod/pdfannotator:administrateuserinput', $context)) {
|
|
$administratesuserinput = true;
|
|
} else {
|
|
$administratesuserinput = false;
|
|
}
|
|
|
|
// Pass parameters from PHP to JavaScript.
|
|
|
|
// 1. Toolbar settings.
|
|
$toolbarsettings = new stdClass();
|
|
$toolbarsettings->use_studenttextbox = $pdfannotator->use_studenttextbox;
|
|
$toolbarsettings->use_studentdrawing = $pdfannotator->use_studentdrawing;
|
|
$toolbarsettings->useprint = $pdfannotator->useprint;
|
|
// 2. Capabilities.
|
|
$capabilities = new stdClass();
|
|
$capabilities->viewquestions = has_capability('mod/pdfannotator:viewquestions', $context);
|
|
$capabilities->viewanswers = has_capability('mod/pdfannotator:viewanswers', $context);
|
|
$capabilities->viewposts = has_capability('mod/pdfannotator:viewposts', $context);
|
|
$capabilities->viewreports = has_capability('mod/pdfannotator:viewreports', $context);
|
|
$capabilities->deleteany = has_capability('mod/pdfannotator:deleteany', $context);
|
|
$capabilities->usetextbox = has_capability('mod/pdfannotator:usetextbox', $context);
|
|
$capabilities->usedrawing = has_capability('mod/pdfannotator:usedrawing', $context);
|
|
|
|
$params = array($cm, $documentobject, $USER->id, $administratesuserinput, $toolbarsettings, $page, $annoid, $commid, $capabilities);
|
|
$PAGE->requires->js_init_call('adjustPdfannotatorNavbar', null, true);
|
|
$PAGE->requires->js_init_call('startIndex', $params, true);
|
|
// The renderer renders the original index.php / takes the template and renders it.
|
|
$myrenderer = $PAGE->get_renderer('mod_pdfannotator');
|
|
echo $myrenderer->render_index(new index($pdfannotator, $administratesuserinput, $file));
|
|
|
|
pdfannotator_print_intro($pdfannotator, $cm, $course);
|
|
|
|
echo $OUTPUT->footer();
|
|
die;
|
|
}
|
|
|
|
function pdfannotator_get_instance_name($id) {
|
|
|
|
global $DB;
|
|
return $DB->get_field('pdfannotator', 'name', array('id' => $id), $strictness = MUST_EXIST);
|
|
}
|
|
|
|
function pdfannotator_get_course_name_by_id($courseid) {
|
|
global $DB;
|
|
return $DB->get_field('course', 'fullname', array('id' => $courseid), $strictness = MUST_EXIST);
|
|
}
|
|
|
|
function pdfannotator_get_username($userid) {
|
|
global $DB;
|
|
$user = $DB->get_record('user', array('id' => $userid));
|
|
return fullname($user);
|
|
}
|
|
|
|
function pdfannotator_get_annotationtype_id($typename) {
|
|
global $DB;
|
|
if ($typename == 'point') {
|
|
$typename = 'pin';
|
|
}
|
|
$result = $DB->get_records('pdfannotator_annotationtypes', array('name' => $typename));
|
|
foreach ($result as $r) {
|
|
return $r->id;
|
|
}
|
|
}
|
|
|
|
function pdfannotator_get_annotationtype_name($typeid) {
|
|
global $DB;
|
|
$result = $DB->get_records('pdfannotator_annotationtypes', array('id' => $typeid));
|
|
foreach ($result as $r) {
|
|
return $r->name;
|
|
}
|
|
}
|
|
|
|
/* function pdfannotator_get_typename_of_annotation($annotationid) {
|
|
global $DB;
|
|
$result = $DB->get_records('pdfannotator_annotations', array('id' => $annotationid));
|
|
return pdfannotator_get_annotationtype_name($result[$annotationid]->annotationtypeid);
|
|
} */
|
|
|
|
function pdfannotator_handle_latex($subject) {
|
|
|
|
global $CFG;
|
|
require_once($CFG->dirroot . '/mod/pdfannotator/constants.php');
|
|
|
|
// Look for these formulae: $$ ... $$, \( ... \) and \[ ... \]
|
|
// !!! keep indentation!
|
|
$pattern = <<<'SIGN'
|
|
~(?:\$\$.*?\$\$)|(?:\\\(.*?\\\))|(?:\\\[.*?\\\])~
|
|
SIGN;
|
|
// Working, but less readable, alternative: $pattern = '~(?:\\$\\$.*?\\$\\$)|(?:\\\\\\(.*?\\\\\\))|(?:\\\\\\[.*?\\\\\\])~';
|
|
|
|
$matches = array();
|
|
$hits = preg_match_all($pattern, $subject, $matches, PREG_OFFSET_CAPTURE);
|
|
|
|
if ($hits == 0) {
|
|
return $subject;
|
|
}
|
|
|
|
$textstart = 0;
|
|
$formulalength = 0;
|
|
$formulaoffset = 0;
|
|
$result = [];
|
|
$matches = $matches[0];
|
|
foreach ($matches as $match) {
|
|
$formulalength = strlen($match[0]);
|
|
$formulaoffset = $match[1];
|
|
$result[] = trim(substr($subject, $textstart, $formulaoffset - $textstart));
|
|
$result[] = pdfannotator_process_latex($match[0]);
|
|
$textstart = $formulaoffset + $formulalength;
|
|
}
|
|
if ($textstart != strlen($subject) - 1) {
|
|
$result[] = trim(substr($subject, $textstart, strlen($subject) - $textstart));
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Function takes a latex code string, modifies and url encodes it for the Google Api to process,
|
|
* and returns the resulting image along with its height
|
|
*
|
|
* @param type $string
|
|
* @return type
|
|
*/
|
|
function pdfannotator_process_latex($string) {
|
|
$string = str_replace('\xrightarrow', '\rightarrow', $string);
|
|
$string = str_replace('\xlefttarrow', '\leftarrow', $string);
|
|
|
|
$pos = strpos($string, '\\[');
|
|
if ($pos !== false) {
|
|
$string = substr_replace($string, '', $pos, strlen('\\['));
|
|
}
|
|
|
|
$pos = strpos($string, '\\(');
|
|
if ($pos !== false) {
|
|
$string = substr_replace($string, '', $pos, strlen('\\('));
|
|
}
|
|
|
|
$string = str_replace('\\]', '', $string);
|
|
|
|
$string = str_replace('\\)', '', $string);
|
|
|
|
$string = str_replace('\begin{aligned}', '', $string);
|
|
$string = str_replace('\end{aligned}', '', $string);
|
|
|
|
$string = str_replace('\begin{align*}', '', $string);
|
|
$string = str_replace('\end{align*}', '', $string);
|
|
|
|
// Find any backslash preceding a ( or [ and replace it with \backslash
|
|
$pattern = '~\\\\(?=[\\\(\\\[])~';
|
|
$string = preg_replace($pattern, '\\backslash', $string);
|
|
|
|
$length = strlen($string);
|
|
|
|
$im = null;
|
|
if ($length <= 200) { // Google API constraint XXX find better alternative if possible.
|
|
$latexdata = urlencode($string);
|
|
$requesturl = LATEX_TO_PNG_REQUEST . $latexdata;
|
|
$im = @file_get_contents($requesturl); // '@' suppresses warnings so that one failed google request doesn't prevent the pdf from being printed,
|
|
// but just the one formula from being presented as a picture.
|
|
}
|
|
if ($im != null) {
|
|
$array = [];
|
|
try {
|
|
list($width, $height) = getimagesize($requesturl); // XXX alternative: acess height by decoding the string (saving the extra server request)?
|
|
if ($height != null) {
|
|
$imagedata = IMAGE_PREFIX . base64_encode($im); // Image.
|
|
$array['image'] = $imagedata;
|
|
$array['imageheight'] = $height;
|
|
return $array;
|
|
}
|
|
} catch (Exception $ex) {
|
|
return $string;
|
|
}
|
|
} else {
|
|
return $string;
|
|
}
|
|
}
|
|
|
|
function pdfannotator_notify_manager($recipient, $course, $cm, $name, $messagetext, $anonymous = false) {
|
|
global $USER;
|
|
global $CFG;
|
|
$userfrom = $USER;
|
|
if ($anonymous) {
|
|
$userfrom = clone($USER);
|
|
$userfrom->firstname = get_string('pdfannotatorname', 'pdfannotator') . ':';
|
|
$userfrom->lastname = $cm->name;
|
|
}
|
|
$message = new \core\message\message();
|
|
$message->component = 'mod_pdfannotator';
|
|
$message->name = $name;
|
|
$message->courseid = $course->id;
|
|
$message->userfrom = $userfrom;
|
|
$message->userto = $recipient;
|
|
$message->subject = get_string('notificationsubject:' . $name, 'pdfannotator', $cm->name);
|
|
$message->fullmessage = $messagetext->text;
|
|
$message->fullmessageformat = FORMAT_PLAIN;
|
|
$message->fullmessagehtml = $messagetext->html;
|
|
$message->smallmessage = get_string('notificationsubject:' . $name, 'pdfannotator', $cm->name);
|
|
$message->notification = 1; // For personal messages '0' important: the 1 without '' and 0 with ''.
|
|
$message->contexturl = $messagetext->url;
|
|
$message->contexturlname = 'Context name';
|
|
$content = array('*' => array('header' => ' test ', 'footer' => ' test ')); // Extra content for specific processor.
|
|
|
|
$messageid = message_send($message);
|
|
|
|
return $messageid;
|
|
}
|
|
|
|
function pdfannotator_format_notification_message_text($course, $cm, $context, $modulename, $pdfannotatorname, $paramsforlanguagestring, $messagetype) {
|
|
global $CFG;
|
|
$formatparams = array('context' => $context->get_course_context());
|
|
$posttext = format_string($course->shortname, true, $formatparams) .
|
|
' -> ' .
|
|
$modulename .
|
|
' -> ' .
|
|
format_string($pdfannotatorname, true, $formatparams) . "\n";
|
|
$posttext .= '---------------------------------------------------------------------' . "\n";
|
|
$posttext .= "\n";
|
|
$posttext .= get_string($messagetype . 'text', 'pdfannotator', $paramsforlanguagestring) . "\n---------------------------------------------------------------------\n";
|
|
return $posttext;
|
|
}
|
|
|
|
/**
|
|
* Format a notification for HTML.
|
|
*
|
|
* @param string $messagetype
|
|
* @param stdClass $info
|
|
* @param stdClass $course
|
|
* @param stdClass $context
|
|
* @param string $modulename
|
|
* @param stdClass $coursemodule
|
|
* @param string $assignmentname
|
|
*/
|
|
function pdfannotator_format_notification_message_html($course, $cm, $context, $modulename, $pdfannotatorname, $report, $messagetype) {
|
|
global $CFG, $USER;
|
|
$formatparams = array('context' => $context->get_course_context());
|
|
$posthtml = '<p><font face="sans-serif">' .
|
|
'<a href="' . $CFG->wwwroot . '/course/view.php?id=' . $course->id . '">' .
|
|
format_string($course->shortname, true, $formatparams) .
|
|
'</a> ->' .
|
|
'<a href="' . $CFG->wwwroot . '/mod/pdfannotator/index.php?id=' . $course->id . '">' .
|
|
$modulename .
|
|
'</a> ->' .
|
|
'<a href="' . $CFG->wwwroot . '/mod/pdfannotator/view.php?id=' . $cm->id . '">' .
|
|
format_string($pdfannotatorname, true, $formatparams) .
|
|
'</a></font></p>';
|
|
$posthtml .= '<hr /><font face="sans-serif">';
|
|
$report->urltoreport = $CFG->wwwroot . '/mod/pdfannotator/view.php?id=' . $cm->id . '&action=overview';
|
|
$posthtml .= '<p>' . get_string($messagetype . 'html', 'pdfannotator', $report) . '</p>';
|
|
$linktonotificationsettingspage = new moodle_url('/message/notificationpreferences.php', array('userid' => $USER->id));
|
|
$linktonotificationsettingspage = $linktonotificationsettingspage->__toString();
|
|
$posthtml .= '</font><hr />';
|
|
$posthtml .= '<font face="sans-serif"><p>' . get_string('unsubscribe_notification', 'pdfannotator', $linktonotificationsettingspage) . '</p></font>';
|
|
return $posthtml;
|
|
}
|
|
|
|
/**
|
|
* Internal function - create click to open text with link.
|
|
*/
|
|
function pdfannotator_get_clicktoopen($file, $revision, $extra = '') {
|
|
global $CFG;
|
|
|
|
$filename = $file->get_filename();
|
|
$path = '/' . $file->get_contextid() . '/mod_pdfannotator/content/' . $revision . $file->get_filepath() . $file->get_filename();
|
|
$fullurl = file_encode_url($CFG->wwwroot . '/pluginfile.php', $path, false);
|
|
|
|
$string = get_string('clicktoopen2', 'pdfannotator', "<a href=\"$fullurl\" $extra>$filename</a>");
|
|
|
|
return $string;
|
|
}
|
|
|
|
/**
|
|
* Internal function - create click to open text with link.
|
|
*/
|
|
function pdfannotator_get_clicktodownload($file, $revision) {
|
|
global $CFG;
|
|
|
|
$filename = $file->get_filename();
|
|
$path = '/' . $file->get_contextid() . '/mod_pdfannotator/content/' . $revision . $file->get_filepath() . $file->get_filename();
|
|
$fullurl = file_encode_url($CFG->wwwroot . '/pluginfile.php', $path, true);
|
|
|
|
$string = get_string('clicktodownload', 'pdfannotator', "<a href=\"$fullurl\">$filename</a>");
|
|
|
|
return $string;
|
|
}
|
|
|
|
/**
|
|
* Print pdfannotator header.
|
|
* @param object $pdfannotator
|
|
* @param object $cm
|
|
* @param object $course
|
|
* @return void
|
|
*/
|
|
function pdfannotator_print_header($pdfannotator, $cm, $course) {
|
|
global $PAGE, $OUTPUT;
|
|
$PAGE->set_title($course->shortname . ': ' . $pdfannotator->name);
|
|
$PAGE->set_heading($course->fullname);
|
|
$PAGE->set_activity_record($pdfannotator);
|
|
echo $OUTPUT->header();
|
|
}
|
|
|
|
/**
|
|
* Gets details of the file to cache in course cache to be displayed using {@link pdfannotator_get_optional_details()}
|
|
*
|
|
* @param object $pdfannotator pdfannotator table row (only property 'displayoptions' is used here)
|
|
* @param object $cm Course-module table row
|
|
* @return string Size and type or empty string if show options are not enabled
|
|
*/
|
|
function pdfannotator_get_file_details($pdfannotator, $cm) {
|
|
$filedetails = array();
|
|
|
|
$context = context_module::instance($cm->id);
|
|
$fs = get_file_storage();
|
|
$files = $fs->get_area_files($context->id, 'mod_pdfannotator', 'content', 0, 'sortorder DESC, id ASC', false);
|
|
// For a typical file pdfannotator, the sortorder is 1 for the main file
|
|
// and 0 for all other files. This sort approach is used just in case
|
|
// there are situations where the file has a different sort order.
|
|
$mainfile = $files ? reset($files) : null;
|
|
|
|
foreach ($files as $file) {
|
|
// This will also synchronize the file size for external files if needed.
|
|
$filedetails['size'] += $file->get_filesize();
|
|
if ($file->get_repository_id()) {
|
|
// If file is a reference the 'size' attribute can not be cached.
|
|
$filedetails['isref'] = true;
|
|
}
|
|
}
|
|
|
|
return $filedetails;
|
|
}
|
|
|
|
/**
|
|
* Print pdfannotator introduction.
|
|
* @param object $pdfannotator
|
|
* @param object $cm
|
|
* @param object $course
|
|
* @param bool $ignoresettings print even if not specified in modedit
|
|
* @return void
|
|
*/
|
|
function pdfannotator_print_intro($pdfannotator, $cm, $course, $ignoresettings = false) {
|
|
global $OUTPUT;
|
|
if ($ignoresettings) {
|
|
$gotintro = trim(strip_tags($pdfannotator->intro));
|
|
if ($gotintro || $extraintro) {
|
|
echo $OUTPUT->box_start('mod_introbox', 'pdfannotatorintro');
|
|
if ($gotintro) {
|
|
echo format_module_intro('pdfannotator', $pdfannotator, $cm->id);
|
|
}
|
|
echo $extraintro;
|
|
echo $OUTPUT->box_end();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Print warning that file can not be found.
|
|
* @param object $pdfannotator
|
|
* @param object $cm
|
|
* @param object $course
|
|
* @return void, does not return
|
|
*/
|
|
function pdfannotator_print_filenotfound($pdfannotator, $cm, $course) {
|
|
global $DB, $OUTPUT;
|
|
|
|
pdfannotator_print_header($pdfannotator, $cm, $course);
|
|
// pdfannotator_print_heading($pdfannotator, $cm, $course);//TODO Method is not defined.
|
|
pdfannotator_print_intro($pdfannotator, $cm, $course);
|
|
echo $OUTPUT->notification(get_string('filenotfound', 'pdfannotator'));
|
|
|
|
echo $OUTPUT->footer();
|
|
die;
|
|
}
|
|
|
|
/**
|
|
* Function returns the number of new comments, drawings and textboxes*
|
|
* in this annotator. 'New' is defined here as 'no older than 24h' but
|
|
* can easily be changed to another time span.
|
|
* *Drawings and textboxes cannot be commented. In their case (only),
|
|
* therefore, annotations are counted.
|
|
*
|
|
* @global type $DB
|
|
*/
|
|
function pdfannotator_get_number_of_new_activities($annotatorid) {
|
|
|
|
global $DB;
|
|
|
|
$parameters = array();
|
|
$parameters[] = $annotatorid;
|
|
$parameters[] = strtotime("-1 day");
|
|
|
|
$sql = "SELECT c.id FROM {pdfannotator_annotations} a JOIN {pdfannotator_comments} c ON c.annotationid = a.id "
|
|
. "WHERE a.pdfannotatorid = ? AND c.timemodified >= ?";
|
|
$sql2 = "SELECT a.id FROM {pdfannotator_annotations} a JOIN {pdfannotator_annotationtypes} t ON a.annotationtypeid = t.id "
|
|
. "WHERE a.pdfannotatorid = ? AND a.timecreated >= ? AND t.name IN('drawing','textbox')";
|
|
|
|
return ( count($DB->get_records_sql($sql, $parameters)) + count($DB->get_records_sql($sql2, $parameters)) );
|
|
}
|
|
|
|
/**
|
|
* Function returns the datetime of the last modification on or in the specified annotator.
|
|
* The modification can be the creation of the annotator, a change of title or description,
|
|
* a new annotation or a new comment. Reports are not considered.
|
|
*
|
|
* @global type $DB
|
|
* @param int $annotatorid
|
|
* @return datetime $timemodified
|
|
* The timestamp can be transformed into a readable string with this moodle method: userdate($timestamp, $format = '', $timezone = 99, $fixday = true, $fixhour = true);
|
|
*/
|
|
function pdfannotator_get_datetime_of_last_modification($annotatorid) {
|
|
|
|
global $DB;
|
|
|
|
// 1. When was the last time the annotator itself (i.e. its title, description or pdf) was modified?
|
|
$timemodified = $DB->get_record('pdfannotator', array('id' => $annotatorid), 'timemodified', MUST_EXIST);
|
|
$timemodified = $timemodified->timemodified;
|
|
|
|
// 2. When was the last time an annotation or a comment was added in the specified annotator?
|
|
$sql = "SELECT max(a.timecreated) as 'last_annotation', max(c.timemodified) as 'last_comment' "
|
|
. "FROM {pdfannotator_annotations} a LEFT OUTER JOIN {pdfannotator_comments} c ON a.id = c.annotationid "
|
|
. "WHERE a.pdfannotatorid = ?";
|
|
$newposts = $DB->get_records_sql($sql, array($annotatorid));
|
|
|
|
if (!empty($newposts)) {
|
|
|
|
foreach ($newposts as $entry) {
|
|
|
|
// 2.a) If there is an annotation younger than the creation/modification of the annotator, set timemodified to the annotation time.
|
|
if (!empty($entry->last_annotation) && ($entry->last_annotation > $timemodified)) {
|
|
$timemodified = $entry->last_annotation;
|
|
}
|
|
// 2.b) If there is a comment younger than the creation/modification of the annotator or its newest annotation, set timemodified to the comment time.
|
|
if (!empty($entry->last_comment) && ($entry->last_comment > $timemodified)) {
|
|
$timemodified = $entry->last_comment;
|
|
}
|
|
return $timemodified;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* File browsing support class
|
|
*/
|
|
class pdfannotator_content_file_info extends file_info_stored {
|
|
|
|
public function get_parent() {
|
|
if ($this->lf->get_filepath() === '/' and $this->lf->get_filename() === '.') {
|
|
return $this->browser->get_file_info($this->context);
|
|
}
|
|
return parent::get_parent();
|
|
}
|
|
|
|
public function get_visible_name() {
|
|
if ($this->lf->get_filepath() === '/' and $this->lf->get_filename() === '.') {
|
|
return $this->topvisiblename;
|
|
}
|
|
return parent::get_visible_name();
|
|
}
|
|
|
|
}
|
|
|
|
function pdfannotator_set_mainfile($data) {
|
|
global $DB;
|
|
$fs = get_file_storage();
|
|
$cmid = $data->coursemodule;
|
|
$draftitemid = $data->files; // Name from the filemanger.
|
|
|
|
$context = context_module::instance($cmid);
|
|
if ($draftitemid) {
|
|
file_save_draft_area_files($draftitemid, $context->id, 'mod_pdfannotator', 'content', 0, array('subdirs' => true));
|
|
}
|
|
$files = $fs->get_area_files($context->id, 'mod_pdfannotator', 'content', 0, 'sortorder', false);
|
|
if (count($files) == 1) {
|
|
// Only one file attached, set it as main file automatically.
|
|
$file = reset($files);
|
|
file_set_sortorder($context->id, 'mod_pdfannotator', 'content', 0, $file->get_filepath(), $file->get_filename(), 1);
|
|
}
|
|
}
|
|
|
|
function pdfannotator_render_listitem_actions(array $actions = null) {
|
|
$menu = new action_menu();
|
|
$menu->attributes['class'] .= ' course-item-actions item-actions';
|
|
$hasitems = false;
|
|
foreach ($actions as $key => $action) {
|
|
$hasitems = true;
|
|
$menu->add(new action_menu_link(
|
|
$action['url'], $action['icon'], $action['string'], in_array($key, array()), array('data-action' => $key, 'class' => 'action-' . $key)
|
|
));
|
|
}
|
|
if (!$hasitems) {
|
|
return '';
|
|
}
|
|
return pdfannotator_render_action_menu($menu);
|
|
}
|
|
|
|
function pdfannotator_render_action_menu($menu) {
|
|
global $OUTPUT;
|
|
return $OUTPUT->render($menu);
|
|
}
|
|
|
|
function pdfannotator_subscribe_all($annotatorid) {
|
|
global $DB;
|
|
$sql = "SELECT id FROM {pdfannotator_annotations} "
|
|
. "WHERE pdfannotatorid = ? AND annotationtypeid NOT IN "
|
|
. "(SELECT id FROM {pdfannotator_annotationtypes} WHERE name = ? OR name = ?)";
|
|
$params = [$annotatorid, 'drawing', 'textbox'];
|
|
$ids = $DB->get_fieldset_sql($sql, $params);
|
|
foreach ($ids as $annotationid) {
|
|
pdfannotator_comment::insert_subscription($annotationid);
|
|
}
|
|
}
|
|
|
|
function pdfannotator_unsubscribe_all($annotatorid) {
|
|
global $DB, $USER;
|
|
$sql = "SELECT a.id FROM {pdfannotator_annotations} a JOIN {pdfannotator_subscriptions} s "
|
|
. "ON s.annotationid = a.id AND s.userid = ? WHERE pdfannotatorid = ?";
|
|
$ids = $DB->get_fieldset_sql($sql, [$USER->id, $annotatorid]);
|
|
foreach ($ids as $annotationid) {
|
|
pdfannotator_comment::delete_subscription($annotationid);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks wether a user has subscribed to all questions in an annotator.
|
|
* Returns 1 if all questions are subscribed, 0 if no questions are subscribed and -1 if at least one but not all questions are subscribed.
|
|
* @global type $DB
|
|
* @global type $USER
|
|
* @param type $annotatorid
|
|
*/
|
|
function pdfannotator_subscribed($annotatorid) {
|
|
global $DB, $USER;
|
|
$sql = "SELECT COUNT(*) FROM {pdfannotator_annotations} a JOIN {pdfannotator_subscriptions} s "
|
|
. "ON s.annotationid = a.id AND s.userid = ? WHERE a.pdfannotatorid = ?";
|
|
$subscriptions = $DB->count_records_sql($sql, [$USER->id, $annotatorid]);
|
|
$sql = "SELECT COUNT(*) FROM {pdfannotator_annotations} "
|
|
. "WHERE pdfannotatorid = ? AND annotationtypeid NOT IN "
|
|
. "(SELECT id FROM {pdfannotator_annotationtypes} WHERE name = ? OR name = ?)";
|
|
$params = [$annotatorid, 'drawing', 'textbox'];
|
|
$annotations = $DB->count_records_sql($sql, $params);
|
|
|
|
if ($subscriptions === 0) {
|
|
return 0;
|
|
} else if ($subscriptions === $annotations) {
|
|
return 1;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
/**
|
|
*
|
|
* @param type $timestamp
|
|
* @return string Day, D Month Y, Time
|
|
*/
|
|
function pdfannotator_get_user_datetime($timestamp) {
|
|
$userdatetime = userdate($timestamp, $format = '', $timezone = 99, $fixday = true, $fixhour = true); // Method in lib/moodlelib.php
|
|
return $userdatetime;
|
|
}
|
|
/**
|
|
*
|
|
* @param type $timestamp
|
|
* @return string
|
|
*/
|
|
function pdfannotator_get_user_datetime_shortformat($timestamp) {
|
|
$shortformat = get_string('strftimedatetime', 'pdfannotator'); // Format strings in moodle\lang\en\langconfig.php.
|
|
$userdatetime = userdate($timestamp, $shortformat, $timezone = 99, $fixday = true, $fixhour = true); // Method in lib/moodlelib.php
|
|
return $userdatetime;
|
|
}
|
|
|
|
/**
|
|
* Function is executed each time one of the overview categories is accessed.
|
|
* It creates the tab navigation and makes javascript accessible.
|
|
*
|
|
* @param type $CFG
|
|
* @param type $PAGE
|
|
* @param type $myrenderer
|
|
* @param type $taburl
|
|
* @param type $action
|
|
* @param type $pdfannotator
|
|
* @param type $context
|
|
*/
|
|
function pdfannotator_prepare_overviewpage($cmid, $myrenderer, $taburl, $action, $pdfannotator, $context) {
|
|
|
|
global $CFG, $PAGE;
|
|
|
|
// require_once($CFG->dirroot . '/mod/pdfannotator/classes/output/overview.php');
|
|
|
|
$PAGE->set_title("overview");
|
|
|
|
// 1.1 Display tab navigation.
|
|
echo $myrenderer->pdfannotator_render_tabs($taburl, $action, $pdfannotator->name, $context);
|
|
|
|
// 1.2 Give javascript (see below) access to the language string repository.
|
|
$stringman = get_string_manager();
|
|
$strings = $stringman->load_component_strings('pdfannotator', 'en'); // Method gets the strings of the language files.
|
|
$PAGE->requires->strings_for_js(array_keys($strings), 'pdfannotator'); // Method to use the language-strings in javascript.
|
|
// 1.3 Add the javascript file that determines the dynamic behaviour of the page.
|
|
$PAGE->requires->js(new moodle_url("/mod/pdfannotator/shared/locallib.js?ver=00002"));
|
|
$PAGE->requires->js(new moodle_url("/mod/pdfannotator/shared/overview.js?ver=00002"));
|
|
|
|
// 1.4 Check user capabilities to view the different categories.
|
|
// The argument 'false' disregards administrator's magical 'doanything' power.
|
|
$capabilities = new stdClass();
|
|
$capabilities->viewquestions = has_capability('mod/pdfannotator:viewquestions', $context);
|
|
$capabilities->viewanswers = has_capability('mod/pdfannotator:viewanswers', $context);
|
|
$capabilities->viewposts = has_capability('mod/pdfannotator:viewposts', $context);
|
|
$capabilities->viewreports = has_capability('mod/pdfannotator:viewreports', $context);
|
|
|
|
$params = array($pdfannotator->id, $cmid, $capabilities);
|
|
$PAGE->requires->js_init_call('startOverview', $params, true); // 1. name of JS function, 2. parameters.
|
|
|
|
}
|
|
/**
|
|
* Function serves as subcontroller that tells the annotator model to collect
|
|
* all unsolved questions asked in this course.
|
|
*
|
|
* @global type $OUTPUT
|
|
* @param int $openannotator
|
|
* @param int $courseid
|
|
*/
|
|
function pdfannotator_get_unsolved_questions($courseid) {
|
|
|
|
global $DB;
|
|
|
|
$coll = [];
|
|
|
|
$sql = "SELECT a.id as annoid, a.page, a.pdfannotatorid, p.name AS pdfannotatorname, p.usevotes, "
|
|
. "cm.id AS cmid, c.id as commentid, c.content, c.userid, c.timecreated, c.isdeleted, "
|
|
. "SUM(vote) AS votes, COUNT(answ.id)-1 AS answercount, MAX(answ.timecreated) AS lastanswered "
|
|
. "FROM {pdfannotator_annotations} a "
|
|
. "JOIN {pdfannotator_comments} c ON c.annotationid = a.id "
|
|
. "JOIN {pdfannotator} p ON a.pdfannotatorid = p.id "
|
|
. "JOIN {course_modules} cm ON p.id = cm.instance "
|
|
. "JOIN {modules} m ON cm.module = m.id "
|
|
. "LEFT JOIN {pdfannotator_votes} v ON c.id=v.commentid "
|
|
. "LEFT JOIN {pdfannotator_comments} answ ON answ.annotationid = a.id "
|
|
. "WHERE c.isquestion = 1 AND c.solved = 0 AND p.course = ? AND m.name = ? "
|
|
. "GROUP BY a.id, p.name, p.usevotes, cm.id, c.id";
|
|
|
|
$unsolvedquestions = $DB->get_records_sql($sql, array($courseid, 'pdfannotator'));
|
|
|
|
foreach ($unsolvedquestions as $question) {
|
|
|
|
if (empty($question->votes)) {
|
|
$question->votes = 0;
|
|
}
|
|
if ($question->usevotes == 0) {
|
|
$question->votes = '-';
|
|
}
|
|
if (empty($question->answercount)) {
|
|
$question->answercount = 0;
|
|
}
|
|
if (!($question->lastanswered == $question->timecreated)) {
|
|
$conditions = array('annotationid' => $question->annoid, 'timemodified' => $question->lastanswered, 'isquestion' => 0);
|
|
$question->lastuser = $DB->get_record('pdfannotator_comments', $conditions, 'userid', IGNORE_MISSING);
|
|
if (!empty($question->lastuser)) {
|
|
$question->lastuser = $question->lastuser->userid;
|
|
}
|
|
}
|
|
if ($question->isdeleted == 1) {
|
|
$question->content = "<em>" . get_string('deletedQuestion', 'pdfannotator') . "</em>";
|
|
} else {
|
|
$question->content = format_text($question->content);
|
|
}
|
|
$question->link = (new moodle_url('/mod/pdfannotator/view.php', array('id' => $question->cmid,
|
|
'page' => $question->page, 'annoid' => $question->annoid, 'commid' => $question->commentid)))->out();
|
|
$coll[] = $question;
|
|
}
|
|
return $coll;
|
|
}
|
|
/**
|
|
* Function serves as subcontroller that tells the annotator model to collect all
|
|
* questions and answers this user posted in the course.
|
|
*
|
|
* @global type $DB
|
|
* @global type $USER
|
|
* @param int $courseid
|
|
* @return type
|
|
*/
|
|
function pdfannotator_get_posts_by_this_user($courseid) {
|
|
|
|
global $DB, $USER;
|
|
|
|
$coll = [];
|
|
|
|
$sql = "SELECT c.id as commid, c.annotationid, c.content, c.timemodified, a.id AS annoid, "
|
|
. "a.page, a.pdfannotatorid, p.name AS pdfannotatorname, p.usevotes, cm.id AS cmid, "
|
|
. "SUM(v.vote) AS votes "
|
|
. "FROM {pdfannotator_comments} c "
|
|
. "JOIN {pdfannotator_annotations} a ON c.annotationid = a.id "
|
|
. "JOIN {pdfannotator} p ON a.pdfannotatorid = p.id "
|
|
. "JOIN {course_modules} cm ON p.id = cm.instance "
|
|
. "JOIN {modules} m ON cm.module = m.id "
|
|
. "LEFT JOIN {pdfannotator_votes} v ON c.id = v.commentid "
|
|
. "WHERE c.userid = ? AND p.course = ? AND m.name = ?"
|
|
. "GROUP BY a.id, p.name, p.usevotes, cm.id, c.id";
|
|
|
|
$posts = $DB->get_records_sql($sql, array($USER->id, $courseid, 'pdfannotator'));
|
|
|
|
foreach ($posts as $post) {
|
|
if (empty($post->votes)) {
|
|
$post->votes = 0;
|
|
}
|
|
if ($post->usevotes == 0) {
|
|
$post->votes = '-';
|
|
}
|
|
$params = array('id' => $post->cmid, 'page' => $post->page, 'annoid' => $post->annotationid, 'commid' => $post->commid);
|
|
$post->link = (new moodle_url('/mod/pdfannotator/view.php', $params))->out();
|
|
$post->content = format_text($post->content);
|
|
$coll[] = $post;
|
|
}
|
|
return $coll;
|
|
}
|
|
/**
|
|
* Function serves as subcontroller that tells the annotator model to collect
|
|
* all answers given to questions that the current user asked or subscribed to
|
|
* in this course.
|
|
*
|
|
* @global type $DB
|
|
* @global type $USER
|
|
* @param int $courseid
|
|
* @param int $seen
|
|
* @return array of stdClass objects
|
|
*/
|
|
function pdfannotator_get_answers_for_this_user($courseid, $seen = 0) {
|
|
|
|
global $DB, $USER;
|
|
|
|
$coll = [];
|
|
|
|
$sql = "SELECT c.id AS answerid, c.content AS answer, c.userid AS answeredby, "
|
|
. "c.timemodified, c.solved AS correct, a.id AS annoid, a.page, q.id AS questionid, "
|
|
. "q.content AS answeredquestion, q.isdeleted AS questiondeleted, p.id AS annotatorid, "
|
|
. "p.name AS pdfannotatorname, cm.id AS cmid "
|
|
. "FROM {pdfannotator_subscriptions} s "
|
|
. "JOIN {pdfannotator_annotations} a ON a.id = s.annotationid "
|
|
. "JOIN {pdfannotator_comments} q ON q.annotationid = a.id " // question comment
|
|
. "JOIN {pdfannotator_comments} c ON c.annotationid = a.id " // answer comment
|
|
. "JOIN {pdfannotator} p ON a.pdfannotatorid = p.id "
|
|
. "JOIN {course_modules} cm ON p.id = cm.instance "
|
|
. "JOIN {modules} m ON cm.module = m.id "
|
|
. "WHERE s.userid = ? AND p.course = ? AND m.name = ? AND c.seen = ? AND q.isquestion = 1 AND NOT c.isquestion = 1 AND NOT c.isdeleted = 1 "
|
|
. "ORDER BY annoid ASC";
|
|
|
|
$entries = $DB->get_records_sql($sql, array($USER->id, $courseid, 'pdfannotator', $seen));
|
|
|
|
foreach ($entries as $entry) {
|
|
$entry->link = (new moodle_url('/mod/pdfannotator/view.php',
|
|
array('id' => $entry->cmid, 'page' => $entry->page, 'annoid' => $entry->annoid, 'commid' => $entry->answerid)))->out();
|
|
$entry->questionlink = (new moodle_url('/mod/pdfannotator/view.php',
|
|
array('id' => $entry->cmid, 'page' => $entry->page, 'annoid' => $entry->annoid, 'commid' => $entry->questionid)))->out();
|
|
if ($entry->questiondeleted == 1) {
|
|
$entry->answeredquestion = get_string('deletedComment', 'pdfannotator');
|
|
}
|
|
$entry->answer = format_text($entry->answer);
|
|
$entry->answeredquestion = format_text($entry->answeredquestion);
|
|
$coll[] = $entry;
|
|
}
|
|
|
|
return $coll;
|
|
}
|
|
function pdfannotator_get_reports($courseid, $seen = 0) {
|
|
|
|
global $DB;
|
|
|
|
$coll = [];
|
|
|
|
// Retrieve reports from db as an array of stdClass objects, representing a report record each.
|
|
$sql = "SELECT r.id as reportid, r.commentid, r.message as report, r.userid AS reportinguser, r.timecreated, "
|
|
. "a.page, c.id AS commentid, c.annotationid, c.userid AS commentauthor, c.content AS reportedcomment, c.timecreated AS commenttime, c.visibility, "
|
|
. "p.id AS annotatorid, p.name AS pdfannotatorname, cm.id AS cmid "
|
|
. "FROM {pdfannotator_reports} r "
|
|
. "JOIN {pdfannotator_comments} c ON r.commentid = c.id "
|
|
. "JOIN {pdfannotator_annotations} a ON c.annotationid = a.id "
|
|
. "JOIN {pdfannotator} p ON a.pdfannotatorid = p.id "
|
|
. "JOIN {course_modules} cm ON p.id = cm.instance "
|
|
. "JOIN {modules} m ON cm.module = m.id "
|
|
. "WHERE r.courseid = ? AND r.seen = ? AND m.name = ?";
|
|
|
|
$reports = $DB->get_records_sql($sql, array($courseid, $seen, 'pdfannotator'));
|
|
|
|
foreach ($reports as $report) {
|
|
$report->link = (new moodle_url('/mod/pdfannotator/view.php',
|
|
array('id' => $report->cmid, 'page' => $report->page, 'annoid' => $report->annotationid, 'commid' => $report->commentid)))->out();
|
|
$report->reportedcomment = format_text($report->reportedcomment);
|
|
$report->report = format_text($report->report);
|
|
$coll[] = $report;
|
|
}
|
|
return $coll;
|
|
|
|
}
|
|
/**
|
|
* Function returns html code for an actiondropdown menu.
|
|
* At present it is used on the overview page, subpage 'overviewanswers'.
|
|
*
|
|
* XXX ideally replace with template and templatable.
|
|
*
|
|
* @return string
|
|
*/
|
|
function pdfannotator_anwers_add_actiondropdown($answerid, $annotatorid, $annotationid) {
|
|
|
|
$hideanswerid = 'hideanswer_' . $answerid;
|
|
$unsubscribeid = 'unsubscribe' . $annotationid;
|
|
|
|
$html = "<div class='dropdown'>" .
|
|
|
|
"<a tabindex='0' class='dropdown-toggle icon-no-margin' data-toggle='dropdown' role='button' "
|
|
."aria-haspopup='true' aria-expanded='false' aria-controls='action-menu-3-menu'>
|
|
<i class='icon fa fa-cog fa-fw ' aria-hidden='true' title='" . get_string('actiondropdown', 'pdfannotator')
|
|
. "' aria-label='" . get_string('actiondropdown', 'pdfannotator') . "'></i>
|
|
</a>" .
|
|
"<ul class='dropdown-menu'>" .
|
|
"<li><button class='dropdown-item comment-subscribe' type='button' id=$unsubscribeid>" .
|
|
"<i class='icon fa fa-bell-slash fa-fw'></i>".
|
|
get_string('unsubscribeQuestion', 'pdfannotator') .
|
|
"</button>" .
|
|
"</li>" .
|
|
"</ul>" .
|
|
"</div>";
|
|
|
|
return $html;
|
|
}
|
|
|
|
/**
|
|
* Function returns html code for an actiondropdown menu.
|
|
* At present it is used on the overview page, subpage 'overviewreports'.
|
|
*
|
|
* XXX ideally replace with template and templatable.
|
|
*
|
|
* @return string
|
|
*/
|
|
function pdfannotator_reports_add_actiondropdown($reportid, $commentid, $annotatorid, $annotationid) {
|
|
|
|
$html = "<div class='dropdown'>" .
|
|
"<a tabindex='0' class='dropdown-toggle icon-no-margin' data-toggle='dropdown' role='button' "
|
|
."aria-haspopup='true' aria-expanded='false' aria-controls='action-menu-3-menu'>
|
|
<i class='icon fa fa-cog fa-fw ' aria-hidden='true' title='" . get_string('actiondropdown', 'pdfannotator')
|
|
. "' aria-label='" . get_string('actiondropdown', 'pdfannotator') . "'></i>
|
|
</a>" .
|
|
"<ul class='dropdown-menu'>" .
|
|
"<li><button class='dropdown-item report-recycle' type='button' id=". $reportid . " data-annotator=" . $annotatorid . ">" .
|
|
"<i class='icon fa fa-trash fa-fw'></i>".
|
|
get_string('putinrecyclebin_report', 'pdfannotator').
|
|
"</button>".
|
|
"</li>" .
|
|
"</ul>" .
|
|
"</div>";
|
|
|
|
return $html;
|
|
|
|
}
|
|
/**
|
|
*
|
|
*
|
|
* @param type $reportid
|
|
* @param type $annotatorid
|
|
* @return string
|
|
*/
|
|
function pdfannotator_recycled_reports_add_actionsdropdown($reportid, $annotatorid) {
|
|
|
|
$html = "<div class='dropdown'>" .
|
|
"<a tabindex='0' class='dropdown-toggle icon-no-margin' data-toggle='dropdown' role='button' aria-haspopup='true' "
|
|
."aria-expanded='false' aria-controls='action-menu-3-menu'>
|
|
<i class='icon fa fa-cog fa-fw ' aria-hidden='true' title='" . get_string('actiondropdown', 'pdfannotator')
|
|
. "' aria-label='" . get_string('actiondropdown', 'pdfannotator') . "'></i>"
|
|
. "</a>" .
|
|
"<ul class='dropdown-menu'>" .
|
|
"<li><button class='dropdown-item report-redisplay' type='button' id=". $reportid . " data-annotator=" . $annotatorid . ">"
|
|
. "<i class='icon fa fa-eye-slash fa-fw'></i>" . get_string('displayreportagain', 'pdfannotator').
|
|
"</button>".
|
|
"</li>" .
|
|
"</ul>" .
|
|
"</div>";
|
|
|
|
return $html;
|
|
}
|
|
// Comparison function for question objects
|
|
function compare_votes_ascending($a, $b) {
|
|
if ($a->usevotes == 0 && $b->usevotes == 0 && $a->votes == $b->votes) {
|
|
return 0;
|
|
}
|
|
return ($a->usevotes != 1 || ($a->votes < $b->votes)) ? -1 : 1;
|
|
}
|
|
|
|
function compare_votes_descending($a, $b) {
|
|
if ($a->usevotes == 0 && $b->usevotes == 0 && $a->votes == $b->votes) {
|
|
return 0;
|
|
}
|
|
return ($b->usevotes != 1 || ($a->votes > $b->votes)) ? -1 : 1;
|
|
}
|
|
|
|
// Comparison function for question objects
|
|
function compare_answers_ascending($a, $b) {
|
|
if ($a->answercount == $b->answercount) {
|
|
return 0;
|
|
}
|
|
return ($a->answercount < $b->answercount) ? -1 : 1;
|
|
}
|
|
|
|
function compare_answers_descending($a, $b) {
|
|
if ($a->answercount == $b->answercount) {
|
|
return 0;
|
|
}
|
|
return ($a->answercount > $b->answercount) ? -1 : 1;
|
|
}
|
|
|
|
function compare_time_ascending($a, $b) {
|
|
if ($a->timemodified == $b->timemodified) {
|
|
return 0;
|
|
}
|
|
return ($a->timemodified < $b->timemodified) ? -1 : 1;
|
|
}
|
|
|
|
function compare_time_descending($a, $b) {
|
|
if ($a->timemodified == $b->timemodified) {
|
|
return 0;
|
|
}
|
|
return ($a->timemodified > $b->timemodified) ? -1 : 1;
|
|
}
|
|
|
|
function compare_lastanswertime_ascending($a, $b) {
|
|
if ($a->lastanswered == $b->lastanswered) {
|
|
return 0;
|
|
}
|
|
return ($a->lastanswered < $b->lastanswered) ? -1 : 1;
|
|
}
|
|
|
|
function compare_lastanswertime_descending($a, $b) {
|
|
if ($a->lastanswered == $b->lastanswered) {
|
|
return 0;
|
|
}
|
|
return ($a->lastanswered > $b->lastanswered) ? -1 : 1;
|
|
}
|
|
|
|
function compare_commenttime_ascending($a, $b) {
|
|
if ($a->commenttime == $b->commenttime) {
|
|
return 0;
|
|
}
|
|
return ($a->commenttime < $b->commenttime) ? -1 : 1;
|
|
}
|
|
|
|
function compare_commenttime_descending($a, $b) {
|
|
if ($a->commenttime == $b->commenttime) {
|
|
return 0;
|
|
}
|
|
return ($a->commenttime > $b->commenttime) ? -1 : 1;
|
|
}
|
|
|
|
function compare_creationtime_ascending($a, $b) {
|
|
if ($a->timecreated == $b->timecreated) {
|
|
return 0;
|
|
}
|
|
return ($a->timecreated < $b->timecreated) ? -1 : 1;
|
|
}
|
|
|
|
function compare_creationtime_descending($a, $b) {
|
|
if ($a->timecreated == $b->timecreated) {
|
|
return 0;
|
|
}
|
|
return ($a->timecreated > $b->timecreated) ? -1 : 1;
|
|
}
|
|
|
|
function compare_alphabetically_ascending($a, $b) {
|
|
if ($a->pdfannotatorname == $b->pdfannotatorname) {
|
|
return 0;
|
|
}
|
|
if (strcasecmp($a->pdfannotatorname, $b->pdfannotatorname) < 0) {
|
|
return -1;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
function compare_alphabetically_descending($a, $b) {
|
|
if ($a->pdfannotatorname == $b->pdfannotatorname) {
|
|
return 0;
|
|
}
|
|
if (strcasecmp($a->pdfannotatorname, $b->pdfannotatorname) > 0) {
|
|
return -1;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
function compare_question_ascending($a, $b) {
|
|
if ($a->answeredquestion == $b->answeredquestion) {
|
|
return 0;
|
|
}
|
|
if (strcasecmp($a->answeredquestion, $b->answeredquestion) < 0) {
|
|
return -1;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
function compare_question_descending($a, $b) {
|
|
if ($a->answeredquestion == $b->answeredquestion) {
|
|
return 0;
|
|
}
|
|
if (strcasecmp($a->answeredquestion, $b->answeredquestion) > 0) {
|
|
return -1;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Function sorts entries in a table according to time, number of votes or annotator.
|
|
* Function is applicable to 'unsolved questions' and 'my posts' category on overview page.
|
|
*
|
|
* @param array $questions
|
|
* @param string $sortcriterium The column according to which the table should be sorted
|
|
* @param int $sortorder 3 for descending, 4 for ascending
|
|
*/
|
|
function pdfannotator_sort_entries($questions, $sortcriterium, $sortorder) {
|
|
switch ($sortcriterium) {
|
|
case 'col1':
|
|
if ($sortorder === 4) {
|
|
usort($questions, 'compare_time_ascending');
|
|
} else if ($sortorder === 3) {
|
|
usort($questions, 'compare_time_descending');
|
|
}
|
|
break;
|
|
case 'col2':
|
|
if ($sortorder === 3) {
|
|
usort($questions, 'compare_votes_ascending');
|
|
} else if ($sortorder === 4) {
|
|
usort($questions, 'compare_votes_descending');
|
|
}
|
|
break;
|
|
case 'col3':
|
|
if ($sortorder === 4) {
|
|
usort($questions, 'compare_alphabetically_ascending');
|
|
} else if ($sortorder === 3) {
|
|
usort($questions, 'compare_alphabetically_descending');
|
|
}
|
|
break;
|
|
default:
|
|
}
|
|
return $questions;
|
|
}
|
|
function pdfannotator_sort_questions($questions, $sortcriterium, $sortorder) {
|
|
switch ($sortcriterium) {
|
|
case 'col1':
|
|
if ($sortorder === 4) {
|
|
usort($questions, 'compare_creationtime_ascending');
|
|
} else if ($sortorder === 3) {
|
|
usort($questions, 'compare_creationtime_descending');
|
|
}
|
|
break;
|
|
case 'col2':
|
|
if ($sortorder === 3) {
|
|
usort($questions, 'compare_votes_ascending');
|
|
} else if ($sortorder === 4) {
|
|
usort($questions, 'compare_votes_descending');
|
|
}
|
|
break;
|
|
case 'col3':
|
|
if ($sortorder === 4) {
|
|
usort($questions, 'compare_answers_ascending');
|
|
} else if ($sortorder === 3) {
|
|
usort($questions, 'compare_answers_descending');
|
|
}
|
|
break;
|
|
case 'col4':
|
|
if ($sortorder === 4) {
|
|
usort($questions, 'compare_lastanswertime_ascending');
|
|
} else if ($sortorder === 3) {
|
|
usort($questions, 'compare_lastanswertime_descending');
|
|
}
|
|
break;
|
|
case 'col5':
|
|
if ($sortorder === 4) {
|
|
usort($questions, 'compare_alphabetically_ascending');
|
|
} else if ($sortorder === 3) {
|
|
usort($questions, 'compare_alphabetically_descending');
|
|
}
|
|
break;
|
|
default:
|
|
}
|
|
return $questions;
|
|
}
|
|
/**
|
|
* Function sorts entries in a table according to annotator or time.
|
|
* Applicable for overview answers category.
|
|
*
|
|
* XXX Maybe rename 'colx' into something like 'time', so as to avoid code redundancy.
|
|
*
|
|
* @param array $answers
|
|
* @param int $sortcriterium
|
|
* @param int $sortorder
|
|
* @return array $answers
|
|
*/
|
|
function pdfannotator_sort_answers($answers, $sortcriterium, $sortorder) {
|
|
switch ($sortcriterium) {
|
|
case 'col4':
|
|
if ($sortorder === 4) {
|
|
usort($answers, 'compare_alphabetically_ascending');
|
|
} else if ($sortorder === 3) {
|
|
usort($answers, 'compare_alphabetically_descending');
|
|
}
|
|
break;
|
|
case 'col2':
|
|
if ($sortorder === 4) {
|
|
usort($answers, 'compare_time_ascending');
|
|
} else if ($sortorder === 3) {
|
|
usort($answers, 'compare_time_descending');
|
|
}
|
|
break;
|
|
case 'col3':
|
|
if ($sortorder === 4) {
|
|
usort($answers, 'compare_question_ascending');
|
|
} else if ($sortorder === 3) {
|
|
usort($answers, 'compare_question_descending');
|
|
}
|
|
break;
|
|
default:
|
|
}
|
|
return $answers;
|
|
}
|
|
/**
|
|
*
|
|
* @param array $reports
|
|
* @param string $sortcriterium
|
|
* @param int $sortorder
|
|
* @return array $reports (sorted)
|
|
*/
|
|
function pdfannotator_sort_reports($reports, $sortcriterium, $sortorder) {
|
|
switch ($sortcriterium) {
|
|
case 'col1':
|
|
if ($sortorder === 4) {
|
|
usort($reports, 'compare_commenttime_ascending');
|
|
} else if ($sortorder === 3) {
|
|
usort($reports, 'compare_commenttime_descending');
|
|
}
|
|
break;
|
|
case 'col3':
|
|
if ($sortorder === 4) {
|
|
usort($reports, 'compare_creationtime_ascending');
|
|
} else if ($sortorder === 3) {
|
|
usort($reports, 'compare_creationtime_descending');
|
|
}
|
|
break;
|
|
default:
|
|
}
|
|
return $reports;
|
|
}
|
|
/**
|
|
* Function takes an array and returns its first key.
|
|
*
|
|
* @param array $array
|
|
* @return mixed
|
|
*/
|
|
function pdfannotator_get_first_key_in_array($array) {
|
|
|
|
if (!function_exists('array_key_first')) { // Function exists in PHP version 7.3 and later.
|
|
/**
|
|
* Gets the first key of an array
|
|
*
|
|
* @param array $array
|
|
* @return mixed
|
|
*/
|
|
function array_key_first(array $array) {
|
|
if (count($array)) {
|
|
reset($array);
|
|
return key($array);
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
return array_key_first($array);
|
|
}
|
|
/**
|
|
* This function renders the table of unsolved questions on the overview page.
|
|
*
|
|
* @global type $CFG
|
|
* @param array $questions
|
|
* @param int $thiscourse
|
|
* @param Moodle url object $url
|
|
* @param int $currentpage
|
|
*/
|
|
function pdfannotator_print_questions($questions, $thiscourse, $urlparams, $currentpage, $itemsperpage) {
|
|
|
|
global $CFG, $OUTPUT;
|
|
require_once("$CFG->dirroot/mod/pdfannotator/model/overviewtable.php");
|
|
|
|
$questioncount = count($questions);
|
|
$usepagination = !($itemsperpage == -1 || $itemsperpage >= $questioncount);
|
|
$offset = $currentpage * $itemsperpage;
|
|
|
|
if ($usepagination == 1 && ($offset >= $questioncount)) {
|
|
$offset = 0;
|
|
$urlparams['page'] = 0;
|
|
}
|
|
$url = new moodle_url($CFG->wwwroot . '/mod/pdfannotator/view.php', $urlparams);
|
|
|
|
// Define flexible table.
|
|
$table = new questionstable($url);
|
|
$table->setup();
|
|
|
|
// Sort the entries of the table according to time or number of votes.
|
|
if (!empty($sortinfo = $table->get_sort_columns())) {
|
|
$sortcriterium = pdfannotator_get_first_key_in_array($sortinfo); // Returns the name (e.g. col2) of the column which was clicked for sorting.
|
|
$sortorder = $sortinfo[$sortcriterium]; // 3 for descending, 4 for ascending.
|
|
$questions = pdfannotator_sort_questions($questions, $sortcriterium, $sortorder);
|
|
}
|
|
// Add data to the table and print the requested table (page).
|
|
if ($itemsperpage == -1 || $itemsperpage >= $questioncount) { // No pagination.
|
|
foreach ($questions as $question) {
|
|
pdfannotator_questionstable_add_row($thiscourse, $table, $question);
|
|
}
|
|
} else {
|
|
$table->pagesize($itemsperpage, $questioncount);
|
|
for ($i = $offset; $i < $questioncount; $i++) {
|
|
$question = $questions[$i];
|
|
if ($itemsperpage === 0) {
|
|
break;
|
|
}
|
|
pdfannotator_questionstable_add_row($thiscourse, $table, $question);
|
|
$itemsperpage--;
|
|
}
|
|
}
|
|
$table->finish_html();
|
|
}
|
|
/**
|
|
* Function prints a table view of all answers to questions the current
|
|
* user asked or subscribed to.
|
|
*
|
|
* @global type $CFG
|
|
* @global type $OUTPUT
|
|
* @param int $annotator
|
|
* @param Moodle url object $url
|
|
* @param int $thiscourse
|
|
*/
|
|
function pdfannotator_print_answers($data, $thiscourse, $url, $currentpage, $itemsperpage) {
|
|
|
|
global $CFG, $OUTPUT;
|
|
require_once("$CFG->dirroot/mod/pdfannotator/model/overviewtable.php");
|
|
|
|
$table = new answerstable($url);
|
|
$table->setup();
|
|
|
|
// Sort the entries of the table according to time or number of votes.
|
|
if (!empty($sortinfo = $table->get_sort_columns())) {
|
|
$sortcriterium = pdfannotator_get_first_key_in_array($sortinfo); // Returns the name (e.g. col2) of the column which was clicked for sorting.
|
|
$sortorder = $sortinfo[$sortcriterium]; // 3 for descending, 4 for ascending.
|
|
$data = pdfannotator_sort_answers($data, $sortcriterium, $sortorder);
|
|
}
|
|
|
|
// Add data to the table and print the requested table page.
|
|
if ($itemsperpage == -1) { // No pagination.
|
|
foreach ($data as $answer) {
|
|
pdfannotator_answerstable_add_row($thiscourse, $table, $answer);
|
|
}
|
|
} else {
|
|
$answercount = count($data);
|
|
$table->pagesize($itemsperpage, $answercount);
|
|
$offset = $currentpage * $itemsperpage;
|
|
for ($i = $offset; $i < $answercount; $i++) {
|
|
$answer = $data[$i];
|
|
if ($itemsperpage === 0) {
|
|
break;
|
|
}
|
|
pdfannotator_answerstable_add_row($thiscourse, $table, $answer);
|
|
$itemsperpage--;
|
|
}
|
|
}
|
|
$table->finish_html();
|
|
}
|
|
/**
|
|
*
|
|
* @global type $CFG
|
|
* @global type $DB
|
|
* @global type $USER
|
|
* @param type $posts
|
|
* @param type $url
|
|
* @param type $thiscourse
|
|
*/
|
|
function pdfannotator_print_this_users_posts($posts, $thiscourse, $url, $currentpage, $itemsperpage) {
|
|
|
|
global $CFG;
|
|
require_once("$CFG->dirroot/mod/pdfannotator/model/overviewtable.php");
|
|
|
|
$table = new userspoststable($url);
|
|
$table->setup();
|
|
|
|
// Sort the entries of the table according to time or number of votes.
|
|
if (!empty($sortinfo = $table->get_sort_columns())) {
|
|
$sortcriterium = pdfannotator_get_first_key_in_array($sortinfo); // Returns the name (e.g. col2) of the column which was clicked for sorting.
|
|
$sortorder = $sortinfo[$sortcriterium]; // 3 for descending, 4 for ascending.
|
|
$posts = pdfannotator_sort_entries($posts, $sortcriterium, $sortorder);
|
|
}
|
|
|
|
// Add data to the table and print the requested table page.
|
|
if ($itemsperpage == -1) {
|
|
foreach ($posts as $post) {
|
|
pdfannotator_userspoststable_add_row($table, $post);
|
|
}
|
|
} else {
|
|
$postcount = count($posts);
|
|
$table->pagesize($itemsperpage, $postcount);
|
|
$offset = $currentpage * $itemsperpage;
|
|
for ($i = $offset; $i < $postcount; $i++) {
|
|
$post = $posts[$i];
|
|
if ($itemsperpage === 0) {
|
|
break;
|
|
}
|
|
pdfannotator_userspoststable_add_row($table, $post);
|
|
$itemsperpage--;
|
|
}
|
|
}
|
|
$table->finish_html();
|
|
}
|
|
/**
|
|
* Function prints a table view of all comments that were reported as inappropriate.
|
|
*
|
|
* @global type $CFG
|
|
* @param array of objects $reports
|
|
* @param int $thiscourse
|
|
* @param Moodle url object $url
|
|
* @param int $currentpage
|
|
* @param bool $recycled
|
|
*/
|
|
function pdfannotator_print_reports($reports, $thiscourse, $url, $currentpage, $itemsperpage, $recycled = false) {
|
|
|
|
global $CFG, $OUTPUT;
|
|
require_once("$CFG->dirroot/mod/pdfannotator/model/overviewtable.php");
|
|
|
|
$table = new reportstable($url);
|
|
$table->setup();
|
|
// Sort the entries of the table according to time or number of votes.
|
|
if (!empty($sortinfo = $table->get_sort_columns())) {
|
|
$sortcriterium = pdfannotator_get_first_key_in_array($sortinfo); // Returns the name (e.g. col2) of the column which was clicked for sorting.
|
|
$sortorder = $sortinfo[$sortcriterium]; // 3 for descending, 4 for ascending.
|
|
$reports = pdfannotator_sort_reports($reports, $sortcriterium, $sortorder);
|
|
}
|
|
// Add data to the table and print the requested table page.
|
|
if ($itemsperpage == -1) {
|
|
foreach ($reports as $report) {
|
|
pdfannotator_reportstable_add_row($thiscourse, $table, $recycled, $report);
|
|
}
|
|
|
|
} else {
|
|
$reportcount = count($reports);
|
|
$table->pagesize($itemsperpage, $reportcount);
|
|
$offset = $currentpage * $itemsperpage;
|
|
for ($i = $offset; $i < $reportcount; $i++) {
|
|
$report = $reports[$i];
|
|
if ($itemsperpage === 0) {
|
|
break;
|
|
}
|
|
pdfannotator_reportstable_add_row($thiscourse, $table, $recycled, $report);
|
|
$itemsperpage--;
|
|
}
|
|
}
|
|
$table->finish_html();
|
|
}
|
|
/**
|
|
* This function adds a row of data to the overview table that displays all
|
|
* unsolved questions in the course.
|
|
*
|
|
* @param int $thiscourse
|
|
* @param questionstable $table
|
|
* @param object $question
|
|
*/
|
|
function pdfannotator_questionstable_add_row($thiscourse, $table, $question) {
|
|
global $CFG;
|
|
$author = "<a href=" . $CFG->wwwroot . "/user/view.php?id=$question->userid&course=$thiscourse>" . pdfannotator_get_username($question->userid) . "</a>";
|
|
$time = pdfannotator_get_user_datetime_shortformat($question->timecreated);
|
|
if ($question->lastanswered != $question->timecreated) {
|
|
$lastresponder = "<a href=" . $CFG->wwwroot . "/user/view.php?id=$question->lastuser&course=$thiscourse>" . pdfannotator_get_username($question->lastuser) . "</a>";
|
|
$answertime = pdfannotator_timeago($question->lastanswered);
|
|
$lastanswered = $lastresponder . "<br>" . $answertime;
|
|
} else {
|
|
$lastanswered = '-';
|
|
}
|
|
$content = "<a href=$question->link class='more'>$question->content</a>";
|
|
$pdfannotatorname = $question->pdfannotatorname;
|
|
$table->add_data(array($content, $author . '<br>' . $time, $question->votes, $question->answercount, $lastanswered, $pdfannotatorname));
|
|
}
|
|
/**
|
|
* This function adds a row of data to the overview table that displays
|
|
* answers to any question the user subscribed to.
|
|
*
|
|
* @global type $CFG
|
|
* @param int $thiscourse
|
|
* @param answerstable $table
|
|
* @param object $answer
|
|
*/
|
|
function pdfannotator_answerstable_add_row($thiscourse, $table, $answer) {
|
|
global $CFG;
|
|
$question = "<a class='".$answer->annoid." more' href=$answer->questionlink>$answer->answeredquestion</a>";
|
|
$pdfannotatorname = $answer->pdfannotatorname;
|
|
if ($answer->correct == 2) {
|
|
$checked = "<i class='icon fa fa-check fa-fw' style='color:green;'></i>";
|
|
} else {
|
|
$checked = "";
|
|
}
|
|
$answerid = 'answer_' . $answer->answerid;
|
|
$answerlink = "<a id=$answerid data-question=$answer->questionid href=$answer->link class='more'>$answer->answer</a>";
|
|
$answeredby = "<a href=" . $CFG->wwwroot . "/user/view.php?id=$answer->answeredby&course=$thiscourse>" . pdfannotator_get_username($answer->answeredby) . "</a>";
|
|
$answertime = pdfannotator_get_user_datetime($answer->timemodified);
|
|
|
|
$dropdown = pdfannotator_anwers_add_actiondropdown($answer->answerid, $answer->annotatorid, $answer->annoid);
|
|
|
|
$table->add_data(array($answerlink, $checked, $answeredby . '<br>' . $answertime, $question, $pdfannotatorname, $dropdown));
|
|
}
|
|
/**
|
|
* This function adds a row of data to the overview table that displays all
|
|
* comments the current user posted in this course.
|
|
*
|
|
* @param userspoststable $table
|
|
* @param object $post
|
|
*/
|
|
function pdfannotator_userspoststable_add_row($table, $post) {
|
|
$time = pdfannotator_get_user_datetime($post->timemodified);
|
|
$content = "<a href=$post->link class='more'>$post->content</a>";
|
|
$pdfannotatorname = $post->pdfannotatorname;
|
|
$table->add_data(array($content, $time, $post->votes, $pdfannotatorname));
|
|
}
|
|
/**
|
|
* This function adds a row of data to the overview table that displays all
|
|
* comments reported in this course.
|
|
*
|
|
* @param int $thiscourse
|
|
* @param reportstable $table
|
|
* @param boolean $recycled
|
|
* @param object $report
|
|
*/
|
|
function pdfannotator_reportstable_add_row($thiscourse, $table, $recycled, $report) {
|
|
global $CFG;
|
|
$reportid = 'report_' . $report->reportid;
|
|
$reportedcommmentlink = "<a id=$reportid href=$report->link class='more'>$report->reportedcomment</a>";
|
|
$writtenby = "<a href=" . $CFG->wwwroot . "/user/view.php?id=$report->commentauthor&course=$thiscourse>" . pdfannotator_get_username($report->commentauthor) . "</a>";
|
|
$commenttime = pdfannotator_get_user_datetime($report->commenttime);
|
|
$reportedby = "<a href=" . $CFG->wwwroot . "/user/view.php?id=$report->reportinguser&course=$thiscourse>" . pdfannotator_get_username($report->reportinguser) . "</a>";
|
|
$reporttime = pdfannotator_get_user_datetime($report->timecreated);
|
|
if (!$recycled) {
|
|
$dropdown = pdfannotator_reports_add_actiondropdown($report->reportid, $report->commentid, $report->annotatorid, $report->annotationid);
|
|
} else {
|
|
$dropdown = pdfannotator_recycled_reports_add_actionsdropdown($report->reportid, $report->annotatorid);
|
|
}
|
|
$report->report = "<div class='more'>$report->report</div>";
|
|
$table->add_data(array($reportedcommmentlink, $writtenby . '<br>' . $commenttime, $report->report, $reportedby . '<br>' . $reporttime, $dropdown));
|
|
}
|
|
/**
|
|
* This function receives data from our feedback form and sends it to the
|
|
* developers via email.
|
|
*
|
|
* @global type $USER
|
|
* @param type $formdata
|
|
*/
|
|
function pdfannotator_send_feedbackmail($formdata) {
|
|
|
|
global $USER;
|
|
|
|
$subject = 'Feedback zum PDF-Annotation-Tool aus Aachen';
|
|
$messagetext = $formdata->feedback;
|
|
$from = $USER->firstname . ' ' . $USER->lastname . '<' . $USER->email . '>';
|
|
$headers = "From: $from\r\n";
|
|
$headers .= "Content-type: text/html\r\n";
|
|
$to = 'obeid@cil.rwth-aachen.de, schwager@cil.rwth-aachen.de, heynkes@cil.rwth-aachen.de';
|
|
$html = <<<EOF
|
|
<html>
|
|
<head>
|
|
<meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
|
|
</head>
|
|
<body dir='auto'>
|
|
<div dir='ltr'>$messagetext</div>
|
|
</body>
|
|
</html>
|
|
EOF;
|
|
|
|
mail($to, $subject, $html, $headers);
|
|
}
|
|
/**
|
|
* Function takes a moodle timestamp, calculates how much time has since elapsed
|
|
* and returns this information as a string (e.g.: '3 days ago').
|
|
*
|
|
* @param int $timestamp
|
|
* @return string
|
|
*/
|
|
function pdfannotator_timeago($timestamp) {
|
|
$strtime = array(get_string('second', 'pdfannotator'), get_string('minute', 'pdfannotator'), get_string('hour', 'pdfannotator'));
|
|
$strtime[] = get_string('day', 'pdfannotator');
|
|
$strtime[] = get_string('month', 'pdfannotator');
|
|
$strtime[] = get_string('year', 'pdfannotator');
|
|
$strtimeplural = array(get_string('seconds', 'pdfannotator'), get_string('minutes', 'pdfannotator'));
|
|
$strtimeplural[] = get_string('hours', 'pdfannotator');
|
|
$strtimeplural[] = get_string('days', 'pdfannotator');
|
|
$strtimeplural[] = get_string('months', 'pdfannotator');
|
|
$strtimeplural[] = get_string('years', 'pdfannotator');
|
|
$length = array("60", "60", "24", "30", "12", "10");
|
|
$currenttime = time();
|
|
if ($currenttime >= $timestamp) {
|
|
$diff = time() - $timestamp;
|
|
for ($i = 0; $diff >= $length[$i] && $i < count($length) - 1; $i++) {
|
|
$diff = $diff / $length[$i];
|
|
}
|
|
if ($diff > 1) {
|
|
$diff = round($diff) . ' ' . $strtimeplural[$i];
|
|
} else {
|
|
$diff = round($diff) . ' ' . $strtime[$i];
|
|
}
|
|
return get_string('ago', 'pdfannotator', $diff);
|
|
}
|
|
}
|
|
|
|
function pdfannotator_is_mobile_device() {
|
|
$param = filter_input(INPUT_SERVER, 'HTTP_USER_AGENT', FILTER_DEFAULT); // XXX How to filter, here?
|
|
return preg_match("/(android|avantgo|blackberry|bolt|boost|cricket|docomo|fone|hiptop|mini|mobi|palm|phone|pie|tablet|up\.browser|up\.link|webos|wos)/i", $param);
|
|
}
|