diff --git a/backup/moodle2/backup_mediagallery_stepslib.php b/backup/moodle2/backup_mediagallery_stepslib.php index 0e524e1..87bef1a 100644 --- a/backup/moodle2/backup_mediagallery_stepslib.php +++ b/backup/moodle2/backup_mediagallery_stepslib.php @@ -32,6 +32,7 @@ protected function define_structure() { // To know if we are including userinfo. $userinfo = $this->get_setting_value('userinfo'); + $includecomments = $this->get_setting_value('comments'); // Define each element separated. $mediagallery = new backup_nested_element('mediagallery', array('id'), array( @@ -40,7 +41,9 @@ protected function define_structure() { 'captionposition', 'galleryfocus', 'carousel', 'grid', 'gridrows', 'gridcolumns', 'enforcedefaults', 'readonlyfrom', 'readonlyto', 'maxbytes', 'maxitems', 'maxgalleries', 'allowcomments', 'allowlikes', - 'colltype', 'objectid', 'source', 'mode', 'creator', 'userid', + 'colltype', + 'completiongalleries', 'completionitems', 'completioncomments', + 'objectid', 'source', 'mode', 'creator', 'userid', )); $userfeedbacks = new backup_nested_element('userfeedback'); @@ -63,6 +66,16 @@ protected function define_structure() { 'timecreated', 'broadcaster', 'objectid', 'source', 'processing_status', 'creator', )); + $gcomments = new backup_nested_element('gallerycomments'); + $gcomment = new backup_nested_element('gallerycomment', ['id'], [ + 'contextid', 'component', 'commentarea', 'itemid', 'content', 'format', 'userid', 'timecreated', + ]); + + $icomments = new backup_nested_element('itemcomments'); + $icomment = new backup_nested_element('itemcomment', ['id'], [ + 'contextid', 'component', 'commentarea', 'itemid', 'content', 'format', 'userid', 'timecreated', + ]); + $ctags = new backup_nested_element('collectiontags'); $ctag = new backup_nested_element('collectiontag', array('id'), array('itemid', 'rawname')); @@ -80,12 +93,16 @@ protected function define_structure() { $gallerys->add_child($gallery); $gallery->add_child($items); + $gallery->add_child($gcomments); + $gcomments->add_child($gcomment); $gallery->add_child($gtags); $gtags->add_child($gtag); $items->add_child($item); $userfeedbacks->add_child($userfeedback); $item->add_child($userfeedbacks); + $item->add_child($icomments); + $icomments->add_child($icomment); $item->add_child($itags); $itags->add_child($itag); @@ -98,6 +115,17 @@ protected function define_structure() { $item->set_source_table('mediagallery_item', array('galleryid' => backup::VAR_PARENTID)); $userfeedback->set_source_table('mediagallery_userfeedback', array('itemid' => backup::VAR_PARENTID)); + if ($includecomments) { + $gcomment->set_source_table('comments', [ + 'contextid' => backup::VAR_CONTEXTID, + 'commentarea' => backup_helper::is_sqlparam('gallery'), + 'itemid' => backup::VAR_PARENTID]); + $icomment->set_source_table('comments', [ + 'contextid' => backup::VAR_CONTEXTID, + 'commentarea' => backup_helper::is_sqlparam('item'), + 'itemid' => backup::VAR_PARENTID]); + } + if (core_tag_tag::is_enabled('mod_mediagallery', 'mediagallery')) { $ctag->set_source_sql('SELECT t.id, ti.itemid, t.rawname FROM {tag} t @@ -144,6 +172,9 @@ protected function define_structure() { $gallery->annotate_ids('user', 'userid'); $item->annotate_ids('user', 'userid'); + $gcomment->annotate_ids('user', 'userid'); + $icomment->annotate_ids('user', 'userid'); + // Return the root element (mediagallery), wrapped into standard activity structure. return $this->prepare_activity_structure($mediagallery); } diff --git a/backup/moodle2/restore_mediagallery_stepslib.php b/backup/moodle2/restore_mediagallery_stepslib.php index 446e16a..eea213a 100644 --- a/backup/moodle2/restore_mediagallery_stepslib.php +++ b/backup/moodle2/restore_mediagallery_stepslib.php @@ -36,6 +36,7 @@ protected function define_structure() { $paths = array(); $userinfo = $this->get_setting_value('userinfo'); + $includecomments = $this->get_setting_value('comments'); $mediagallery = new restore_path_element('mediagallery', '/activity/mediagallery'); $paths[] = $mediagallery; @@ -51,6 +52,13 @@ protected function define_structure() { '/activity/mediagallery/gallerys/gallery/items/item/userfeedback/feedback'); $paths[] = $userfeedback; + if ($includecomments) { + $paths[] = new restore_path_element('mediagallery_gcomment', + '/activity/mediagallery/gallerys/gallery/gallerycomments/gallerycomment'); + $paths[] = new restore_path_element('mediagallery_icomment', + '/activity/mediagallery/gallerys/gallery/items/item/itemcomments/itemcomment'); + } + $paths[] = new restore_path_element('mediagallery_ctag', '/activity/mediagallery/collectiontags/collectiontag'); $paths[] = new restore_path_element('mediagallery_gtag', '/activity/mediagallery/gallerys/gallery/gallerytags/gallerytag'); @@ -127,6 +135,46 @@ protected function process_mediagallery_item($data) { $this->set_mapping('mediagallery_item', $oldid, $newitemid, true); } + /** + * Process a gallery comment. + * + * @param object|array $data + * @return void + */ + protected function process_mediagallery_gcomment($data): void { + global $DB; + + $data = (object)$data; + $oldid = $data->id; + + $data->itemid = $this->get_new_parentid('mediagallery_gallery'); + $data->timecreated = $this->apply_date_offset($data->timecreated); + $data->userid = $this->get_mappingid('user', $data->userid); + $data->contextid = $this->get_mappingid('context', $data->contextid); + $newcommentid = $DB->insert_record('comments', $data); + $this->set_mapping('comments', $oldid, $newcommentid, true); + } + + /** + * Process an item comment. + * + * @param object|array $data + * @return void + */ + protected function process_mediagallery_icomment($data): void { + global $DB; + + $data = (object)$data; + $oldid = $data->id; + + $data->itemid = $this->get_new_parentid('mediagallery_item'); + $data->timecreated = $this->apply_date_offset($data->timecreated); + $data->userid = $this->get_mappingid('user', $data->userid); + $data->contextid = $this->get_mappingid('context', $data->contextid); + $newcommentid = $DB->insert_record('comments', $data); + $this->set_mapping('comments', $oldid, $newcommentid, true); + } + protected function process_mediagallery_ctag($data) { $data = (object)$data; if (!core_tag_tag::is_enabled('mod_mediagallery', 'mediagallery')) { diff --git a/classes/completion/custom_completion.php b/classes/completion/custom_completion.php new file mode 100644 index 0000000..7e26d03 --- /dev/null +++ b/classes/completion/custom_completion.php @@ -0,0 +1,128 @@ +. + +declare(strict_types=1); + +namespace mod_mediagallery\completion; + +use core_completion\activity_custom_completion; + +/** + * Activity custom completion subclass for the Media collection activity. + * + * Class for defining mod_mediagallery's custom completion rules and fetching the completion statuses + * of the custom completion rules for a given Media collection instance and a user. + * + * @package mod_mediagallery + * @copyright 2023 Otago Polytechnic + * @author James Calder + * @copyright based on work by Simey Lameze + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class custom_completion extends activity_custom_completion { + + /** + * Fetches the completion state for a given completion rule. + * + * @param string $rule The completion rule. + * @return int The completion state. + */ + public function get_state(string $rule): int { + global $DB; + + $this->validate_rule($rule); + + $userid = $this->userid; + $collectionid = $this->cm->instance; + + if (!$collection = $DB->get_record('mediagallery', ['id' => $collectionid])) { + throw new \moodle_exception('Unable to find Media collection with id ' . $collectionid); + } + + $itemcountparams = ['collectionid' => $collectionid, 'userid' => $userid]; + $itemcountsql = + "SELECT COUNT(*) + FROM {mediagallery_item} mgi + JOIN {mediagallery_gallery} mgg ON mgg.id = mgi.galleryid + WHERE mgg.instanceid = :collectionid AND mgi.userid = :userid"; + + $commentcountparams = ['collectionid' => $collectionid, 'userid' => $userid]; + $commentcountsql = + "SELECT COUNT(*) + FROM {comments} c + LEFT JOIN {mediagallery_item} mgi + ON c.component = 'mod_mediagallery' AND c.commentarea = 'item' AND mgi.id = c.itemid + JOIN {mediagallery_gallery} mgg ON c.component = 'mod_mediagallery' + AND (c.commentarea = 'gallery' AND mgg.id = c.itemid + OR c.commentarea = 'item' AND mgg.id = mgi.galleryid) + WHERE c.component = 'mod_mediagallery' AND mgg.instanceid = :collectionid AND c.userid = :userid"; + + if ($rule == 'completiongalleries') { + $status = $collection->completiongalleries <= + $DB->count_records('mediagallery_gallery', ['instanceid' => $collectionid, 'userid' => $userid]); + } else if ($rule == 'completionitems') { + $status = $collection->completionitems <= $DB->get_field_sql($itemcountsql, $itemcountparams); + } else if ($rule == 'completioncomments') { + $status = $collection->completioncomments <= $DB->get_field_sql($commentcountsql, $commentcountparams); + } + + return $status ? COMPLETION_COMPLETE : COMPLETION_INCOMPLETE; + } + + /** + * Fetch the list of custom completion rules that this module defines. + * + * @return string[] + */ + public static function get_defined_custom_rules(): array { + return [ + 'completiongalleries', + 'completionitems', + 'completioncomments', + ]; + } + + /** + * Returns an associative array of the descriptions of custom completion rules. + * + * @return string[] + */ + public function get_custom_rule_descriptions(): array { + $completiongalleries = $this->cm->customdata['customcompletionrules']['completiongalleries'] ?? 0; + $completionitems = $this->cm->customdata['customcompletionrules']['completionitems'] ?? 0; + $completioncomments = $this->cm->customdata['customcompletionrules']['completioncomments'] ?? 0; + + return [ + 'completiongalleries' => get_string('completiondetail:galleries', 'mediagallery', $completiongalleries), + 'completionitems' => get_string('completiondetail:items', 'mediagallery', $completionitems), + 'completioncomments' => get_string('completiondetail:comments', 'mediagallery', $completioncomments), + ]; + } + + /** + * Returns an array of all completion rules, in the order they should be displayed to users. + * + * @return string[] + */ + public function get_sort_order(): array { + return [ + 'completionview', + 'completiongalleries', + 'completionitems', + 'completioncomments', + ]; + } +} diff --git a/classes/event/comment_created.php b/classes/event/comment_created.php new file mode 100644 index 0000000..d07b390 --- /dev/null +++ b/classes/event/comment_created.php @@ -0,0 +1,50 @@ +. + +/** + * The mod_mediagallery comment created event. + * + * @package mod_mediagallery + * @copyright 2023 Otago Polytechnic + * @author James Calder + * @copyright based on work by 2013 Rajesh Taneja + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_mediagallery\event; + +/** + * The mod_mediagallery comment created event class. + * + * @package mod_mediagallery + * @copyright 2023 Otago Polytechnic + * @author James Calder + * @copyright based on work by 2013 Rajesh Taneja + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class comment_created extends \core\event\comment_created { + + /** + * Returns description of what happened. + * + * @return string + */ + public function get_description(): string { + return "The user with id '$this->userid' added the comment with id '$this->objectid' " . + "to the Media collection activity with course module id '$this->contextinstanceid'."; + } + +} diff --git a/classes/event/comment_deleted.php b/classes/event/comment_deleted.php new file mode 100644 index 0000000..5aec1b5 --- /dev/null +++ b/classes/event/comment_deleted.php @@ -0,0 +1,50 @@ +. + +/** + * The mod_mediagallery comment deleted event. + * + * @package mod_mediagallery + * @copyright 2023 Otago Polytechnic + * @author James Calder + * @copyright based on work by 2013 Rajesh Taneja + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_mediagallery\event; + +/** + * The mod_mediagallery comment deleted event class. + * + * @package mod_mediagallery + * @copyright 2023 Otago Polytechnic + * @author James Calder + * @copyright based on work by 2013 Rajesh Taneja + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class comment_deleted extends \core\event\comment_deleted { + + /** + * Returns description of what happened. + * + * @return string + */ + public function get_description(): string { + return "The user with id '$this->userid' deleted the comment with id '$this->objectid' " . + "from the Media collection activity with course module id '$this->contextinstanceid'."; + } + +} diff --git a/classes/gallery.php b/classes/gallery.php index 7225efa..fe0c82c 100644 --- a/classes/gallery.php +++ b/classes/gallery.php @@ -81,23 +81,39 @@ public function copy($targetid) { $newgalleryrecord->userid = $USER->id; $newgalleryrecord->groupid = 0; - $newgallery = self::create($newgalleryrecord); + $newgallery = self::create($newgalleryrecord, ['nocompletionupdate' => true]); + $completionupdateuserids = []; + if ($newgallery->record->userid) { + $completionupdateuserids[$newgallery->record->userid] = true; + } $thumbnail = 0; foreach ($this->get_items() as $item) { - $newitem = $item->copy($newgallery->id); + $newitem = $item->copy($newgallery->id, ['nocompletionupdate' => true]); if ($item->id == $newgallery->thumbnail) { $thumbnail = $newitem->id; } + if ($newitem->record->userid) { + $completionupdateuserids[$newitem->record->userid] = true; + } } // Update thumbnail. $DB->set_field('mediagallery_gallery', 'thumbnail', $thumbnail, array('id' => $newgallery->id)); + // Update completion state. + $cm = get_coursemodule_from_instance('mediagallery', $newgallery->record->instanceid); + $completion = new \completion_info(get_course($cm->course)); + if ($completion->is_enabled($cm) == COMPLETION_TRACKING_AUTOMATIC) { + foreach ($completionupdateuserids as $userid => $needschange) { + $completion->update_state($cm, COMPLETION_COMPLETE, $userid); + } + } + return true; } - public static function create(\stdClass $data) { + public static function create(\stdClass $data, array $options = []) { if ($data->mode == 'youtube') { $data->galleryfocus = self::TYPE_VIDEO; } @@ -106,6 +122,15 @@ public static function create(\stdClass $data) { } $result = parent::create($data); + // Update completion state. + if (empty($options['nocompletionupdate']) && $result->record->userid) { + $cm = get_coursemodule_from_instance('mediagallery', $result->record->instanceid); + $completion = new \completion_info(get_course($cm->course)); + if ($completion->is_enabled($cm) == COMPLETION_TRACKING_AUTOMATIC) { + $completion->update_state($cm, COMPLETION_COMPLETE, $result->record->userid); + } + } + $params = array( 'context' => $result->get_collection()->context, 'objectid' => $result->id, @@ -138,12 +163,43 @@ public function delete($options = array()) { $event->add_record_snapshot('mediagallery_gallery', $this->record); $event->trigger(); + // Get users for completion update. + $completionupdateparams = ['galleryid1' => $this->record->id, 'galleryid2' => $this->record->id, + 'galleryid3' => $this->record->id, 'galleryid4' => $this->record->id]; + $completionupdatesql = + " (SELECT DISTINCT mgg.userid + FROM {mediagallery_gallery} mgg + WHERE mgg.id = :galleryid1 AND mgg.userid <> 0) + UNION + (SELECT DISTINCT mgi.userid + FROM {mediagallery_item} mgi + WHERE mgi.galleryid = :galleryid2 AND mgi.userid <> 0) + UNION + (SELECT DISTINCT c.userid + FROM {comments} c + LEFT JOIN {mediagallery_item} mgi + ON c.component = 'mod_mediagallery' AND c.commentarea = 'item' AND mgi.id = c.itemid + WHERE c.component = 'mod_mediagallery' + AND (c.commentarea = 'gallery' AND c.itemid = :galleryid3 + OR c.commentarea = 'item' AND mgi.galleryid = :galleryid4) + AND c.userid <> 0)"; + $completionupdateuserids = $DB->get_fieldset_sql($completionupdatesql, $completionupdateparams); + // Delete all items and then the gallery. item::delete_all_by_gallery($this->record->id); \comment::delete_comments(array('commentarea' => 'gallery', 'itemid' => $this->record->id)); parent::delete(); + // Update completion state. + $cm = get_coursemodule_from_instance('mediagallery', $this->record->instanceid); + $completion = new \completion_info(get_course($cm->course)); + if ($completion->is_enabled($cm) == COMPLETION_TRACKING_AUTOMATIC) { + foreach ($completionupdateuserids as $userid) { + $completion->update_state($cm, COMPLETION_INCOMPLETE, $userid); + } + } + return true; } diff --git a/classes/item.php b/classes/item.php index 8a5c114..84331e3 100644 --- a/classes/item.php +++ b/classes/item.php @@ -67,12 +67,12 @@ public function __construct($recordorid, $options = array()) { } } - public function copy($targetid) { + public function copy($targetid, array $options = []) { $newitemrecord = clone $this->record; unset($newitemrecord->id); $newitemrecord->galleryid = $targetid; - $newitem = self::create($newitemrecord); + $newitem = self::create($newitemrecord, ['nocompletionupdate' => true]); $fs = get_file_storage(); if ($file = $this->get_file()) { // Item. @@ -96,10 +96,21 @@ public function copy($targetid) { ); $fs->create_file_from_storedfile($fileinfo, $file); } + + // Update completion state. + if (empty($options['nocompletionupdate']) && $newitem->record->userid) { + $gallery = $newitem->gallery ?? new gallery($newitem->record->galleryid); + $cm = get_coursemodule_from_instance('mediagallery', $gallery->record->instanceid); + $completion = new \completion_info(get_course($cm->course)); + if ($completion->is_enabled($cm) == COMPLETION_TRACKING_AUTOMATIC) { + $completion->update_state($cm, COMPLETION_COMPLETE, $newitem->record->userid); + } + } + return $newitem; } - public static function create(\stdClass $data) { + public static function create(\stdClass $data, array $options = []) { global $DB, $USER; foreach (static::$defaultvalues as $key => $val) { if (!isset($data->$key)) { @@ -118,6 +129,16 @@ public static function create(\stdClass $data) { $DB->execute($sql, array('item' => $result->id, 'galleryid' => $result->galleryid)); } + // Update completion state. + if (empty($options['nocompletionupdate']) && $result->record->userid) { + $gallery = $result->gallery ?? new gallery($result->record->galleryid); + $cm = get_coursemodule_from_instance('mediagallery', $gallery->record->instanceid); + $completion = new \completion_info(get_course($cm->course)); + if ($completion->is_enabled($cm) == COMPLETION_TRACKING_AUTOMATIC) { + $completion->update_state($cm, COMPLETION_COMPLETE, $result->record->userid); + } + } + $params = array( 'context' => $result->get_context(), 'objectid' => $result->id, @@ -133,7 +154,7 @@ public static function create(\stdClass $data) { } public static function create_from_archive(gallery $gallery, \stored_file $storedfile, $formdata = array()) { - global $DB; + global $DB, $USER; $context = $gallery->get_collection()->context; $maxitems = $gallery->get_collection()->maxitems; @@ -182,7 +203,7 @@ public static function create_from_archive(gallery $gallery, \stored_file $store $data->thumbnail = 1; $count = 0; } - $item = self::create($data); + $item = self::create($data, ['nocompletionupdate' => true]); // Copy the file into the correct area. $fileinfo = array( @@ -201,6 +222,16 @@ public static function create_from_archive(gallery $gallery, \stored_file $store $count++; } $fs->delete_area_files($context->id, 'mod_mediagallery', 'unpacktemp', 0); + + // Update completion state. + if ($USER->id) { + $cm = get_coursemodule_from_instance('mediagallery', $gallery->record->instanceid); + $completion = new \completion_info(get_course($cm->course)); + if ($completion->is_enabled($cm) == COMPLETION_TRACKING_AUTOMATIC) { + $completion->update_state($cm, COMPLETION_COMPLETE, $USER->id); + } + } + } /** @@ -223,6 +254,19 @@ public function delete($options = array()) { $event->add_record_snapshot('mediagallery_item', $this->record); $event->trigger(); + // Get users for completion update. + $completionupdateparams = ['itemid1' => $this->record->id, 'itemid2' => $this->record->id]; + $completionupdatesql = + " (SELECT DISTINCT mgi.userid + FROM {mediagallery_item} mgi + WHERE mgi.id = :itemid1 AND mgi.userid <> 0) + UNION + (SELECT DISTINCT c.userid + FROM {comments} c + WHERE c.component = 'mod_mediagallery' AND c.commentarea = 'item' AND c.itemid = :itemid2 + AND c.userid <> 0)"; + $completionupdateuserids = $DB->get_fieldset_sql($completionupdatesql, $completionupdateparams); + $fs = get_file_storage(); $fs->delete_area_files($this->get_context()->id, 'mod_mediagallery', 'item', $this->record->id); $fs->delete_area_files($this->get_context()->id, 'mod_mediagallery', 'lowres', $this->record->id); @@ -231,7 +275,21 @@ public function delete($options = array()) { $DB->delete_records('mediagallery_userfeedback', array('itemid' => $this->record->id)); \comment::delete_comments(array('commentarea' => 'item', 'itemid' => $this->record->id)); - return parent::delete(); + $result = parent::delete(); + + // Update completion state. + if (empty($options['nocompletionupdate'])) { + $gallery = $this->gallery ?? new gallery($this->record->galleryid); + $cm = get_coursemodule_from_instance('mediagallery', $gallery->record->instanceid); + $completion = new \completion_info(get_course($cm->course)); + if ($completion->is_enabled($cm) == COMPLETION_TRACKING_AUTOMATIC) { + foreach ($completionupdateuserids as $userid) { + $completion->update_state($cm, COMPLETION_INCOMPLETE, $userid); + } + } + } + + return $result; } public static function delete_all_by_gallery($galleryid) { @@ -259,6 +317,10 @@ public static function delete_all_by_gallery($galleryid) { )"; $DB->execute($sql, array('galleryid' => $galleryid)); $DB->delete_records('mediagallery_item', array('galleryid' => $galleryid)); + + // We don't need to update completion state here, + // because this function is only called from gallery::delete, which takes care of it. + return true; } diff --git a/classes/observers.php b/classes/observers.php new file mode 100644 index 0000000..c306559 --- /dev/null +++ b/classes/observers.php @@ -0,0 +1,74 @@ +. + +/** + * Event observers. + * + * @package mod_mediagallery + * @copyright 2023 Otago Polytechnic + * @author James Calder + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_mediagallery; + +/** + * Event observers. + * + * @package mod_mediagallery + * @copyright 2023 Otago Polytechnic + * @author James Calder + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class observers { + + // TODO: Ignore if course reset in progress? + + /** + * A comment was created. + * + * @param \core\event\base $event The event. + * @return void + */ + public static function comment_created($event): void { + // Update completion state. + if ($event->userid) { + $cm = get_coursemodule_from_id('mediagallery', $event->contextinstanceid); + $completion = new \completion_info(get_course($cm->course)); + if ($completion->is_enabled($cm) == COMPLETION_TRACKING_AUTOMATIC) { + $completion->update_state($cm, COMPLETION_COMPLETE, $event->userid); + } + } + } + + /** + * A comment was deleted. + * + * @param \core\event\base $event The event. + * @return void + */ + public static function comment_deleted($event): void { + // Update completion state. + if ($event->userid) { + $cm = get_coursemodule_from_id('mediagallery', $event->contextinstanceid); + $completion = new \completion_info(get_course($cm->course)); + if ($completion->is_enabled($cm) == COMPLETION_TRACKING_AUTOMATIC) { + $completion->update_state($cm, COMPLETION_INCOMPLETE, $event->userid); + } + } + } + +} diff --git a/classes/privacy/provider.php b/classes/privacy/provider.php index 15e03a2..a96508f 100644 --- a/classes/privacy/provider.php +++ b/classes/privacy/provider.php @@ -332,7 +332,8 @@ protected static function export_item_data(int $userid, \context $context, $gall * @param \context $context the context to delete in. */ public static function delete_data_for_all_users_in_context(\context $context) { - global $DB; + global $CFG, $DB; + require_once($CFG->libdir . '/completionlib.php'); if (!$context instanceof \context_module) { return; @@ -340,6 +341,29 @@ public static function delete_data_for_all_users_in_context(\context $context) { $instanceid = $DB->get_field('course_modules', 'instance', ['id' => $context->instanceid], MUST_EXIST); + // Get users for completion update. + $completionupdateparams = ['instanceid1' => $instanceid, 'instanceid2' => $instanceid, 'instanceid3' => $instanceid]; + $completionupdatesql = + " (SELECT DISTINCT mgg.userid + FROM {mediagallery_gallery} mgg + WHERE mgg.instanceid = :instanceid1 AND mgg.userid <> 0) + UNION + (SELECT DISTINCT mgi.userid + FROM {mediagallery_item} mgi + JOIN {mediagallery_gallery} mgg ON mgg.id = mgi.galleryid + WHERE mgg.instanceid = :instanceid2 AND mgi.userid <> 0) + UNION + (SELECT DISTINCT c.userid + FROM {comments} c + LEFT JOIN {mediagallery_item} mgi + ON c.component = 'mod_mediagallery' AND c.commentarea = 'item' AND mgi.id = c.itemid + JOIN {mediagallery_gallery} mgg + ON c.component = 'mod_mediagallery' + AND (c.commentarea = 'gallery' AND mgg.id = c.itemid + OR c.commentarea = 'item' AND mgg.id = mgi.galleryid) + WHERE c.component = 'mod_mediagallery' AND mgg.instanceid = :instanceid3 AND c.userid <> 0)"; + $completionupdateuserids = $DB->get_fieldset_sql($completionupdatesql, $completionupdateparams); + $DB->delete_records_select('mediagallery_userfeedback', "itemid IN ( SELECT i.id @@ -367,6 +391,15 @@ public static function delete_data_for_all_users_in_context(\context $context) { \core_tag\privacy\provider::delete_item_tags($context, 'mod_mediagallery', 'mediagallery_gallery'); \core_tag\privacy\provider::delete_item_tags($context, 'mod_mediagallery', 'mediagallery_item'); + + // Update completion state. + $cm = get_coursemodule_from_instance('mediagallery', $instanceid); + $completion = new \completion_info(get_course($cm->course)); + if ($completion->is_enabled($cm) == COMPLETION_TRACKING_AUTOMATIC) { + foreach ($completionupdateuserids as $userid) { + $completion->update_state($cm, COMPLETION_INCOMPLETE, $userid); + } + } } /** @@ -375,7 +408,8 @@ public static function delete_data_for_all_users_in_context(\context $context) { * @param approved_contextlist $contextlist a list of contexts approved for deletion. */ public static function delete_data_for_user(approved_contextlist $contextlist) { - global $DB; + global $CFG, $DB; + require_once($CFG->libdir . '/completionlib.php'); if (empty($contextlist->count())) { return; @@ -384,6 +418,26 @@ public static function delete_data_for_user(approved_contextlist $contextlist) { $userid = $contextlist->get_user()->id; $fs = get_file_storage(); + $completionupdatesql = + " (SELECT DISTINCT mgg.userid + FROM {mediagallery_gallery} mgg + WHERE mgg.instanceid = :instanceid1 AND mgg.userid = :guserid) + UNION + (SELECT DISTINCT mgi.userid + FROM {mediagallery_item} mgi + JOIN {mediagallery_gallery} mgg ON mgg.id = mgi.galleryid + WHERE mgg.instanceid = :instanceid2 AND (mgg.userid = :guserid2 OR mgi.userid = :iuserid)) + UNION + (SELECT DISTINCT c.userid + FROM {comments} c + LEFT JOIN {mediagallery_item} mgi + ON c.component = 'mod_mediagallery' AND c.commentarea = 'item' AND mgi.id = c.itemid + JOIN {mediagallery_gallery} mgg + ON c.component = 'mod_mediagallery' + AND (c.commentarea = 'gallery' AND mgg.id = c.itemid + OR c.commentarea = 'item' AND mgg.id = mgi.galleryid) + WHERE c.component = 'mod_mediagallery' AND mgg.instanceid = :instanceid3 + AND (mgg.userid = :guserid3 OR mgi.userid = :iuserid2 OR c.userid = :cuserid))"; $galleryidsql = "SELECT g.id FROM {mediagallery_gallery} g WHERE userid = :userid AND instanceid = :instanceid"; @@ -400,6 +454,14 @@ public static function delete_data_for_user(approved_contextlist $contextlist) { } $instanceid = $DB->get_field('course_modules', 'instance', ['id' => $context->instanceid], MUST_EXIST); + // Get users for completion update. + $completionupdateparams = [ + 'instanceid1' => $instanceid, 'guserid' => $userid, + 'instanceid2' => $instanceid, 'guserid2' => $userid, 'iuserid' => $userid, + 'instanceid3' => $instanceid, 'guserid3' => $userid, 'iuserid2' => $userid, 'cuserid' => $userid, + ]; + $completionupdateuserids = $DB->get_fieldset_sql($completionupdatesql, $completionupdateparams); + $params = [ 'userid' => $userid, 'instanceid' => $instanceid, @@ -414,15 +476,27 @@ public static function delete_data_for_user(approved_contextlist $contextlist) { \core_tag\privacy\provider::delete_item_tags_select($context, 'mod_mediagallery', 'item', "IN ($itemidsql)", $params); + $singlecontextlist = new approved_contextlist($contextlist->get_user(), 'mod_mediagallery', [$context->id]); + \core_comment\privacy\provider::delete_comments_for_user($singlecontextlist, 'mod_mediagallery', 'gallery'); + \core_comment\privacy\provider::delete_comments_for_user($singlecontextlist, 'mod_mediagallery', 'item'); + // We delete these last as the deletes above depend on these records. $DB->delete_records_select('mediagallery_userfeedback', "itemid IN ($itemidsql)", $params); $DB->delete_records_select('mediagallery_item', "galleryid IN ($galleryidsql)", $params); $DB->delete_records('mediagallery_gallery', $params); - } - \core_comment\privacy\provider::delete_comments_for_user($contextlist, 'mod_mediagallery', 'gallery'); - \core_comment\privacy\provider::delete_comments_for_user($contextlist, 'mod_mediagallery', 'item'); + // Update completion state. + // TODO: Test. + $cm = get_coursemodule_from_instance('mediagallery', $instanceid); + $completion = new \completion_info(get_course($cm->course)); + if ($completion->is_enabled($cm) == COMPLETION_TRACKING_AUTOMATIC) { + foreach ($completionupdateuserids as $completionupdateuserid) { + $completion->update_state($cm, COMPLETION_INCOMPLETE, $completionupdateuserid); + } + } + + } } @@ -482,7 +556,8 @@ public static function get_users_in_context(userlist $userlist) { * @param approved_userlist $userlist The approved context and user information to delete information for. */ public static function delete_data_for_users(approved_userlist $userlist) { - global $DB; + global $CFG, $DB; + require_once($CFG->libdir . '/completionlib.php'); $context = $userlist->get_context(); if ($context->contextlevel != CONTEXT_MODULE) { @@ -496,6 +571,40 @@ public static function delete_data_for_users(approved_userlist $userlist) { } list($insql, $inparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED); + list($ginsql, $ginparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED, 'guserid'); + list($ginsql2, $ginparams2) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED, 'guserid2'); + list($ginsql3, $ginparams3) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED, 'guserid3'); + list($iinsql, $iinparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED, 'iuserid'); + list($iinsql2, $iinparams2) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED, 'iuserid2'); + list($cinsql, $cinparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED, 'cuserid'); + + // Get users for completion update. + $completionupdateparams = array_merge( + ['instanceid1' => $instanceid], $ginparams, + ['instanceid2' => $instanceid], $ginparams2, $iinparams, + ['instanceid3' => $instanceid], $ginparams3, $iinparams2, $cinparams + ); + $completionupdatesql = + " (SELECT DISTINCT mgg.userid + FROM {mediagallery_gallery} mgg + WHERE mgg.instanceid = :instanceid1 AND mgg.userid $ginsql) + UNION + (SELECT DISTINCT mgi.userid + FROM {mediagallery_item} mgi + JOIN {mediagallery_gallery} mgg ON mgg.id = mgi.galleryid + WHERE mgg.instanceid = :instanceid2 AND (mgg.userid $ginsql2 OR mgi.userid $iinsql)) + UNION + (SELECT DISTINCT c.userid + FROM {comments} c + LEFT JOIN {mediagallery_item} mgi + ON c.component = 'mod_mediagallery' AND c.commentarea = 'item' AND mgi.id = c.itemid + JOIN {mediagallery_gallery} mgg + ON c.component = 'mod_mediagallery' + AND (c.commentarea = 'gallery' AND mgg.id = c.itemid + OR c.commentarea = 'item' AND mgg.id = mgi.galleryid) + WHERE c.component = 'mod_mediagallery' AND mgg.instanceid = :instanceid3 + AND (mgg.userid $ginsql3 OR mgi.userid $iinsql2 OR c.userid $cinsql))"; + $completionupdateuserids = $DB->get_fieldset_sql($completionupdatesql, $completionupdateparams); $fs = get_file_storage(); @@ -525,10 +634,20 @@ public static function delete_data_for_users(approved_userlist $userlist) { $DB->delete_records_select('mediagallery_userfeedback', "itemid IN ($itemidsql)", $params); $DB->delete_records_select('mediagallery_item', "galleryid IN ($galleryidsql)", $params); - $DB->delete_records('mediagallery_gallery', $params); + $DB->delete_records_select('mediagallery_gallery', "userid $insql AND instanceid = :instanceid", $params); \core_comment\privacy\provider::delete_comments_for_users($userlist, 'mod_mediagallery', 'gallery'); \core_comment\privacy\provider::delete_comments_for_users($userlist, 'mod_mediagallery', 'item'); + + // Update completion state. + // TODO: Test. + $cm = get_coursemodule_from_instance('mediagallery', $instanceid); + $completion = new \completion_info(get_course($cm->course)); + if ($completion->is_enabled($cm) == COMPLETION_TRACKING_AUTOMATIC) { + foreach ($completionupdateuserids as $userid) { + $completion->update_state($cm, COMPLETION_INCOMPLETE, $userid); + } + } } protected static function get_mediagallery_id_from_context(\context_module $context) { diff --git a/db/events.php b/db/events.php new file mode 100644 index 0000000..5a38f6d --- /dev/null +++ b/db/events.php @@ -0,0 +1,37 @@ +. + +/** + * Media collection event handler definition. + * + * @package mod_mediagallery + * @copyright 2023 Otago Polytechnic + * @author James Calder + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$observers = [ + [ + 'eventname' => '\mod_mediagallery\event\comment_created', + 'callback' => '\mod_mediagallery\observers::comment_created', + ], + [ + 'eventname' => '\mod_mediagallery\event\comment_deleted', + 'callback' => '\mod_mediagallery\observers::comment_deleted', + ], +]; diff --git a/db/install.xml b/db/install.xml index 5ad936c..8170e26 100644 --- a/db/install.xml +++ b/db/install.xml @@ -32,6 +32,9 @@ + + + diff --git a/db/upgrade.php b/db/upgrade.php index 8b87fde..0010890 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -443,5 +443,25 @@ function xmldb_mediagallery_upgrade($oldversion) { upgrade_mod_savepoint(true, 2015082600, 'mediagallery'); } + if ($oldversion < 2024080802) { // TODO: Set actual version number. + $table = new xmldb_table('mediagallery'); + $fields = [ + new xmldb_field('completiongalleries', XMLDB_TYPE_INTEGER, '9', null, XMLDB_NOTNULL, null, + '0', 'allowlikes'), + new xmldb_field('completionitems', XMLDB_TYPE_INTEGER, '9', null, XMLDB_NOTNULL, null, + '0', 'completiongalleries'), + new xmldb_field('completioncomments', XMLDB_TYPE_INTEGER, '9', null, XMLDB_NOTNULL, null, + '0', 'completionitems'), + ]; + + foreach ($fields as $field) { + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + } + + upgrade_mod_savepoint(true, 2024080802, 'mediagallery'); // TODO: Set actual version number. + } + return true; } diff --git a/lang/en/mediagallery.php b/lang/en/mediagallery.php index 1aea7e6..e1cd43f 100644 --- a/lang/en/mediagallery.php +++ b/lang/en/mediagallery.php @@ -83,6 +83,18 @@ $string['comments'] = 'Comments'; $string['completegallery'] = 'Complete gallery'; +$string['completioncomments'] = 'Student must add comments:'; +$string['completioncommentsdesc'] = 'Student must add at least {$a} comment(s)'; +$string['completioncommentsgroup'] = 'Require comments'; +$string['completiondetail:comments'] = 'Add comments: {$a}'; +$string['completiondetail:galleries'] = 'Add galleries: {$a}'; +$string['completiondetail:items'] = 'Add items: {$a}'; +$string['completiongalleries'] = 'Student must add galleries:'; +$string['completiongalleriesdesc'] = 'Student must add at least {$a} gallery/galleries'; +$string['completiongalleriesgroup'] = 'Require galleries'; +$string['completionitems'] = 'Student must add items:'; +$string['completionitemsdesc'] = 'Student must add at least {$a} item(s)'; +$string['completionitemsgroup'] = 'Require items'; $string['configdisablestandardgallery'] = 'Prevent users from creating standard galleries.'; $string['configenablethebox'] = 'This needs to be enabled in order for users to create new theBox based collections and content. If disabled, existing theBox collections will display a message saying the activity is currently unavailable. Standard collections will not be affected.'; $string['configmaxbytes'] = 'Default maximum item file size for all media collections on the site (subject to course limits and other local settings)'; diff --git a/lib.php b/lib.php index b533584..2ebf406 100644 --- a/lib.php +++ b/lib.php @@ -43,7 +43,7 @@ function mediagallery_supports($feature) { case FEATURE_COMPLETION_TRACKS_VIEWS: return true; case FEATURE_COMPLETION_HAS_RULES: - return false; + return true; case FEATURE_GRADE_HAS_GRADE: return false; case FEATURE_GRADE_OUTCOMES: @@ -461,6 +461,82 @@ function mediagallery_pluginfile($course, $cm, $context, $filearea, array $args, send_stored_file($file, 0, 0, $forcedownload, $options); // Download MUST be forced - security! } +/** + * Add a get_coursemodule_info function in case any Media collection type wants to add 'extra' information + * for the course (see resource). + * + * Given a course_module object, this function returns any "extra" information that may be needed + * when printing this activity in a course listing. See get_array_of_activities() in course/lib.php. + * + * @param stdClass $coursemodule The coursemodule object (record). + * @return cached_cm_info An object on information that the courses + * will know about (most noticeably, an icon). + */ +function mediagallery_get_coursemodule_info($coursemodule) { + global $DB; + + $dbparams = ['id' => $coursemodule->instance]; + $fields = 'id, name, intro, introformat, completiongalleries, completionitems, completioncomments'; + if (!$collection = $DB->get_record('mediagallery', $dbparams, $fields)) { + return false; + } + + $result = new cached_cm_info(); + $result->name = $collection->name; + + if ($coursemodule->showdescription) { + // Convert intro to html. Do not filter cached version, filters run at display time. + $result->content = format_module_intro('mediagallery', $collection, $coursemodule->id, false); + } + + // Populate the custom completion rules as key => value pairs, but only if the completion mode is 'automatic'. + if ($coursemodule->completion == COMPLETION_TRACKING_AUTOMATIC) { + $result->customdata['customcompletionrules']['completiongalleries'] = $collection->completiongalleries; + $result->customdata['customcompletionrules']['completionitems'] = $collection->completionitems; + $result->customdata['customcompletionrules']['completioncomments'] = $collection->completioncomments; + } + + return $result; +} + +/** + * Callback which returns human-readable strings describing the active completion custom rules for the module instance. + * + * @param cm_info|stdClass $cm object with fields ->completion and ->customdata['customcompletionrules'] + * @return string[] $descriptions the array of descriptions for the custom rules. + */ +function mod_mediagallery_get_completion_active_rule_descriptions($cm): array { + // Values will be present in cm_info, and we assume these are up to date. + if (empty($cm->customdata['customcompletionrules']) + || $cm->completion != COMPLETION_TRACKING_AUTOMATIC) { + return []; + } + + $descriptions = []; + foreach ($cm->customdata['customcompletionrules'] as $key => $val) { + switch ($key) { + case 'completiongalleries': + if (!empty($val)) { + $descriptions[] = get_string('completiongalleriesdesc', 'mediagallery', $val); + } + break; + case 'completionitems': + if (!empty($val)) { + $descriptions[] = get_string('completionitemsdesc', 'mediagallery', $val); + } + break; + case 'completioncomments': + if (!empty($val)) { + $descriptions[] = get_string('completioncommentsdesc', 'mediagallery', $val); + } + break; + default: + break; + } + } + return $descriptions; +} + /** * Validate comment parameter before perform other comments actions * diff --git a/mod_form.php b/mod_form.php index f706d89..979a69e 100644 --- a/mod_form.php +++ b/mod_form.php @@ -258,6 +258,25 @@ public function data_preprocessing(&$toform) { if (isset($toform['galleryfocus'])) { $toform['gallerytypeoptions']['focus'] = $toform['galleryfocus']; } + + // Set up the completion checkboxes which aren't part of standard data. + // We also make the default value (if you turn on the checkbox) for those + // numbers to be 1, this will not apply unless checkbox is ticked. + $toform['completiongalleriesenabled'] = + !empty($toform['completiongalleries']) ? 1 : 0; + if (empty($toform['completiongalleries'])) { + $toform['completiongalleries'] = 1; + } + $toform['completionitemsenabled'] = + !empty($toform['completionitems']) ? 1 : 0; + if (empty($toform['completionitems'])) { + $toform['completionitems'] = 1; + } + $toform['completioncommentsenabled'] = + !empty($toform['completioncomments']) ? 1 : 0; + if (empty($toform['completioncomments'])) { + $toform['completioncomments'] = 1; + } } /** @@ -277,4 +296,77 @@ public function set_data($data) { parent::set_data($data); } + /** + * Add custom completion rules. + * + * @return string[] Array of string IDs of added items, empty array if none + */ + public function add_completion_rules(): array { + $mform =& $this->_form; + + $group = []; + $group[] =& $mform->createElement('checkbox', 'completiongalleriesenabled', '', + get_string('completiongalleries', 'mediagallery')); + $group[] =& $mform->createElement('text', 'completiongalleries', '', ['size' => 3]); + $mform->setType('completiongalleries', PARAM_INT); + $mform->addGroup($group, 'completiongalleriesgroup', get_string('completiongalleriesgroup', 'mediagallery'), [' '], + false); + $mform->disabledIf('completiongalleries', 'completiongalleriesenabled', 'notchecked'); + + $group = []; + $group[] =& $mform->createElement('checkbox', 'completionitemsenabled', '', get_string('completionitems', 'mediagallery')); + $group[] =& $mform->createElement('text', 'completionitems', '', ['size' => 3]); + $mform->setType('completionitems', PARAM_INT); + $mform->addGroup($group, 'completionitemsgroup', get_string('completionitemsgroup', 'mediagallery'), [' '], false); + $mform->disabledIf('completionitems', 'completionitemsenabled', 'notchecked'); + + $group = []; + $group[] =& $mform->createElement('checkbox', 'completioncommentsenabled', '', + get_string('completioncomments', 'mediagallery')); + $group[] =& $mform->createElement('text', 'completioncomments', '', ['size' => 3]); + $mform->setType('completioncomments', PARAM_INT); + $mform->addGroup($group, 'completioncommentsgroup', get_string('completioncommentsgroup', 'mediagallery'), [' '], + false); + $mform->disabledIf('completioncomments', 'completioncommentsenabled', 'notchecked'); + + return ['completiongalleriesgroup', 'completionitemsgroup', 'completioncommentsgroup']; + } + + /** + * Called during validation. Indicates whether a module-specific completion rule is selected. + * + * @param array $data Input data (not yet validated) + * @return bool True if one or more rules is enabled, false if none are. + */ + public function completion_rule_enabled($data): bool { + return (!empty($data['completiongalleriesenabled']) && $data['completiongalleries'] != 0) || + (!empty($data['completionitemsenabled']) && $data['completionitems'] != 0) || + (!empty($data['completioncommentsenabled']) && $data['completioncomments'] != 0); + } + + /** + * Allows module to modify the data returned by form get_data(). + * This method is also called in the bulk activity completion form. + * + * Only available on moodleform_mod. + * + * @param stdClass $data the form data to be modified. + */ + public function data_postprocessing($data): void { + parent::data_postprocessing($data); + // Turn off completion settings if the checkboxes aren't ticked. + if (!empty($data->completionunlocked)) { + $autocompletion = !empty($data->completion) && $data->completion == COMPLETION_TRACKING_AUTOMATIC; + if (empty($data->completiongalleriesenabled) || !$autocompletion) { + $data->completiongalleries = 0; + } + if (empty($data->completionitemsenabled) || !$autocompletion) { + $data->completionitems = 0; + } + if (empty($data->completioncommentsenabled) || !$autocompletion) { + $data->completioncomments = 0; + } + } + } + } diff --git a/rest.php b/rest.php index 711c3f3..e2fc625 100644 --- a/rest.php +++ b/rest.php @@ -86,7 +86,16 @@ $info->likes = $count; echo json_encode($info); } else if ($action == 'sample') { - $info = $object->copy($data[0]); + $object->copy($data[0]); + // Return new activity completion info. + $cminfo = get_fast_modinfo($course, $USER->id)->cms[$cm->id]; + $completion = \core_completion\cm_completion_details::get_instance($cminfo, $USER->id); + $activitydates = \core\activity_dates::get_dates_for_module($cminfo, $USER->id); + $actinfo = new \core_course\output\activity_information($cminfo, $completion, $activitydates); + $actinfoeft = $actinfo->export_for_template($OUTPUT); + $actinfohtml = $OUTPUT->render_from_template('core_course/activity_info', $actinfoeft); + $info = new stdClass(); + $info->actinfohtml = $actinfohtml; echo json_encode($info); } break; @@ -112,7 +121,16 @@ } if ($success) { - echo json_encode('success'); + // Return new activity completion info. + $cminfo = get_fast_modinfo($course, $USER->id)->cms[$cm->id]; + $completion = \core_completion\cm_completion_details::get_instance($cminfo, $USER->id); + $activitydates = \core\activity_dates::get_dates_for_module($cminfo, $USER->id); + $actinfo = new \core_course\output\activity_information($cminfo, $completion, $activitydates); + $actinfoeft = $actinfo->export_for_template($OUTPUT); + $actinfohtml = $OUTPUT->render_from_template('core_course/activity_info', $actinfoeft); + $info = new stdClass(); + $info->actinfohtml = $actinfohtml; + echo json_encode($info); } else { throw new moodle_exception("failed to delete $class $id"); } diff --git a/test.php b/test.php new file mode 100644 index 0000000..dd54f7f --- /dev/null +++ b/test.php @@ -0,0 +1,64 @@ +. + +/** + * Tests + * + * @package mod_mediagallery + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once('../../config.php'); + +require_admin(); + +// TODO: Delete this file. + +$action = optional_param('action', '', PARAM_ALPHANUMEXT); +$id = optional_param('id', 0, PARAM_INT); // A course_module id. +$user1id = optional_param('user1id', 0, PARAM_INT); +$user2id = optional_param('user2id', 0, PARAM_INT); + +if ($id) { + $cm = get_coursemodule_from_id('mediagallery', $id, 0, false, MUST_EXIST); + $course = $DB->get_record('course', ['id' => $cm->course], '*', MUST_EXIST); + $mediagallery = new \mod_mediagallery\collection($cm->instance); + $context = \context_module::instance($cm->id); +} + +if ($user1id) { + $user1 = $DB->get_record('user', ['id' => $user1id]); + $userlist = new \core_privacy\local\request\approved_userlist($context, 'mod_mediagallery', [$user1id]); + $contextlist = new \core_privacy\local\request\approved_contextlist($user1, 'mod_mediagallery', [$context->id]); +} + +if ($user2id) { + $userlist = new \core_privacy\local\request\approved_userlist($context, 'mod_mediagallery', [$user1id, $user2id]); +} + +echo "

Action: {$action}

\n"; + +if ($action == 'delete_data_for_all_users_in_context') { + \mod_mediagallery\privacy\provider::delete_data_for_all_users_in_context($context); +} else if ($action == 'delete_data_for_user') { + \mod_mediagallery\privacy\provider::delete_data_for_user($contextlist); +} else if ($action == 'delete_data_for_users') { + \mod_mediagallery\privacy\provider::delete_data_for_users($userlist); +} else { + echo "

Unknown action

\n"; +} + +echo "

Done

\n"; diff --git a/version.php b/version.php index 6323a09..2265003 100644 --- a/version.php +++ b/version.php @@ -28,7 +28,7 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2024080801; +$plugin->version = 2024080802; // TODO: Set actual version number. $plugin->requires = 2023100900; $plugin->component = 'mod_mediagallery'; $plugin->maturity = MATURITY_STABLE; diff --git a/yui/build/moodle-mod_mediagallery-base/moodle-mod_mediagallery-base-debug.js b/yui/build/moodle-mod_mediagallery-base/moodle-mod_mediagallery-base-debug.js index 7bf9ee7..aa50104 100644 --- a/yui/build/moodle-mod_mediagallery-base/moodle-mod_mediagallery-base-debug.js +++ b/yui/build/moodle-mod_mediagallery-base/moodle-mod_mediagallery-base-debug.js @@ -328,6 +328,9 @@ M.mod_mediagallery.base = { if (selector) { Y.one(selector + '[data-id=' + data.id + ']').remove(); } + if (responsetext.actinfohtml && Y.one('.activity-information')) { + Y.one('.activity-information').replace(responsetext.actinfohtml); + } if (data['class'] === 'collection') { // Redirect to course. window.location.href = M.cfg.wwwroot + '/course/view.php?id=' + this.courseid; @@ -752,6 +755,9 @@ M.mod_mediagallery.base = { } catch (e) { new M.core.ajaxException(); } + if (responsetext.actinfohtml && Y.one('.activity-information')) { + Y.one('.activity-information').replace(responsetext.actinfohtml); + } }, failure : function() { } diff --git a/yui/build/moodle-mod_mediagallery-base/moodle-mod_mediagallery-base-min.js b/yui/build/moodle-mod_mediagallery-base/moodle-mod_mediagallery-base-min.js index fa6a970..9051b81 100644 --- a/yui/build/moodle-mod_mediagallery-base/moodle-mod_mediagallery-base-min.js +++ b/yui/build/moodle-mod_mediagallery-base/moodle-mod_mediagallery-base-min.js @@ -1,3 +1,3 @@ YUI.add("moodle-mod_mediagallery-base",function(r,e){M.mod_mediagallery=M.mod_mediagallery||{},M.mod_mediagallery.base={defaultitemwidth:187,courseid:0,mid:0,gallery:0,lbg_setup_ran:!1,options:{enablecomments:!1,enablelikes:!1,mode:"standard"},uri:M.cfg.wwwroot+"/mod/mediagallery/rest.php",init:function(e,t,o,a,i,l){this.courseid=e,this.mid=t,this.gallery=i,l&&l.enablecomments&&(this.options.enablecomments=!0),l&&l.enablelikes&&(this.options.enablelikes=!0),l&&l.mode&&(this.options.mode=l.mode),!a&&"gallery"!==o||("thebox"!==this.options.mode?this.watch_editing_buttons(o):this.watch_delete_thebox(o)),0===i&&"gallery"===o&&this.add_remove_collection_handler(),a||this.watch_mediasize(),this.setup_sample_link(),this.watch_resize()},add_remove_collection_handler:function(){var o,a,i,e=r.one(".collection.actions .remove");e&&(o=e.hasClass("owner"),a={title:M.str.mod_mediagallery.confirmcollectiondelete,question:M.str.mod_mediagallery.removecollectionconfirm,yesLabel:M.str.moodle.submit,noLabel:M.str.moodle.cancel,closeButtonTitle:M.str.moodle.cancel},i={"class":"collection",id:this.mid},this._confirmationListener=this._confirmationListener||e.on("click",function(e){var t;e.preventDefault(),o?(e=M.str.mod_mediagallery.deleteorremovecollection,e=(e+='

')+M.str.mod_mediagallery.deleteorremovecollectionwarn,a.question='
'+e+"
",(t=new M.core.confirm(a)).on("complete-yes",function(){i.action="remove";var e=r.one('input[name="deleteorremove"]');if(e)if("DELETE"===e.get("value").toUpperCase())i.action="delete";else if(""!==e.get("value"))return;this._confirmationListener.detach(),M.mod_mediagallery.base.delete_object(i)},this)):(t=new M.core.confirm(a),i.action="remove",t.on("complete-yes",function(){this._confirmationListener.detach(),M.mod_mediagallery.base.delete_object(i)},this)),t.show()},this))},watch_delete_thebox:function(a){var i="item"===a?".item":".gallery_list_item",l={title:M.str.mod_mediagallery["confirm"+a+"delete"],yesLabel:M.str.moodle.submit,noLabel:M.str.moodle.cancel,closeButtonTitle:M.str.moodle.cancel};r.all(i+" .controls .delete").each(function(){this._confirmationListener=this._confirmationListener||this.on("click",function(e){var t,o;e.preventDefault(),e=this.hasClass("owner"),(t=this.ancestor("div"+i).getData())["class"]=a,e?(e=M.str.mod_mediagallery["deleteorremove"+a],e=(e+='

')+M.str.mod_mediagallery["deleteorremove"+a+"warn"],l.question='
'+e+"
",(o=new M.core.confirm(l)).on("complete-yes",function(){t.action="remove";var e=r.one('input[name="deleteorremove"]');if(e)if("DELETE"===e.get("value").toUpperCase())t.action="delete";else if(""!==e.get("value"))return;this._confirmationListener.detach(),M.mod_mediagallery.base.delete_object(t,i)},this)):(l.question=M.str.mod_mediagallery["remove"+a+"confirm"],o=new M.core.confirm(l),t.action="remove",o.on("complete-yes",function(){this._confirmationListener.detach(),M.mod_mediagallery.base.delete_object(t,i)},this)),o.show()},this)})},watch_editing_buttons:function(o){var a="item"===o?".item":".gallery_list_item",i={title:M.str.mod_mediagallery["confirm"+o+"delete"],origquestion:M.str.mod_mediagallery["delete"+o]+" ",yesLabel:M.str.moodle.yes,noLabel:M.str.moodle.cancel,closeButtonTitle:M.str.moodle.cancel};r.all(a+" .controls .delete").each(function(){this._confirmationListener=this._confirmationListener||this.on("click",function(e){var t;e.preventDefault(),t=this.ancestor("div"+a).getData(),i.question=i.origquestion+t.title+"?",(e=new M.core.confirm(i)).on("complete-yes",function(){this._confirmationListener.detach(),t["class"]=o,M.mod_mediagallery.base.delete_object(t,a)},this),e.show()},this)})},watch_mediasize:function(){var e=".mediasize_selector select";r.one(e)&&r.one(e).on("change",function(e){var t,o,a;e.preventDefault(),(t=r.one(".gallery")).removeClass("small"),t.removeClass("large"),a="","0"===(o=e.target.get("value"))?a="small":"2"===o&&(a="large"),""!==a&&t.addClass(a),require(["core_user/repository"],function(e){e.setUserPreference("mod_mediagallery_mediasize",o)})})},add_gallery_info_modal:function(e,t){var o,a=r.Node.create('
'),i=''+t.firstname+" "+t.lastname+"",e=[[M.str.mod_mediagallery.galleryname,t.name],[M.str.mod_mediagallery.creator,i]],null!==t.groupname&&e.push([M.str.moodle.group,t.groupname]),r.each(e,function(e){r.Node.create('
'+e[0]+'
'+e[1]+"
").appendTo(a)}),o={headerContent:M.str.mod_mediagallery.information,bodyContent:a,modal:!0},r.one(".gallery_list_item[data-id="+t.id+"] .action-icon.info").on("click",function(e){e.preventDefault(),new M.core.dialogue(o).show()})},add_item_info_modal:function(e){var t,a=r.Node.create('
'),i=[[M.str.mod_mediagallery.caption,e.caption],[M.str.mod_mediagallery.datecreated,e.timecreatedformatted],[M.str.moodle.fullnameuser,e.firstname+" "+e.lastname],[M.str.moodle.username,e.username],[M.str.moodle.group,e.groupname],[M.str.moodle.description,e.description],[M.str.mod_mediagallery.moralrights,"1"==e.moralrights?M.str.moodle.yes:M.str.moodle.no],[M.str.mod_mediagallery.copyright,e.copyrightformatted],[M.str.mod_mediagallery.originalauthor,e.originalauthor],[M.str.mod_mediagallery.productiondate,e.productiondateformatted],[M.str.mod_mediagallery.medium,e.medium],[M.str.mod_mediagallery.publisher,e.publisher],[M.str.mod_mediagallery.broadcaster,e.broadcaster],[M.str.mod_mediagallery.reference,e.reference],[M.str.mod_mediagallery.tags,e.tags]];r.each(i,function(e,t){var o=!0;(o=4!==t&&3!==t&&7!=t||null!==i[t][1]&&""!==i[t][1]?o:!1)&&r.Node.create('
'+e[0]+'
'+e[1]+"
").appendTo(a)}),t={headerContent:M.str.mod_mediagallery.information,bodyContent:a,modal:!0},r.one( -".item[data-id="+e.id+"] .action-icon.info").on("click",function(e){e.preventDefault(),new M.core.dialogue(t).show()})},delete_object:function(i,l){i.m=this.mid,i.sesskey=M.cfg.sesskey,r.io(M.mod_mediagallery.base.uri,{method:"DELETE",data:i,on:{success:function(e,t){try{var o=r.JSON.parse(t.responseText);o.error&&new M.core.ajaxException(o)}catch(a){new M.core.ajaxException}l&&r.one(l+"[data-id="+i.id+"]").remove(),"collection"===i["class"]&&(window.location.href=M.cfg.wwwroot+"/course/view.php?id="+this.courseid)},failure:function(){0}},context:this,sync:!0})},lbg_setup:function(){var e,t;this.lbg_setup_ran||(this.lbg_setup_ran=!0,e=r.one(".lb-social"),t='
',this.options.enablelikes&&(t=(t+=''),e.setHTML(t+='
'),r.delegate("click",function(e){var o,t,a,i;e.preventDefault(),e="like",o=1,t="unlike",a='
',r.one(".lb-socialactions a.like div").hasClass("unlike")&&(e="unlike",o=0,t="like",a=''),i=r.one(".lb-data"),i={sesskey:M.cfg.sesskey,m:M.mod_mediagallery.base.mid,id:i.getData("itemid"),"class":"item",action:e},r.io(M.mod_mediagallery.base.uri,{method:"POST",data:i,on:{success:function(e,t){t=JSON.parse(t.responseText);M.mod_mediagallery.base.update_likes(t.likes,o)}},context:this,sync:!0}),r.one(".lb-socialactions a.like").setHTML(a+M.str.mod_mediagallery[t])},".lb-social",".lb-socialactions a.like"))},update_likes:function(e,t){var o="";0