MDL-67585 core_course: add content_item_service class
authorJake Dallimore <jake@moodle.com>
Tue, 21 Jan 2020 03:43:08 +0000 (11:43 +0800)
committerJake Dallimore <jake@moodle.com>
Thu, 20 Feb 2020 01:28:57 +0000 (09:28 +0800)
The service object encapsulates access control logic and allows
consumers to query content items in different contexts/scopes.

course/classes/local/service/content_item_service.php [new file with mode: 0644]
course/tests/services_content_item_service_test.php [new file with mode: 0644]

diff --git a/course/classes/local/service/content_item_service.php b/course/classes/local/service/content_item_service.php
new file mode 100644 (file)
index 0000000..6b04ace
--- /dev/null
@@ -0,0 +1,98 @@
+<?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/>.
+
+/**
+ * Contains the content_item_service class.
+ *
+ * @package    core
+ * @subpackage course
+ * @copyright  2020 Jake Dallimore <jrhdallimore@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace core_course\local\service;
+
+defined('MOODLE_INTERNAL') || die();
+
+use core_course\local\exporters\course_content_items_exporter;
+use core_course\local\repository\content_item_readonly_repository_interface;
+
+/**
+ * The content_item_service class, providing the api for interacting with content items.
+ *
+ * @copyright  2020 Jake Dallimore <jrhdallimore@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class content_item_service {
+
+    /** @var content_item_readonly_repository_interface $repository a repository for content items. */
+    private $repository;
+
+    /**
+     * The content_item_service constructor.
+     *
+     * @param content_item_readonly_repository_interface $repository a content item repository.
+     */
+    public function __construct(content_item_readonly_repository_interface $repository) {
+        $this->repository = $repository;
+    }
+
+    /**
+     * Return a representation of the available content items, for a user in a course.
+     *
+     * @param \stdClass $user the user to check access for.
+     * @param \stdClass $course the course to scope the content items to.
+     * @param array $linkparams the desired section to return to.
+     * @return \stdClass[] the content items, scoped to a course.
+     */
+    public function get_content_items_for_user_in_course(\stdClass $user, \stdClass $course, array $linkparams = []): array {
+        global $PAGE;
+
+        if (!has_capability('moodle/course:manageactivities', \context_course::instance($course->id), $user)) {
+            return [];
+        }
+
+        // Get all the visible content items.
+        $allcontentitems = $this->repository->find_all_for_course($course, $user);
+
+        // Now, check access to these items for the user.
+        $availablecontentitems = array_filter($allcontentitems, function($contentitem) use ($course, $user) {
+            // Check the parent module access for the user.
+            return course_allowed_module($course, explode('_', $contentitem->get_component_name())[1], $user);
+        });
+
+        // Add the link params to the link, if any have been provided.
+        if (!empty($linkparams)) {
+            $availablecontentitems = array_map(function ($item) use ($linkparams) {
+                $item->get_link()->params($linkparams);
+                return $item;
+            }, $availablecontentitems);
+        }
+
+        // Export the objects to get the formatted objects for transfer/display.
+        $ciexporter = new course_content_items_exporter(
+            $availablecontentitems,
+            ['context' => \context_course::instance($course->id)]
+        );
+        $exported = $ciexporter->export($PAGE->get_renderer('course'));
+
+        // Sort by title for return.
+        usort($exported->content_items, function($a, $b) {
+            return $a->title > $b->title;
+        });
+
+        return $exported->content_items;
+    }
+}
diff --git a/course/tests/services_content_item_service_test.php b/course/tests/services_content_item_service_test.php
new file mode 100644 (file)
index 0000000..eb9ff72
--- /dev/null
@@ -0,0 +1,106 @@
+<?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/>.
+
+/**
+ * Contains the tests for the content_item_service class.
+ *
+ * @package    core
+ * @subpackage course
+ * @copyright  2020 Jake Dallimore <jrhdallimore@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace tests\course;
+
+defined('MOODLE_INTERNAL') || die();
+
+use \core_course\local\service\content_item_service;
+use \core_course\local\repository\content_item_readonly_repository;
+
+/**
+ * The tests for the content_item_service class.
+ *
+ * @copyright  2020 Jake Dallimore <jrhdallimore@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class services_content_item_service_testcase extends \advanced_testcase {
+
+    /**
+     * Test confirming that content items are returned by the service.
+     */
+    public function test_get_content_items_for_user_in_course_basic() {
+        $this->resetAfterTest();
+
+        // Create a user in a course.
+        $course = $this->getDataGenerator()->create_course();
+        $user = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
+
+        $cis = new content_item_service(new content_item_readonly_repository());
+        $contentitems = $cis->get_content_items_for_user_in_course($user, $course);
+
+        foreach ($contentitems as $key => $contentitem) {
+            $this->assertObjectHasAttribute('id', $contentitem);
+            $this->assertObjectHasAttribute('name', $contentitem);
+            $this->assertObjectHasAttribute('title', $contentitem);
+            $this->assertObjectHasAttribute('link', $contentitem);
+            $this->assertObjectHasAttribute('icon', $contentitem);
+            $this->assertObjectHasAttribute('help', $contentitem);
+            $this->assertObjectHasAttribute('archetype', $contentitem);
+            $this->assertObjectHasAttribute('componentname', $contentitem);
+        }
+    }
+
+    /**
+     * Test confirming that access control is performed when asking the service to return content items for a user in a course.
+     */
+    public function test_get_content_items_for_user_in_course_permissions() {
+        $this->resetAfterTest();
+        global $DB;
+
+        // Create a user in a course.
+        $course = $this->getDataGenerator()->create_course();
+        $user = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
+
+        // No cap override, so assign should be returned.
+        $cis = new content_item_service(new content_item_readonly_repository());
+        $contentitems = $cis->get_content_items_for_user_in_course($user, $course);
+        $this->assertContains('assign', array_column($contentitems, 'name'));
+
+        // Override the capability 'mod/assign:addinstance' for the 'editing teacher' role.
+        $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
+        assign_capability('mod/assign:addinstance', CAP_PROHIBIT, $teacherrole->id, \context_course::instance($course->id));
+
+        $contentitems = $cis->get_content_items_for_user_in_course($user, $course);
+        $this->assertArrayNotHasKey('assign', $contentitems);
+    }
+
+    /**
+     * Test confirming that params can be added to the content item's link.
+     */
+    public function test_get_content_item_for_user_in_course_link_params() {
+        $this->resetAfterTest();
+
+        // Create a user in a course.
+        $course = $this->getDataGenerator()->create_course();
+        $user = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
+
+        $cis = new content_item_service(new content_item_readonly_repository());
+        $contentitems = $cis->get_content_items_for_user_in_course($user, $course, ['sr' => 7]);
+
+        foreach ($contentitems as $item) {
+            $this->assertStringContainsString('sr=7', $item->link);
+        }
+    }
+}