MDL-67795 contentbank: delete content UI
authorSara Arjona <sara@moodle.com>
Thu, 16 Apr 2020 13:04:37 +0000 (15:04 +0200)
committerSara Arjona <sara@moodle.com>
Thu, 23 Apr 2020 11:09:40 +0000 (13:09 +0200)
contentbank/amd/build/actions.min.js [new file with mode: 0644]
contentbank/amd/build/actions.min.js.map [new file with mode: 0644]
contentbank/amd/src/actions.js [new file with mode: 0644]
contentbank/index.php
contentbank/tests/behat/delete_content.feature [new file with mode: 0644]
contentbank/view.php

diff --git a/contentbank/amd/build/actions.min.js b/contentbank/amd/build/actions.min.js
new file mode 100644 (file)
index 0000000..2e95d9a
Binary files /dev/null and b/contentbank/amd/build/actions.min.js differ
diff --git a/contentbank/amd/build/actions.min.js.map b/contentbank/amd/build/actions.min.js.map
new file mode 100644 (file)
index 0000000..05f46fb
Binary files /dev/null and b/contentbank/amd/build/actions.min.js.map differ
diff --git a/contentbank/amd/src/actions.js b/contentbank/amd/src/actions.js
new file mode 100644 (file)
index 0000000..caa25c5
--- /dev/null
@@ -0,0 +1,162 @@
+// 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/>.
+
+/**
+ * Module to manage content bank actions, such as delete or rename.
+ *
+ * @module     core_contentbank/actions
+ * @package    core_contentbank
+ * @copyright  2020 Sara Arjona <sara@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define([
+    'jquery',
+    'core/ajax',
+    'core/notification',
+    'core/str',
+    'core/templates',
+    'core/url',
+    'core/modal_factory',
+    'core/modal_events'],
+function($, Ajax, Notification, Str, Templates, Url, ModalFactory, ModalEvents) {
+
+    /**
+     * List of action selectors.
+     *
+     * @type {{DELETE_CONTENT: string}}
+     */
+    var ACTIONS = {
+        DELETE_CONTENT: '[data-action="deletecontent"]',
+    };
+
+    /**
+     * Actions class.
+     */
+    var Actions = function() {
+        this.registerEvents();
+    };
+
+    /**
+     * Register event listeners.
+     */
+    Actions.prototype.registerEvents = function() {
+        $(ACTIONS.DELETE_CONTENT).click(function(e) {
+            e.preventDefault();
+
+            var contentname = $(this).data('contentname');
+            var contentid = $(this).data('contentid');
+            var contextid = $(this).data('contextid');
+
+            var strings = [
+                {
+                    key: 'deletecontent',
+                    component: 'core_contentbank'
+                },
+                {
+                    key: 'deletecontentconfirm',
+                    component: 'core_contentbank',
+                    param: {
+                        name: contentname,
+                    }
+                },
+                {
+                    key: 'delete',
+                    component: 'core'
+                },
+            ];
+
+            var deleteButtonText = '';
+            Str.get_strings(strings).then(function(langStrings) {
+                var modalTitle = langStrings[0];
+                var modalContent = langStrings[1];
+                deleteButtonText = langStrings[2];
+
+                return ModalFactory.create({
+                    title: modalTitle,
+                    body: modalContent,
+                    type: ModalFactory.types.SAVE_CANCEL,
+                    large: true
+                });
+            }).done(function(modal) {
+                modal.setSaveButtonText(deleteButtonText);
+                modal.getRoot().on(ModalEvents.save, function() {
+                    // The action is now confirmed, sending an action for it.
+                    return deleteContent(contentid, contextid);
+                });
+
+                // Handle hidden event.
+                modal.getRoot().on(ModalEvents.hidden, function() {
+                    // Destroy when hidden.
+                    modal.destroy();
+                });
+
+                // Show the modal.
+                modal.show();
+
+                return;
+            }).catch(Notification.exception);
+        });
+    };
+
+    /**
+     * Delete content from the content bank.
+     *
+     * @param {int} contentid The content to delete.
+     * @param {int} contextid The contextid where the content belongs.
+     */
+    function deleteContent(contentid, contextid) {
+        var request = {
+            methodname: 'core_contentbank_delete_content',
+            args: {
+                contentids: {contentid}
+            }
+        };
+
+        var requestType = 'success';
+        Ajax.call([request])[0].then(function(data) {
+            if (data.result) {
+                return Str.get_string('contentdeleted', 'core_contentbank');
+            }
+            requestType = 'error';
+            return Str.get_string('contentnotdeleted', 'core_contentbank');
+
+        }).done(function(message) {
+            var params = {
+                contextid: contextid
+            };
+            if (requestType == 'success') {
+                params.statusmsg = message;
+            } else {
+                params.errormsg = message;
+            }
+            // Redirect to the main content bank page and display the message as a notification.
+            window.location.href = Url.relativeUrl('contentbank/index.php', params, false);
+        }).fail(Notification.exception);
+    }
+
+    return /** @alias module:core_contentbank/actions */ {
+        // Public variables and functions.
+
+        /**
+         * Initialise the contentbank actions.
+         *
+         * @method init
+         * @return {Actions}
+         */
+        'init': function() {
+            return new Actions();
+        }
+    };
+});
index 01d1db6..3a3e9c3 100644 (file)
@@ -31,6 +31,9 @@ $context = context::instance_by_id($contextid, MUST_EXIST);
 
 require_capability('moodle/contentbank:access', $context);
 
