MDL-69331 core_h5p: Add enabled field to libraries
authorSara Arjona <sara@moodle.com>
Wed, 24 Feb 2021 15:11:12 +0000 (16:11 +0100)
committerSara Arjona <sara@moodle.com>
Thu, 15 Apr 2021 07:03:56 +0000 (09:03 +0200)
The "enabled" field has been added to the H5P libraries to let
admins decide whether a library should be used or not in the site.

15 files changed:
h5p/classes/api.php
h5p/classes/framework.php
h5p/classes/output/libraries.php
h5p/libraries.php
h5p/templates/h5plibraries.mustache
h5p/tests/api_test.php
h5p/tests/behat/h5p_libraries.feature
h5p/tests/editor_framework_test.php
h5p/tests/generator/lib.php
h5p/tests/generator_test.php
lib/db/install.xml
lib/db/upgrade.php
theme/boost/scss/moodle/core.scss
theme/boost/style/moodle.css
theme/classic/style/moodle.css

index d864e79..850904c 100644 (file)
@@ -593,4 +593,25 @@ class api {
 
         return null;
     }
+
+    /**
+     * Enable or disable a library.
+     *
+     * @param int $libraryid The id of the library to enable/disable.
+     * @param bool $isenabled True if the library should be enabled; false otherwise.
+     */
+    public static function set_library_enabled(int $libraryid, bool $isenabled): void {
+        global $DB;
+
+        $library = $DB->get_record('h5p_libraries', ['id' => $libraryid], '*', MUST_EXIST);
+        if ($library->runnable) {
+            // For now, only runnable libraries can be enabled/disabled.
+            $record = [
+                'id' => $libraryid,
+                'enabled' => $isenabled,
+            ];
+            $DB->update_record('h5p_libraries', $record);
+        }
+    }
+
 }
index d4b0d6a..e0d22db 100644 (file)
@@ -520,7 +520,7 @@ class framework implements \H5PFrameworkInterface {
 
         $results = $DB->get_records('h5p_libraries', [], 'title ASC, majorversion ASC, minorversion ASC',
             'id, machinename AS machine_name, majorversion AS major_version, minorversion AS minor_version,
-            patchversion AS patch_version, runnable, title');
+            patchversion AS patch_version, runnable, title, enabled');
 
         $libraries = array();
         foreach ($results as $library) {
index 07a8fe9..a3aef63 100644 (file)
@@ -89,6 +89,17 @@ class libraries implements renderable, templatable {
                     get_string('deletelibraryversion', 'core_h5p')
                 ));
                 $version->actionmenu = $actionmenu->export_for_template($output);
+                if ($version->enabled) {
+                    $version->toggleenabledurl = new moodle_url('/h5p/libraries.php', [
+                        'id' => $version->id,
+                        'action' => 'disable',
+                    ]);
+                } else {
+                    $version->toggleenabledurl = new moodle_url('/h5p/libraries.php', [
+                        'id' => $version->id,
+                        'action' => 'enable',
+                    ]);
+                }
                 $installed[] = $version;
             }
         }
index 8d784f6..bb5f383 100644 (file)
@@ -28,6 +28,7 @@ require_login(null, false);
 
 $deletelibrary = optional_param('deletelibrary', null, PARAM_INT);
 $confirm = optional_param('confirm', false, PARAM_BOOL);
+$action = optional_param('action', null, PARAM_ALPHANUMEXT);
 
 $context = context_system::instance();
 require_capability('moodle/h5p:updatelibraries', $context);
@@ -64,6 +65,15 @@ if ($deletelibrary) {
     die();
 }
 
+if (!is_null($action)) {
+    if ($action == 'enable' || $action == 'disable') {
+        // If action is enable or disable, library id is required too.
+        $libraryid = required_param('id', PARAM_INT);
+
+        \core_h5p\api::set_library_enabled($libraryid, ($action == 'enable'));
+    }
+}
+
 echo $OUTPUT->header();
 echo $OUTPUT->heading($pagetitle);
 echo $OUTPUT->box(get_string('librariesmanagerdescription', 'core_h5p'));
index cb6e7b0..f5dba10 100644 (file)
                 "minor_version:": 0,
                 "patch_version:": 0,
                 "runnable": 1,
