first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
@@ -0,0 +1,92 @@
@mod @mod_page @core_completion
Feature: View activity completion information in the Page resource
In order to have visibility of page completion requirements
As a student
I need to be able to view my page completion progress
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Vinnie | Student1 | student1@example.com |
| teacher1 | Darrell | Teacher1 | teacher1@example.com |
And the following "courses" exist:
| fullname | shortname | category | enablecompletion | showcompletionconditions |
| Course 1 | C1 | 0 | 1 | 1 |
| Course 2 | C2 | 0 | 1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| teacher1 | C1 | editingteacher |
Scenario: View automatic completion items as teacher
Given the following "activity" exists:
| activity | page |
| course | C1 |
| idnumber | page1 |
| name | Music history |
| intro | A lesson learned in life |
| completion | 2 |
| completionview | 1 |
When I am on the "Music history" "page activity" page logged in as teacher1
Then "Music history" should have the "View" completion condition
Scenario: View automatic completion items as student
Given the following "activity" exists:
| activity | page |
| course | C1 |
| idnumber | page1 |
| name | Music history |
| intro | A lesson learned in life |
| completion | 2 |
| completionview | 1 |
When I am on the "Music history" "page activity" page logged in as student1
Then the "View" completion condition of "Music history" is displayed as "done"
@javascript
Scenario: Use manual completion as teacher
Given the following "activity" exists:
| activity | page |
| course | C1 |
| idnumber | page1 |
| name | Music history |
| intro | A lesson learned in life |
| completion | 1 |
# Teacher view.
When I am on the "Music history" "page activity" page logged in as teacher1
Then the manual completion button for "Music history" should be disabled
@javascript
Scenario: Use manual completion as student
Given the following "activity" exists:
| activity | page |
| course | C1 |
| idnumber | page1 |
| name | Music history |
| intro | A lesson learned in life |
| completion | 1 |
# Teacher view.
When I am on the "Music history" "page activity" page logged in as student1
And I toggle the manual completion state of "Music history"
And the manual completion button of "Music history" is displayed as "Done"
Scenario: The manual completion button will not be shown on the course page if the Show activity completion conditions is set to No as teacher
Given the following "activity" exists:
| activity | page |
| course | C2 |
| idnumber | page1 |
| name | Music history |
| intro | A lesson learned in life |
| completion | 1 |
When I am on the "Music history" "page activity" page logged in as teacher1
Then the manual completion button for "Music history" should not exist
Scenario: The manual completion button will not be shown on the course page if the Show activity completion conditions is set to No as student
Given the following "activity" exists:
| activity | page |
| course | C2 |
| idnumber | page1 |
| name | Music history |
| intro | A lesson learned in life |
| completion | 1 |
When I am on the "Music history" "page activity" page logged in as student1
Then the manual completion button for "Music history" should not exist
@@ -0,0 +1,28 @@
@mod @mod_page
Feature: Configure page appearance
In order to change the appearance of the page resource
As an admin
I need to configure the page appearance settings
Background:
Given the following "courses" exist:
| shortname | fullname |
| C1 | Course 1 |
And the following "activities" exist:
| activity | name | intro | course | idnumber |
| page | PageName1 | PageDesc1 | C1 | PAGE1 |
@javascript
Scenario Outline: Hide and display page features
Given I am on the "PageName1" "page activity editing" page logged in as admin
And I expand all fieldsets
And I set the field "<feature>" to "<value>"
And I press "Save and display"
Then I <shouldornot> see "<lookfor>" in the "region-main" "region"
Examples:
| feature | lookfor | value | shouldornot |
| Display page description | PageDesc1 | 1 | should |
| Display page description | PageDesc1 | 0 | should not |
| Display last modified date | Last modified: | 1 | should |
| Display last modified date | Last modified: | 0 | should not |
+233
View File
@@ -0,0 +1,233 @@
<?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/>.
namespace mod_page;
use core_external\external_api;
use externallib_advanced_testcase;
use mod_page_external;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/webservice/tests/helpers.php');
/**
* External mod_page functions unit tests
*
* @package mod_page
* @category external
* @copyright 2015 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 3.0
*/
class externallib_test extends externallib_advanced_testcase {
/**
* Test view_page
*/
public function test_view_page(): void {
global $DB;
$this->resetAfterTest(true);
// Setup test data.
$course = $this->getDataGenerator()->create_course();
$page = $this->getDataGenerator()->create_module('page', array('course' => $course->id));
$context = \context_module::instance($page->cmid);
$cm = get_coursemodule_from_instance('page', $page->id);
// Test invalid instance id.
try {
mod_page_external::view_page(0);
$this->fail('Exception expected due to invalid mod_page instance id.');
} catch (\moodle_exception $e) {
$this->assertEquals('invalidrecord', $e->errorcode);
}
// Test not-enrolled user.
$user = self::getDataGenerator()->create_user();
$this->setUser($user);
try {
mod_page_external::view_page($page->id);
$this->fail('Exception expected due to not enrolled user.');
} catch (\moodle_exception $e) {
$this->assertEquals('requireloginerror', $e->errorcode);
}
// Test user with full capabilities.
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
$this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id);
// Trigger and capture the event.
$sink = $this->redirectEvents();
$result = mod_page_external::view_page($page->id);
$result = external_api::clean_returnvalue(mod_page_external::view_page_returns(), $result);
$events = $sink->get_events();
$this->assertCount(1, $events);
$event = array_shift($events);
// Checking that the event contains the expected values.
$this->assertInstanceOf('\mod_page\event\course_module_viewed', $event);
$this->assertEquals($context, $event->get_context());
$moodlepage = new \moodle_url('/mod/page/view.php', array('id' => $cm->id));
$this->assertEquals($moodlepage, $event->get_url());
$this->assertEventContextNotUsed($event);
$this->assertNotEmpty($event->get_name());
// Test user with no capabilities.
// We need a explicit prohibit since this capability is only defined in authenticated user and guest roles.
assign_capability('mod/page:view', CAP_PROHIBIT, $studentrole->id, $context->id);
// Empty all the caches that may be affected by this change.
accesslib_clear_all_caches_for_unit_testing();
\course_modinfo::clear_instance_cache();
try {
mod_page_external::view_page($page->id);
$this->fail('Exception expected due to missing capability.');
} catch (\moodle_exception $e) {
$this->assertEquals('requireloginerror', $e->errorcode);
}
}
/**
* Test test_mod_page_get_pages_by_courses
*/
public function test_mod_page_get_pages_by_courses(): void {
global $DB;
$this->resetAfterTest(true);
$course1 = self::getDataGenerator()->create_course();
$course2 = self::getDataGenerator()->create_course();
$student = self::getDataGenerator()->create_user();
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
$this->getDataGenerator()->enrol_user($student->id, $course1->id, $studentrole->id);
// First page.
$record = new \stdClass();
$record->course = $course1->id;
$page1 = self::getDataGenerator()->create_module('page', $record);
// Second page.
$record = new \stdClass();
$record->course = $course2->id;
$page2 = self::getDataGenerator()->create_module('page', $record);
// Execute real Moodle enrolment as we'll call unenrol() method on the instance later.
$enrol = enrol_get_plugin('manual');
$enrolinstances = enrol_get_instances($course2->id, true);
foreach ($enrolinstances as $courseenrolinstance) {
if ($courseenrolinstance->enrol == "manual") {
$instance2 = $courseenrolinstance;
break;
}
}
$enrol->enrol_user($instance2, $student->id, $studentrole->id);
self::setUser($student);
$returndescription = mod_page_external::get_pages_by_courses_returns();
// Create what we expect to be returned when querying the two courses.
$expectedfields = array('id', 'coursemodule', 'course', 'name', 'intro', 'introformat', 'introfiles', 'lang',
'content', 'contentformat', 'contentfiles', 'legacyfiles', 'legacyfileslast', 'display',
'displayoptions', 'revision', 'timemodified', 'section', 'visible', 'groupmode', 'groupingid');
// Add expected coursemodule and data.
$page1->coursemodule = $page1->cmid;
$page1->introformat = 1;
$page1->contentformat = 1;
$page1->section = 0;
$page1->visible = true;
$page1->groupmode = 0;
$page1->groupingid = 0;
$page1->introfiles = [];
$page1->contentfiles = [];
$page1->lang = '';
$page2->coursemodule = $page2->cmid;
$page2->introformat = 1;
$page2->contentformat = 1;
$page2->section = 0;
$page2->visible = true;
$page2->groupmode = 0;
$page2->groupingid = 0;
$page2->introfiles = [];
$page2->contentfiles = [];
$page2->lang = '';
foreach ($expectedfields as $field) {
$expected1[$field] = $page1->{$field};
$expected2[$field] = $page2->{$field};
}
$expectedpages = array($expected2, $expected1);
// Call the external function passing course ids.
$result = mod_page_external::get_pages_by_courses(array($course2->id, $course1->id));
$result = external_api::clean_returnvalue($returndescription, $result);
$this->assertEquals($expectedpages, $result['pages']);
$this->assertCount(0, $result['warnings']);
// Call the external function without passing course id.
$result = mod_page_external::get_pages_by_courses();
$result = external_api::clean_returnvalue($returndescription, $result);
$this->assertEquals($expectedpages, $result['pages']);
$this->assertCount(0, $result['warnings']);
// Add a file to the intro.
$filename = "file.txt";
$filerecordinline = array(
'contextid' => \context_module::instance($page2->cmid)->id,
'component' => 'mod_page',
'filearea' => 'intro',
'itemid' => 0,
'filepath' => '/',
'filename' => $filename,
);
$fs = get_file_storage();
$timepost = time();
$fs->create_file_from_string($filerecordinline, 'image contents (not really)');
$result = mod_page_external::get_pages_by_courses(array($course2->id, $course1->id));
$result = external_api::clean_returnvalue($returndescription, $result);
$this->assertCount(1, $result['pages'][0]['introfiles']);
$this->assertEquals($filename, $result['pages'][0]['introfiles'][0]['filename']);
// Unenrol user from second course.
$enrol->unenrol_user($instance2, $student->id);
array_shift($expectedpages);
// Call the external function without passing course id.
$result = mod_page_external::get_pages_by_courses();
$result = external_api::clean_returnvalue($returndescription, $result);
$this->assertEquals($expectedpages, $result['pages']);
// Call for the second course we unenrolled the user from, expected warning.
$result = mod_page_external::get_pages_by_courses(array($course2->id));
$this->assertCount(1, $result['warnings']);
$this->assertEquals('1', $result['warnings'][0]['warningcode']);
$this->assertEquals($course2->id, $result['warnings'][0]['itemid']);
}
}
+76
View File
@@ -0,0 +1,76 @@
<?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/>.
/**
* mod_page data generator
*
* @package mod_page
* @category test
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Page module data generator class
*
* @package mod_page
* @category test
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_page_generator extends testing_module_generator {
public function create_instance($record = null, array $options = null) {
global $CFG;
require_once($CFG->dirroot . '/lib/resourcelib.php');
$record = (object)(array)$record;
if (!isset($record->content)) {
$record->content = 'Test page content';
}
if (!isset($record->contentformat)) {
$record->contentformat = FORMAT_MOODLE;
}
if (!isset($record->display)) {
$record->display = RESOURCELIB_DISPLAY_AUTO;
}
if (!isset($record->printintro)) {
$record->printintro = 0;
}
if (!isset($record->printlastmodified)) {
$record->printlastmodified = 1;
}
$instance = parent::create_instance($record, (array)$options);
// Insert files for the 'content' file area.
$instance = $this->insert_files(
$instance,
$record,
'page',
\context_module::instance($instance->cmid),
'mod_page',
'content',
0
);
return $instance;
}
}
+53
View File
@@ -0,0 +1,53 @@
<?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/>.
namespace mod_page;
/**
* PHPUnit data generator testcase
*
* @package mod_page
* @category phpunit
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class generator_test extends \advanced_testcase {
public function test_generator(): void {
global $DB, $SITE;
$this->resetAfterTest(true);
$this->assertEquals(0, $DB->count_records('page'));
/** @var mod_page_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('mod_page');
$this->assertInstanceOf('mod_page_generator', $generator);
$this->assertEquals('page', $generator->get_modulename());
$generator->create_instance(array('course'=>$SITE->id));
$generator->create_instance(array('course'=>$SITE->id));
$page = $generator->create_instance(array('course'=>$SITE->id));
$this->assertEquals(3, $DB->count_records('page'));
$cm = get_coursemodule_from_instance('page', $page->id);
$this->assertEquals($page->id, $cm->instance);
$this->assertEquals('page', $cm->modname);
$this->assertEquals($SITE->id, $cm->course);
$context = \context_module::instance($cm->id);
$this->assertEquals($page->cmid, $context->instanceid);
}
}
+222
View File
@@ -0,0 +1,222 @@
<?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/>.
/**
* Unit tests for mod_page lib
*
* @package mod_page
* @category external
* @copyright 2015 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 3.0
*/
namespace mod_page;
defined('MOODLE_INTERNAL') || die();
/**
* Unit tests for mod_page lib
*
* @package mod_page
* @category external
* @copyright 2015 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 3.0
*/
class lib_test extends \advanced_testcase {
/**
* Prepares things before this test case is initialised
* @return void
*/
public static function setUpBeforeClass(): void {
global $CFG;
require_once($CFG->dirroot . '/mod/page/lib.php');
}
/**
* Test page_view
* @return void
*/
public function test_page_view(): void {
global $CFG;
$CFG->enablecompletion = 1;
$this->resetAfterTest();
// Setup test data.
$course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
$page = $this->getDataGenerator()->create_module('page', array('course' => $course->id),
array('completion' => 2, 'completionview' => 1));
$context = \context_module::instance($page->cmid);
$cm = get_coursemodule_from_instance('page', $page->id);
// Trigger and capture the event.
$sink = $this->redirectEvents();
$this->setAdminUser();
page_view($page, $course, $cm, $context);
$events = $sink->get_events();
// 2 additional events thanks to completion.
$this->assertCount(3, $events);
$event = array_shift($events);
// Checking that the event contains the expected values.
$this->assertInstanceOf('\mod_page\event\course_module_viewed', $event);
$this->assertEquals($context, $event->get_context());
$moodleurl = new \moodle_url('/mod/page/view.php', array('id' => $cm->id));
$this->assertEquals($moodleurl, $event->get_url());
$this->assertEventContextNotUsed($event);
$this->assertNotEmpty($event->get_name());
// Check completion status.
$completion = new \completion_info($course);
$completiondata = $completion->get_data($cm);
$this->assertEquals(1, $completiondata->completionstate);
}
public function test_page_core_calendar_provide_event_action(): void {
$this->resetAfterTest();
$this->setAdminUser();
// Create the activity.
$course = $this->getDataGenerator()->create_course();
$page = $this->getDataGenerator()->create_module('page', array('course' => $course->id));
// Create a calendar event.
$event = $this->create_action_event($course->id, $page->id,
\core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED);
// Create an action factory.
$factory = new \core_calendar\action_factory();
// Decorate action event.
$actionevent = mod_page_core_calendar_provide_event_action($event, $factory);
// Confirm the event was decorated.
$this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
$this->assertEquals(get_string('view'), $actionevent->get_name());
$this->assertInstanceOf('moodle_url', $actionevent->get_url());
$this->assertEquals(1, $actionevent->get_item_count());
$this->assertTrue($actionevent->is_actionable());
}
public function test_page_core_calendar_provide_event_action_already_completed(): void {
global $CFG;
$this->resetAfterTest();
$this->setAdminUser();
$CFG->enablecompletion = 1;
// Create the activity.
$course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
$page = $this->getDataGenerator()->create_module('page', array('course' => $course->id),
array('completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS));
// Get some additional data.
$cm = get_coursemodule_from_instance('page', $page->id);
// Create a calendar event.
$event = $this->create_action_event($course->id, $page->id,
\core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED);
// Mark the activity as completed.
$completion = new \completion_info($course);
$completion->set_module_viewed($cm);
// Create an action factory.
$factory = new \core_calendar\action_factory();
// Decorate action event.
$actionevent = mod_page_core_calendar_provide_event_action($event, $factory);
// Ensure result was null.
$this->assertNull($actionevent);
}
/**
* Test mod_page_core_calendar_provide_event_action with user override
*/
public function test_page_core_calendar_provide_event_action_user_override(): void {
global $CFG, $USER;
$this->resetAfterTest();
$this->setAdminUser();
$user = $this->getDataGenerator()->create_user();
$CFG->enablecompletion = 1;
// Create the activity.
$course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
$page = $this->getDataGenerator()->create_module('page', array('course' => $course->id),
array('completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS));
// Get some additional data.
$cm = get_coursemodule_from_instance('page', $page->id);
// Create a calendar event.
$event = $this->create_action_event($course->id, $page->id,
\core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED);
// Mark the activity as completed.
$completion = new \completion_info($course);
$completion->set_module_viewed($cm);
// Create an action factory.
$factory = new \core_calendar\action_factory();
// Decorate action event.
$actionevent = mod_page_core_calendar_provide_event_action($event, $factory, $USER->id);
// Decorate action with a userid override.
$actionevent2 = mod_page_core_calendar_provide_event_action($event, $factory, $user->id);
// Ensure result was null because it has been marked as completed for the associated user.
// Logic was brought across from the "_already_completed" function.
$this->assertNull($actionevent);
// Confirm the event was decorated.
$this->assertNotNull($actionevent2);
$this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent2);
$this->assertEquals(get_string('view'), $actionevent2->get_name());
$this->assertInstanceOf('moodle_url', $actionevent2->get_url());
$this->assertEquals(1, $actionevent2->get_item_count());
$this->assertTrue($actionevent2->is_actionable());
}
/**
* Creates an action event.
*
* @param int $courseid The course id.
* @param int $instanceid The instance id.
* @param string $eventtype The event type.
* @return bool|calendar_event
*/
private function create_action_event($courseid, $instanceid, $eventtype) {
$event = new \stdClass();
$event->name = 'Calendar event';
$event->modulename = 'page';
$event->courseid = $courseid;
$event->instance = $instanceid;
$event->type = CALENDAR_EVENT_TYPE_ACTION;
$event->eventtype = $eventtype;
$event->timestart = time();
return \calendar_event::create($event);
}
}