+$statusmsg = optional_param('statusmsg', '', PARAM_RAW);
+$errormsg = optional_param('errormsg', '', PARAM_RAW);
+
 $title = get_string('contentbank');
 \core_contentbank\helper::get_page_ready($context, $title);
 if ($PAGE->course) {
@@ -74,6 +77,14 @@ if (has_capability('moodle/contentbank:upload', $context)) {
 echo $OUTPUT->header();
 echo $OUTPUT->box_start('generalbox');
 
+// If needed, display notifications.
+if ($errormsg !== '') {
+    echo $OUTPUT->notification($errormsg);
+} else if ($statusmsg !== '') {
+    echo $OUTPUT->notification($statusmsg, 'notifysuccess');
+}
+
+// Render the contentbank contents.
 $folder = new \core_contentbank\output\bankcontent($foldercontents, $toolbar, $context);
 echo $OUTPUT->render($folder);
 
diff --git a/contentbank/tests/behat/delete_content.feature b/contentbank/tests/behat/delete_content.feature
new file mode 100644 (file)
index 0000000..4b370b1
--- /dev/null
@@ -0,0 +1,70 @@
+@core @core_contentbank @contentbank_h5p @_file_upload @javascript
+Feature: Delete H5P file from the content bank
+  In order remove H5P content from the content bank
+  As an admin
+  I need to be able to delete any H5P content from the content bank
+
+  Background:
+    Given I log in as "admin"
+    And I follow "Manage private files..."
+    And I upload "h5p/tests/fixtures/filltheblanks.h5p" file to "Files" filemanager
+    And I click on "Save changes" "button"
+    And I click on "Content bank" "link"
+    And I click on "Upload" "link"
+    And I click on "Choose a file..." "button"
+    And I click on "Private files" "link" in the ".fp-repo-area" "css_element"
+    And I click on "filltheblanks.h5p" "link"
+    And I click on "Select this file" "button"
+    And I click on "Save changes" "button"
+
+  Scenario: Admins can delete content from the content bank
+    Given I click on "Content bank" "link"
+    And I wait until the page is ready
+    And I should see "filltheblanks.h5p"
+    When I follow "filltheblanks.h5p"
+    And I open the action menu in "region-main-settings-menu" "region"
+    Then I should see "Delete"
+    And I choose "Delete" in the open action menu
+    And I should see "Are you sure you want to delete content 'filltheblanks.h5p'?"
+    And I click on "Cancel" "button" in the "Delete content" "dialogue"
+    And I should see "filltheblanks.h5p"
+    And I open the action menu in "region-main-settings-menu" "region"
+    And I choose "Delete" in the open action menu
+    And I click on "Delete" "button" in the "Delete content" "dialogue"
+    And I wait until the page is ready
+    And I should see "The content has been deleted."
+    And I should not see "filltheblanks.h5p"
+
+  Scenario: Users without the required capability can only delete their own content
+    Given the following "permission overrides" exist:
+      | capability                            | permission | role    | contextlevel | reference |
+      | moodle/contentbank:deleteanycontent   | Prohibit   | manager | System       |           |
+    And the following "users" exist:
+      | username    | firstname | lastname | email              |
+      | manager     | Max       | Manager  | man@example.com    |
+    And the following "role assigns" exist:
+      | user        | role      | contextlevel  | reference     |
+      | manager     | manager       | System    |               |
+    And I log out
+    When I log in as "manager"
+    And I click on "Content bank" "link"
+    And I wait until the page is ready
+    And I should see "filltheblanks.h5p"
+    And I follow "filltheblanks.h5p"
+    Then ".header-actions-container" "css_element" should not exist
+    And I click on "Private files" "link"
+    And I upload "h5p/tests/fixtures/find-the-words.h5p" file to "Files" filemanager
+    And I click on "Save changes" "button"
+    And I click on "Content bank" "link"
+    And I click on "Upload" "link"
+    And I click on "Choose a file..." "button"
+    And I click on "Private files" "link" in the ".fp-repo-area" "css_element"
+    And I click on "find-the-words.h5p" "link"
+    And I click on "Select this file" "button"
+    And I click on "Save changes" "button"
+    And I wait until the page is ready
+    And I should see "filltheblanks.h5p"
+    And I should see "find-the-words.h5p"
+    When I follow "find-the-words.h5p"
+    And I open the action menu in "region-main-settings-menu" "region"
+    Then I should see "Delete"
index 9514bdf..4c4a4c0 100644 (file)
@@ -27,6 +27,10 @@ require('../config.php');
 require_login();
 
 $id = required_param('id', PARAM_INT);
+$deletecontent = optional_param('deletecontent', null, PARAM_INT);
+
+$PAGE->requires->js_call_amd('core_contentbank/actions', 'init');
+
 $record = $DB->get_record('contentbank_content', ['id' => $id], '*', MUST_EXIST);
 $context = context::instance_by_id($record->contextid, MUST_EXIST);
 require_capability('moodle/contentbank:access', $context);
@@ -52,15 +56,42 @@ $title .= ": ".$record->name;
 $PAGE->set_title($title);
 $PAGE->set_pagetype('contenbank');
 
+$contenttypeclass = "\\$record->contenttype\\contenttype";
+$contenttype = new $contenttypeclass($context);
+$contentclass = "\\$record->contenttype\\content";
+$content = new $contentclass($record);
+if ($contenttype->can_delete($content)) {
+    // Create the cog menu with all the secondary actions, such as delete, rename...
+    $actionmenu = new action_menu();
+    $actionmenu->set_alignment(action_menu::TR, action_menu::BR);
+    // Add the delete content item to the menu.
+    $attributes = [
+                'data-action' => 'deletecontent',
+                'data-contentname' => $content->get_name(),
+                'data-contentid' => $content->get_id(),
+                'data-contextid' => $context->id,
+            ];
+    $actionmenu->add_secondary_action(new action_menu_link(
+        new moodle_url('#'),
+        new pix_icon('t/delete', get_string('delete')),
+        get_string('delete'),
+        false,
+        $attributes
+    ));
+
+    // Add the cog menu to the header.
+    $PAGE->add_header_action(html_writer::div(
+        $OUTPUT->render($actionmenu),
+        'd-print-none',
+        ['id' => 'region-main-settings-menu']
+    ));
+}
+
 echo $OUTPUT->header();
 echo $OUTPUT->box_start('generalbox');
 
-$managerlass = "\\$record->contenttype\\contenttype";
-if (class_exists($managerlass)) {
-    $manager = new $managerlass($context);
-    if ($manager->can_access()) {
-        echo $manager->get_view_content($record);
-    }
+if ($contenttype->can_access()) {
+    echo $contenttype->get_view_content($record);
 }
 
 echo $OUTPUT->box_end();