diff --git a/mod/lesson/lang/en/lesson.php b/mod/lesson/lang/en/lesson.php index b47b954c9d8..4b3cea46fc4 100644 --- a/mod/lesson/lang/en/lesson.php +++ b/mod/lesson/lang/en/lesson.php @@ -155,6 +155,7 @@ $string['displayscorewithessays'] = '

You earned {$a->score} out of {$a->tempm

Your {$a->essayquestions} essay question(s) will be graded and added into your final score at a later date.

Your current grade without the essay question(s) is {$a->score} out of {$a->grade}.

'; $string['displayscorewithoutessays'] = 'Your score is {$a->score} (out of {$a->grade}).'; +$string['duplicatepagenamed'] = 'Duplicate page: {$a}'; $string['edit'] = 'Edit'; $string['editingquestionpage'] = 'Editing {$a} question page'; $string['editlessonsettings'] = 'Edit lesson settings'; diff --git a/mod/lesson/lesson.php b/mod/lesson/lesson.php index 390c29c00fb..4256d3994ad 100644 --- a/mod/lesson/lesson.php +++ b/mod/lesson/lesson.php @@ -23,6 +23,7 @@ * delete * move * moveit + * duplicate * @package mod_lesson * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later @@ -129,6 +130,10 @@ switch ($action) { $lesson->resort_pages($pageid, $after); redirect("$CFG->wwwroot/mod/lesson/edit.php?id=$cm->id"); break; + case 'duplicate': + $lesson->duplicate_page($pageid); + redirect(new moodle_url('/mod/lesson/edit.php', array('id' => $cm->id))); + break; default: print_error('unknowaction'); break; diff --git a/mod/lesson/locallib.php b/mod/lesson/locallib.php index 6b84bf42900..80e2547cb5b 100644 --- a/mod/lesson/locallib.php +++ b/mod/lesson/locallib.php @@ -1662,6 +1662,93 @@ class lesson extends lesson_base { return $this->pages; } + /** + * Duplicate the lesson page. + * + * @param int $pageid Page ID of the page to duplicate. + * @return void. + */ + public function duplicate_page($pageid) { + global $PAGE; + $cm = get_coursemodule_from_instance('lesson', $this->properties->id, $this->properties->course); + $context = context_module::instance($cm->id); + // Load the page. + $page = $this->load_page($pageid); + $properties = $page->properties(); + // The create method checks to see if these properties are set and if not sets them to zero, hence the unsetting here. + if (!$properties->qoption) { + unset($properties->qoption); + } + if (!$properties->layout) { + unset($properties->layout); + } + if (!$properties->display) { + unset($properties->display); + } + + $properties->pageid = $pageid; + // Add text and format into the format required to create a new page. + $properties->contents_editor = array( + 'text' => $properties->contents, + 'format' => $properties->contentsformat + ); + $answers = $page->get_answers(); + // Answers need to be added to $properties. + $i = 0; + $answerids = array(); + foreach ($answers as $answer) { + // Needs to be rearranged to work with the create function. + $properties->answer_editor[$i] = array( + 'text' => $answer->answer, + 'format' => $answer->answerformat + ); + + $properties->response_editor[$i] = array( + 'text' => $answer->response, + 'format' => $answer->responseformat + ); + $answerids[] = $answer->id; + + $properties->jumpto[$i] = $answer->jumpto; + $properties->score[$i] = $answer->score; + + $i++; + } + // Create the duplicate page. + $newlessonpage = lesson_page::create($properties, $this, $context, $PAGE->course->maxbytes); + $newanswers = $newlessonpage->get_answers(); + // Copy over the file areas as well. + $this->copy_page_files('page_contents', $pageid, $newlessonpage->id, $context->id); + $j = 0; + foreach ($newanswers as $answer) { + if (strpos($answer->answer, '@@PLUGINFILE@@') !== false) { + $this->copy_page_files('page_answers', $answerids[$j], $answer->id, $context->id); + } + if (strpos($answer->response, '@@PLUGINFILE@@') !== false) { + $this->copy_page_files('page_responses', $answerids[$j], $answer->id, $context->id); + } + $j++; + } + } + + /** + * Copy the files from one page to another. + * + * @param string $filearea Area that the files are stored. + * @param int $itemid Item ID. + * @param int $newitemid The item ID for the new page. + * @param int $contextid Context ID for this page. + * @return void. + */ + protected function copy_page_files($filearea, $itemid, $newitemid, $contextid) { + $fs = get_file_storage(); + $files = $fs->get_area_files($contextid, 'mod_lesson', $filearea, $itemid); + foreach ($files as $file) { + $fieldupdates = array('itemid' => $newitemid); + $fs->create_file_from_storedfile($fieldupdates, $file); + } + } + /** * Determines if a jumpto value is correct or not. * diff --git a/mod/lesson/pix/e/copy.png b/mod/lesson/pix/e/copy.png new file mode 100644 index 00000000000..d3f36c3dd82 Binary files /dev/null and b/mod/lesson/pix/e/copy.png differ diff --git a/mod/lesson/pix/e/copy.svg b/mod/lesson/pix/e/copy.svg new file mode 100644 index 00000000000..6950ca925e4 --- /dev/null +++ b/mod/lesson/pix/e/copy.svg @@ -0,0 +1,3 @@ + +]> \ No newline at end of file diff --git a/mod/lesson/renderer.php b/mod/lesson/renderer.php index 56e071af212..2f7fc2264c6 100644 --- a/mod/lesson/renderer.php +++ b/mod/lesson/renderer.php @@ -414,6 +414,13 @@ class mod_lesson_renderer extends plugin_renderer_base { $img = html_writer::img($this->output->pix_url('t/edit'), $label, array('class' => 'iconsmall')); $actions[] = html_writer::link($url, $img, array('title' => $label)); + // Duplicate action. + $url = new moodle_url('/mod/lesson/lesson.php', array('id' => $this->page->cm->id, 'pageid' => $page->id, + 'action' => 'duplicate', 'sesskey' => sesskey())); + $label = get_string('duplicatepagenamed', 'lesson', format_string($page->title)); + $img = html_writer::img($this->output->pix_url('e/copy', 'mod_lesson'), $label, array('class' => 'iconsmall')); + $actions[] = html_writer::link($url, $img, array('title' => $label)); + $url = new moodle_url('/mod/lesson/view.php', array('id' => $this->page->cm->id, 'pageid' => $page->id)); $label = get_string('previewpagenamed', 'lesson', format_string($page->title)); $img = html_writer::img($this->output->pix_url('t/preview'), $label, array('class' => 'iconsmall')); diff --git a/mod/lesson/tests/behat/duplicate_lesson_page.feature b/mod/lesson/tests/behat/duplicate_lesson_page.feature new file mode 100644 index 00000000000..5f7774c3158 --- /dev/null +++ b/mod/lesson/tests/behat/duplicate_lesson_page.feature @@ -0,0 +1,132 @@ +@mod @mod_lesson +Feature: In a lesson activity, a teacher can duplicate a lesson page + In order to duplicate a lesson page + As a teacher + I need to add content to a lesson + + Background: + Given the following "users" exist: + | username | firstname | lastname | email | + | teacher1 | Teacher | 1 | teacher1@example.com | + | student1 | Student | 1 | student1@example.com | + And the following "courses" exist: + | fullname | shortname | category | + | Course 1 | C1 | 0 | + And the following "course enrolments" exist: + | user | course | role | + | teacher1 | C1 | editingteacher | + | student1 | C1 | student | + And I log in as "teacher1" + And I follow "Manage private files" + And I upload "mod/lesson/tests/fixtures/moodle_logo.jpg" file to "Files" filemanager + And I click on "Save changes" "button" + When I am on homepage + And I follow "Course 1" + And I turn editing mode on + And I add a "Lesson" to section "1" and I fill the form with: + | Name | Test lesson name | + | Description | Test lesson description | + + @javascript + Scenario: Duplicate content page with an image. + Given I follow "Test lesson name" + And I follow "Add a content page" + And I set the following fields to these values: + | Page title | First page name | + | Page contents | First page contents | + | id_answer_editor_0 | Next page | + | id_jumpto_0 | Next page | + | id_answer_editor_1 | Previous page | + | id_jumpto_1 | Previous page | + # Atto needs focus to add image, select empty p tag to do so. + And I select the text in the "id_contents_editor" Atto editor + And I click on "Image" "button" in the "#fitem_id_contents_editor" "css_element" + And I click on "Browse repositories..." "button" + And I click on "Private files" "link" + And I click on "moodle_logo.jpg" "link" + And I click on "Select this file" "button" + And I set the field "Describe this image for someone who cannot see it" to "It's the logo" + And I click on "Save image" "button" + And I press "Save page" + And I follow "Duplicate page: First page name" + And I should see "Inserted page: First page name" + And I follow "Update page: First page name" + And I set the field "Page title" to "Introduction page" + And I press "Save page" + When I follow "Update page: First page name" + And I should see "First page name" + Then "//*[contains(@id, 'id_contents_editor')]//img[contains(@src, 'moodle_logo.jpg')]" "xpath_element" should exist + + @javascript + Scenario: Duplicate question page with image in answer. + Given I follow "Test lesson name" + And I follow "Add a question page" + And I set the field "Select a question type" to "True/false" + And I press "Add a question page" + And I set the following fields to these values: + | Page title | True false with an image in the answer | + | Page contents | Select the picture | + | id_answer_editor_0 | To be replaced | + | id_response_editor_0 | Correct answer | + | id_jumpto_0 | End of lesson | + | id_score_0 | 1 | + | id_answer_editor_1 | 1 | + | id_response_editor_1 | Incorrect answer | + | id_jumpto_1 | This page | + | id_score_1 | 0 | + # Atto needs focus to add image, select empty p tag to do so. + And I select the text in the "id_answer_editor_0" Atto editor + And I click on "Image" "button" in the "#fitem_id_answer_editor_0" "css_element" + And I click on "Browse repositories..." "button" + And I click on "Private files" "link" + And I click on "moodle_logo.jpg" "link" + And I click on "Select this file" "button" + And I set the field "Describe this image for someone who cannot see it" to "It's the logo" + And I click on "Save image" "button" + And I press "Save page" + And I follow "Duplicate page: True false with an image in the answer" + And I should see "Inserted page: True false with an image in the answer" + And I follow "Update page: True false with an image in the answer" + And I set the field "Page title" to "First true false" + And I press "Save page" + When I follow "Update page: True false with an image in the answer" + And I should see "True false with an image in the answer" + And I should see "Select the picture" + Then "//*[contains(@id, 'id_answer_editor_0')]//img[contains(@src, 'moodle_logo.jpg')]" "xpath_element" should exist + +@javascript + Scenario: Duplicate question page with image in feedback. + Given I follow "Test lesson name" + And I follow "Add a question page" + And I set the field "Select a question type" to "True/false" + And I press "Add a question page" + And I set the following fields to these values: + | Page title | True false with an image in the feedback | + | Page contents | Select the picture | + | id_answer_editor_0 | To be replaced | + | id_response_editor_0 | Correct answer | + | id_jumpto_0 | End of lesson | + | id_score_0 | 1 | + | id_answer_editor_1 | 1 | + | id_response_editor_1 | Incorrect answer | + | id_jumpto_1 | This page | + | id_score_1 | 0 | + # Atto needs focus to add image, select empty p tag to do so. + And I select the text in the "id_response_editor_0" Atto editor + And I click on "Image" "button" in the "#fitem_id_response_editor_0" "css_element" + And I click on "Browse repositories..." "button" + And I click on "Private files" "link" + And I click on "moodle_logo.jpg" "link" + And I click on "Select this file" "button" + And I set the field "Describe this image for someone who cannot see it" to "It's the logo" + And I click on "Save image" "button" + And I press "Save page" + And I follow "Duplicate page: True false with an image in the feedback" + And I should see "Inserted page: True false with an image in the feedback" + And I follow "Update page: True false with an image in the feedback" + And I set the field "Page title" to "First true false" + And I press "Save page" + When I follow "Update page: True false with an image in the feedback" + And I should see "True false with an image in the feedback" + And I should see "Select the picture" + Then "//*[contains(@id, 'id_response_editor_0')]//img[contains(@src, 'moodle_logo.jpg')]" "xpath_element" should exist diff --git a/mod/lesson/tests/locallib_test.php b/mod/lesson/tests/locallib_test.php new file mode 100644 index 00000000000..20fe08f9e52 --- /dev/null +++ b/mod/lesson/tests/locallib_test.php @@ -0,0 +1,79 @@ +. + +/** + * locallib tests. + * + * @package mod_lesson + * @category test + * @copyright 2016 Adrian Greeve + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; + +require_once($CFG->dirroot.'/mod/lesson/locallib.php'); + +/** + * locallib testcase. + * + * @package mod_lesson + * @category test + * @copyright 2016 Adrian Greeve + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class mod_lesson_locallib_testcase extends advanced_testcase { + + /** + * Test duplicating a lesson page element. + */ + public function test_duplicate_page() { + global $DB; + + $this->resetAfterTest(); + $this->setAdminUser(); + $course = $this->getDataGenerator()->create_course(); + $lessonmodule = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id)); + // Convert to a lesson object. + $lesson = new lesson($lessonmodule); + + // Set up a generator to create content. + $generator = $this->getDataGenerator()->get_plugin_generator('mod_lesson'); + $tfrecord = $generator->create_question_truefalse($lesson); + $lesson->duplicate_page($tfrecord->id); + + // Lesson pages. + $records = $DB->get_records('lesson_pages', array('qtype' => 2)); + $sameelements = array('lessonid', 'qtype', 'qoption', 'layout', 'display', 'title', 'contents', 'contentsformat'); + $baserecord = array_shift($records); + $secondrecord = array_shift($records); + foreach ($sameelements as $element) { + $this->assertEquals($baserecord->$element, $secondrecord->$element); + } + // Need lesson answers as well. + $baserecordanswers = array_values($DB->get_records('lesson_answers', array('pageid' => $baserecord->id))); + $secondrecordanswers = array_values($DB->get_records('lesson_answers', array('pageid' => $secondrecord->id))); + $sameanswerelements = array('lessonid', 'jumpto', 'grade', 'score', 'flags', 'answer', 'answerformat', 'response', + 'responseformat'); + foreach ($baserecordanswers as $key => $baseanswer) { + foreach ($sameanswerelements as $element) { + $this->assertEquals($baseanswer->$element, $secondrecordanswers[$key]->$element); + } + } + } +}