-                "icon": "icon.svg"
+                "icon": "icon.svg",
+                "enabled": true,
+                "toggleenabledurl": "http://myserver.cat/h5p/libraries.php?id=26&action=disable"
             },
             {
                 "title": "Collage",
                 "major_version": 0,
                 "minor_version:": 3,
                 "patch_version:": 1,
-                "runnable": 1
+                "runnable": 1,
+                "enabled": true,
+                "toggleenabledurl": "http://myserver.cat/h5p/libraries.php?id=37&action=disable"
             },
             {
                 "title": "FontAwesome",
@@ -41,7 +45,9 @@
                 "minor_version:": 5,
                 "patch_version:": 0,
                 "runnable": 1,
-                "icon": "icon.svg"
+                "icon": "icon.svg",
+                "enabled": false,
+                "toggleenabledurl": "http://myserver.cat/h5p/libraries.php?id=54&action=enable"
             }
         ]
     }
@@ -67,6 +73,7 @@
             <table class="admintable generaltable" id="h5pcontenttypes">
                 <thead>
                     <tr>
+                        <th style="width: 5%">{{#str}}enable, core{{/str}}</th>
                         <th>{{#str}}description, core{{/str}}</th>
                         <th>{{#str}}version, core{{/str}}</th>
                         <th aria-label="{{#str}}actions, core_h5p{{/str}}"></th>
                     {{#contenttypes}}
                         {{#runnable}}
                         <tr class="">
+                            <td class="text-center">
+                            {{#enabled}}
+                                <a href="{{{toggleenabledurl}}}" aria-label="{{#str}}disable, core{{/str}}">{{#pix}}t/hide, core,{{#str}}disable{{/str}}{{/pix}}</a>
+                            {{/enabled}}
+                            {{^enabled}}
+                                <a href="{{{toggleenabledurl}}}" aria-label="{{#str}}enable, core{{/str}}">{{#pix}}t/show, core,{{#str}}enable{{/str}}{{/pix}}</a>
+                            {{/enabled}}
+                            </td>
                             <td>
                                 {{#icon}}
                                     <img alt=""
             </table>
         </div>
     </div>
-</div>
\ No newline at end of file
+</div>
index e1ec76e..24ea653 100644 (file)
@@ -35,6 +35,7 @@ defined('MOODLE_INTERNAL') || die();
  * @package    core_h5p
  * @copyright  2020 Sara Arjona <sara@moodle.com>
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @coversDefaultClass \core_h5p\api
  */
 class api_testcase extends \advanced_testcase {
 
@@ -503,4 +504,98 @@ class api_testcase extends \advanced_testcase {
             \core_h5p\file_storage::EXPORT_FILEAREA);
         $this->assertNull($exportfile);
     }
+
+    /**
+     * Test the behaviour of set_library_enabled().
+     *
+     * @covers ::set_library_enabled
+     * @dataProvider set_library_enabled_provider
+     *
+     * @param string $libraryname Library name to enable/disable.
+     * @param string $action Action to be done with the library. Supported values: enable, disable.
+     * @param int $expected Expected value for the enabled library field. -1 will be passed if the library doesn't exist.
+     */
+    public function test_set_library_enabled(string $libraryname, string $action, int $expected): void {
+        global $DB;
+
+        $this->resetAfterTest();
+
+        // Create libraries.
+        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
+        $generator->generate_h5p_data();
+
+        // Check by default the library is enabled.
+        $library = $DB->get_record('h5p_libraries', ['machinename' => $libraryname]);
+        if ($expected >= 0) {
+            $this->assertEquals(1, $library->enabled);
+            $libraryid = (int) $library->id;
+        } else {
+            // Unexisting library. Set libraryid to some unexisting id.
+            $libraryid = -1;
+            $this->expectException('dml_missing_record_exception');
+        }
+
+        \core_h5p\api::set_library_enabled($libraryid, ($action == 'enable'));
+
+        // Check the value of the "enabled" field after calling enable/disable method.
+        $libraries = $DB->get_records('h5p_libraries');
+        foreach ($libraries as $libraryid => $library) {
+            if ($library->machinename == $libraryname) {
+                $this->assertEquals($expected, $library->enabled);
+            } else {
+                // Check that only $libraryname has been enabled/disabled.
+                $this->assertEquals(1, $library->enabled);
+            }
+        }
+    }
+
+    /**
+     * Data provider for test_set_library_enabled().
+     *
+     * @return array
+     */
+    public function set_library_enabled_provider(): array {
+        return [
+            'Disable existing library' => [
+                'libraryname' => 'MainLibrary',
+                'action' => 'disable',
+                'expected' => 0,
+            ],
+            'Enable existing library' => [
+                'libraryname' => 'MainLibrary',
+                'action' => 'enable',
+                'expected' => 1,
+            ],
+            'Disable existing library (not main)' => [
+                'libraryname' => 'Library1',
+                'action' => 'disable',
+                'expected' => 0,
+            ],
+            'Enable existing library (not main)' => [
+                'libraryname' => 'Library1',
+                'action' => 'enable',
+                'expected' => 1,
+            ],
+            'Disable existing library (not runnable)' => [
+                'libraryname' => 'Library3',
+                'action' => 'disable',
+                'expected' => 1, // Not runnable libraries can't be disabled.
+            ],
+            'Enable existing library (not runnable)' => [
+                'libraryname' => 'Library3',
+                'action' => 'enable',
+                'expected' => 1,
+            ],
+            'Enable unexisting library' => [
+                'libraryname' => 'Unexisting library',
+                'action' => 'enable',
+                'expected' => -1,
+            ],
+            'Disable unexisting library' => [
+                'libraryname' => 'Unexisting library',
+                'action' => 'disable',
+                'expected' => -1,
+            ],
+        ];
+    }
 }
index 2d990b3..c5b255a 100644 (file)
@@ -61,3 +61,16 @@ Feature: Upload and list H5P libraries and content types installed
     And I should not see "H5P.FontIcons"
     And I should not see "Joubel UI"
     And I should see "Transition"
+
+  @javascript
+  Scenario: Enable/disable H5P library
+    Given I log in as "admin"
+    And I navigate to "H5P > Manage H5P content types" in site administration
+    And I upload "h5p/tests/fixtures/filltheblanks.h5p" file to "H5P content type" filemanager
+    And I click on "Upload H5P content types" "button" in the "#fitem_id_uploadlibraries" "css_element"
+    When I click on "Disable" "link" in the "Fill in the Blanks" "table_row"
+    Then "Enable" "icon" should exist in the "Fill in the Blanks" "table_row"
+    And "Disable" "icon" should not exist in the "Fill in the Blanks" "table_row"
+    And I click on "Enable" "link" in the "Fill in the Blanks" "table_row"
+    And "Disable" "icon" should exist in the "Fill in the Blanks" "table_row"
+    And "Enable" "icon" should not exist in the "Fill in the Blanks" "table_row"
index 8e3e220..1c93f87 100644 (file)
@@ -37,19 +37,24 @@ use core_h5p\local\library\autoloader;
  *
  * @runTestsInSeparateProcesses
  */
-class editor_framework_testcase extends \advanced_testcase {
+class editor_framework_test extends \advanced_testcase {
 
     /** @var editor_framework H5P editor_framework instance */
     protected $editorframework;
 
+    /**
+     * Setup to ensure that fixtures are loaded.
+     */
+    public static function setupBeforeClass(): void {
+        autoloader::register();
+    }
+
     /**
      * Set up function for tests.
      */
     protected function setUp(): void {
         parent::setUp();
 
-        autoloader::register();
-
         $this->editorframework = new editor_framework();
     }
 
@@ -362,7 +367,7 @@ class editor_framework_testcase extends \advanced_testcase {
 
         $expectedlibraries = [];
         foreach ($data as $key => $value) {
-            if (isset($value->data)) {
+            if (isset($value->data) && $value->data->runnable) {
                 $value->data->name = $value->data->machinename;
                 $value->data->majorVersion = $value->data->majorversion;
                 $value->data->minorVersion = $value->data->minorversion;
index 78c234b..15a13fe 100644 (file)
@@ -177,7 +177,7 @@ class core_h5p_generator extends \component_generator_base {
             'http://tutorial.org', 'http://example.org');
         $lib1 = $libraries[] = $this->create_library_record('Library1', 'Lib1', 2, 0, 1, '', null, null,  'http://example.org');
         $lib2 = $libraries[] = $this->create_library_record('Library2', 'Lib2', 2, 1, 1, '', null, 'http://tutorial.org');
-        $lib3 = $libraries[] = $this->create_library_record('Library3', 'Lib3', 3, 2);
+        $lib3 = $libraries[] = $this->create_library_record('Library3', 'Lib3', 3, 2, 1, '', null, null, null, true, 0);
         $lib4 = $libraries[] = $this->create_library_record('Library4', 'Lib4', 1, 1);
         $lib5 = $libraries[] = $this->create_library_record('Library5', 'Lib5', 1, 3);
 
@@ -251,20 +251,22 @@ class core_h5p_generator extends \component_generator_base {
      * @param string $addto The plugin configuration data
      * @param string $tutorial The tutorial URL
      * @param string $examlpe The example URL
+     * @param bool $enabled Whether the library is enabled or not
+     * @param int $runnable Whether the library is runnable (1) or not (0)
      * @return stdClass An object representing the added library record
      */
     public function create_library_record(string $machinename, string $title, int $majorversion = 1,
             int $minorversion = 0, int $patchversion = 1, string $semantics = '', string $addto = null,
-            string $tutorial = null, string $example = null): stdClass {
+            string $tutorial = null, string $example = null, bool $enabled = true, int $runnable = 1): stdClass {
         global $DB;
 
-        $content = array(
+        $content = [
             'machinename' => $machinename,
             'title' => $title,
             'majorversion' => $majorversion,
             'minorversion' => $minorversion,
             'patchversion' => $patchversion,
-            'runnable' => 1,
+            'runnable' => $runnable,
             'fullscreen' => 1,
             'preloadedjs' => 'js/example.js',
             'preloadedcss' => 'css/example.css',
@@ -272,8 +274,9 @@ class core_h5p_generator extends \component_generator_base {
             'semantics' => $semantics,
             'addto' => $addto,
             'tutorial' => $tutorial,
-            'example' => $example
-        );
+            'example' => $example,
+            'enabled' => $enabled,
+        ];
 
         $libraryid = $DB->insert_record('h5p_libraries', $content);
 
index b43df42..8111ad0 100644 (file)
@@ -251,6 +251,7 @@ class generator_testcase extends \advanced_testcase {
             'coremajor' => null,
             'coreminor' => null,
             'metadatasettings' => null,
+            'enabled' => 1,
         ];
 
         $this->assertEquals($expected, $data);
index 53554a6..fdeba85 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<XMLDB PATH="lib/db" VERSION="20210222" COMMENT="XMLDB file for core Moodle tables"
+<XMLDB PATH="lib/db" VERSION="20210415" COMMENT="XMLDB file for core Moodle tables"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
 >
         <FIELD NAME="metadatasettings" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="Library metadata settings"/>
         <FIELD NAME="tutorial" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="Tutorial URL"/>
         <FIELD NAME="example" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="Example URL"/>
+        <FIELD NAME="enabled" TYPE="int" LENGTH="1" NOTNULL="false" DEFAULT="1" SEQUENCE="false" COMMENT="Defines if this library is enabled (1) or not (0)"/>
       </FIELDS>
       <KEYS>
         <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
index 8704835..cb43328 100644 (file)
@@ -2545,5 +2545,20 @@ function xmldb_main_upgrade($oldversion) {
         upgrade_main_savepoint(true, 2021052500.75);
     }
 
+    if ($oldversion < 2021052500.78) {
+
+        // Define field enabled to be added to h5p_libraries.
+        $table = new xmldb_table('h5p_libraries');
+        $field = new xmldb_field('enabled', XMLDB_TYPE_INTEGER, '1', null, null, null, '1', 'example');
+
+        // Conditionally launch add field enabled.
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        // Main savepoint reached.
+        upgrade_main_savepoint(true, 2021052500.78);
+    }
+
     return true;
 }
index 8bba6e7..e5b9739 100644 (file)
@@ -2466,6 +2466,10 @@ body.h5p-embed {
     }
 }
 
+#h5pcontenttypes td {
+    vertical-align: middle;
+}
+
 .text-decoration-none {
     text-decoration: none !important; /* stylelint-disable-line declaration-no-important */
 }
index 128b5fc..c5e5a30 100644 (file)
@@ -11660,6 +11660,9 @@ body.h5p-embed #maincontent {
 body.h5p-embed .h5pmessages {
   min-height: 230px; }
 
+#h5pcontenttypes td {
+  vertical-align: middle; }
+
 .text-decoration-none {
   text-decoration: none !important;
   /* stylelint-disable-line declaration-no-important */ }
index a5186ba..047068c 100644 (file)
@@ -11878,6 +11878,9 @@ body.h5p-embed #maincontent {
 body.h5p-embed .h5pmessages {
   min-height: 230px; }
 
+#h5pcontenttypes td {
+  vertical-align: middle; }
+
 .text-decoration-none {
   text-decoration: none !important;
   /* stylelint-disable-line declaration-no-important */ }