Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
/**
18
 * Steps definitions related with the forum activity.
19
 *
20
 * @package    mod_forum
21
 * @category   test
22
 * @copyright  2013 David Monllaó
23
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24
 */
25
 
26
// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
27
 
28
require_once(__DIR__ . '/../../../../lib/behat/behat_base.php');
29
 
30
use Behat\Gherkin\Node\TableNode;
31
 
32
/**
33
 * Forum-related steps definitions.
34
 *
35
 * @package    mod_forum
36
 * @category   test
37
 * @copyright  2013 David Monllaó
38
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
39
 */
40
class behat_mod_forum extends behat_base {
41
    /**
42
     * Reset forum caches between tests.
43
     *
44
     * @BeforeScenario @mod_forum
45
     */
46
    public function reset_forum_caches(): void {
47
        \mod_forum\subscriptions::reset_discussion_cache();
48
        \mod_forum\subscriptions::reset_forum_cache();
49
    }
50
 
51
    /**
52
     * Adds a topic to the forum specified by it's name. Useful for the Announcements and blog-style forums.
53
     *
54
     * @Given /^I add a new topic to "(?P<forum_name_string>(?:[^"]|\\")*)" forum with:$/
55
     * @param string $forumname
56
     * @param TableNode $table
57
     */
58
    public function i_add_a_new_topic_to_forum_with($forumname, TableNode $table) {
59
        $this->add_new_discussion($forumname, $table, get_string('addanewdiscussion', 'forum'));
60
    }
61
 
62
    /**
63
     * Adds a Q&A discussion to the Q&A-type forum specified by it's name with the provided table data.
64
     *
65
     * @Given /^I add a new question to "(?P<forum_name_string>(?:[^"]|\\")*)" forum with:$/
66
     * @param string $forumname
67
     * @param TableNode $table
68
     */
69
    public function i_add_a_new_question_to_forum_with($forumname, TableNode $table) {
70
        $this->add_new_discussion($forumname, $table, get_string('addanewdiscussion', 'forum'));
71
    }
72
 
73
    /**
74
     * Adds a discussion to the forum specified by it's name with the provided table data (usually Subject and Message). The step begins from the forum's course page.
75
     *
76
     * @Given /^I add a new discussion to "(?P<forum_name_string>(?:[^"]|\\")*)" forum with:$/
77
     * @param string $forumname
78
     * @param TableNode $table
79
     */
80
    public function i_add_a_forum_discussion_to_forum_with($forumname, TableNode $table) {
81
        $this->add_new_discussion($forumname, $table, get_string('addanewdiscussion', 'forum'));
82
    }
83
 
84
    /**
85
     * Adds a discussion to the forum specified by it's name with the provided table data (usually Subject and Message).
86
     * The step begins from the forum's course page.
87
     *
88
     * @Given /^I add a new discussion to "(?P<forum_name_string>(?:[^"]|\\")*)" forum inline with:$/
89
     * @param string $forumname
90
     * @param TableNode $table
91
     */
92
    public function i_add_a_forum_discussion_to_forum_inline_with($forumname, TableNode $table) {
93
        $this->add_new_discussion_inline($forumname, $table, get_string('addanewdiscussion', 'forum'));
94
    }
95
 
96
    /**
97
     * Adds a reply to the specified post of the specified forum. The step begins from the forum's page or from the forum's course page.
98
     *
99
     * @Given /^I reply "(?P<post_subject_string>(?:[^"]|\\")*)" post from "(?P<forum_name_string>(?:[^"]|\\")*)" forum with:$/
100
     * @param string $postname The subject of the post
101
     * @param string $forumname The forum name
102
     * @param TableNode $table
103
     */
104
    public function i_reply_post_from_forum_with($postsubject, $forumname, TableNode $table) {
105
 
106
        // Navigate to forum.
107
        $this->goto_main_post_reply($postsubject);
108
 
109
        // Fill form and post.
110
        $this->execute('behat_forms::i_set_the_following_fields_to_these_values', $table);
111
 
112
        $this->execute('behat_forms::press_button', get_string('posttoforum', 'forum'));
113
        $this->execute('behat_general::i_wait_to_be_redirected');
114
    }
115
 
116
    /**
117
     * Inpage Reply - adds a reply to the specified post of the specified forum. The step begins from the forum's page or from the forum's course page.
118
     *
119
     * @Given /^I reply "(?P<post_subject_string>(?:[^"]|\\")*)" post from "(?P<forum_name_string>(?:[^"]|\\")*)" forum using an inpage reply with:$/
120
     * @param string $postsubject The subject of the post
121
     * @param string $forumname The forum name
122
     * @param TableNode $table
123
     */
124
    public function i_reply_post_from_forum_using_an_inpage_reply_with($postsubject, $forumname, TableNode $table) {
125
        // Navigate to forum.
126
        $this->execute('behat_navigation::i_am_on_page_instance', [$this->escape($forumname), 'forum activity']);
127
        $this->execute('behat_general::click_link', $this->escape($postsubject));
128
        $this->execute('behat_general::click_link', get_string('reply', 'forum'));
129
 
130
        // Fill form and post.
131
        $this->execute('behat_forms::i_set_the_following_fields_to_these_values', $table);
132
 
133
        $this->execute('behat_forms::press_button', get_string('posttoforum', 'mod_forum'));
134
    }
135
 
136
    /**
137
     * Navigates to a particular discussion page
138
     *
139
     * @Given /^I navigate to post "(?P<post_subject_string>(?:[^"]|\\")*)" in "(?P<forum_name_string>(?:[^"]|\\")*)" forum$/
140
     * @param string $postsubject The subject of the post
141
     * @param string $forumname The forum name
142
     */
143
    public function i_navigate_to_post_in_forum($postsubject, $forumname) {
144
        // Navigate to forum discussion.
145
        $this->execute('behat_navigation::i_am_on_page_instance', [$this->escape($forumname), 'forum activity']);
146
        $this->execute('behat_general::click_link', $this->escape($postsubject));
147
    }
148
 
149
    /**
150
     * Opens up the action menu for the discussion
151
     *
152
     * @Given /^I click on "(?P<post_subject_string>(?:[^"]|\\")*)" action menu$/
153
     * @param string $discussion The subject of the discussion
154
     */
155
    public function i_click_on_action_menu($discussion) {
156
        $this->execute('behat_general::i_click_on_in_the', [
157
            "[data-container='discussion-tools'] [data-toggle='dropdown']", "css_element",
158
            "//tr[contains(concat(' ', normalize-space(@class), ' '), ' discussion ') and contains(.,'$discussion')]",
159
            "xpath_element"
160
        ]);
161
    }
162
 
163
    /**
164
     * Creates new discussions within forums of a given course.
165
     *
166
     * @Given the following forum discussions exist in course :coursename:
167
     * @param string $coursename The full name of the course where the forums exist.
168
     * @param TableNode $discussionsdata The discussion posts to be created.
169
     */
170
    public function the_following_forum_discussions_exist(string $coursename, TableNode $discussionsdata) {
171
        global $DB;
172
 
173
        $courseid = $this->get_course_id($coursename);
174
        $forumgenerator = behat_util::get_data_generator()->get_plugin_generator('mod_forum');
175
 
176
        // Add the discussions to the relevant forum.
177
        foreach ($discussionsdata->getHash() as $discussioninfo) {
178
            $discussioninfo['course'] = $courseid;
179
            $discussioninfo['forum'] = $this->get_forum_id($courseid, $discussioninfo['forum']);
180
            $discussioninfo['userid'] = $this->get_user_id($discussioninfo['user']);
181
 
182
            // Prepare data for any attachments.
183
            if (!empty($discussioninfo['attachments']) || !empty($discussioninfo['inlineattachments'])) {
184
                $discussioninfo['attachment'] = 1;
185
                $cm = get_coursemodule_from_instance('forum', $discussioninfo['forum']);
186
            }
187
 
188
            // Prepare data for groups if needed.
189
            if (!empty($discussioninfo['group'])) {
190
                $discussioninfo['groupid'] = $this->get_group_id($courseid, $discussioninfo['group']);
191
                unset($discussioninfo['group']);
192
            }
193
 
194
            // Create the discussion post.
195
            $discussion = $forumgenerator->create_discussion($discussioninfo);
196
            $postid = $DB->get_field('forum_posts', 'id', ['discussion' => $discussion->id]);
197
 
198
            // Override the creation and modified timestamps as required.
199
            if (!empty($discussioninfo['created']) || !empty($discussioninfo['modified'])) {
200
                $discussiondata = [
201
                    'id' => $discussion->id,
202
                    'timemodified' => empty($discussioninfo['modified']) ? $discussioninfo['created'] : $discussioninfo['modified'],
203
                ];
204
 
205
                $DB->update_record('forum_discussions', $discussiondata);
206
 
207
                $postdata = [
208
                    'id' => $postid,
209
                    'modified' => empty($discussioninfo['modified']) ? $discussioninfo['created'] : $discussioninfo['modified'],
210
                ];
211
 
212
                if (!empty($discussioninfo['created'])) {
213
                    $postdata['created'] = $discussioninfo['created'];
214
                }
215
 
216
                $DB->update_record('forum_posts', $postdata);
217
            }
218
 
219
            // Create attachments to the discussion post if required.
220
            if (!empty($discussioninfo['attachments'])) {
221
                $attachments = array_map('trim', explode(',', $discussioninfo['attachments']));
222
                $this->create_post_attachments($postid, $discussioninfo['userid'], $attachments, $cm, 'attachment');
223
            }
224
 
225
            // Create inline attachments to the discussion post if required.
226
            if (!empty($discussioninfo['inlineattachments'])) {
227
                $inlineattachments = array_map('trim', explode(',', $discussioninfo['inlineattachments']));
228
                $this->create_post_attachments($postid, $discussioninfo['userid'], $inlineattachments, $cm, 'post');
229
            }
230
        }
231
    }
232
 
233
    /**
234
     * Creates replies to discussions within forums of a given course.
235
     *
236
     * @Given the following forum replies exist in course :coursename:
237
     * @param string $coursename The full name of the course where the forums exist.
238
     * @param TableNode $repliesdata The reply posts to be created.
239
     */
240
    public function the_following_forum_replies_exist(string $coursename, TableNode $repliesdata) {
241
        global $DB;
242
 
243
        $courseid = $this->get_course_id($coursename);
244
        $forumgenerator = behat_util::get_data_generator()->get_plugin_generator('mod_forum');
245
 
246
        // Add the replies to the relevant discussions.
247
        foreach ($repliesdata->getHash() as $replyinfo) {
248
            $replyinfo['course'] = $courseid;
249
            $replyinfo['forum'] = $this->get_forum_id($courseid, $replyinfo['forum']);
250
            $replyinfo['userid'] = $this->get_user_id($replyinfo['user']);
251
 
252
            [
253
                'discussionid' => $replyinfo['discussion'],
254
                'parentid' => $replyinfo['parent'],
255
            ] = $this->get_base_discussion($replyinfo['forum'], $replyinfo['discussion']);
256
 
257
            // Prepare data for any attachments.
258
            if (!empty($replyinfo['attachments']) || !empty($replyinfo['inlineattachments'])) {
259
                $replyinfo['attachment'] = 1;
260
                $cm = get_coursemodule_from_instance('forum', $replyinfo['forum']);
261
            }
262
 
263
            // Get the user id of the user to whom the reply is private.
264
            if (!empty($replyinfo['privatereplyto'])) {
265
                $replyinfo['privatereplyto'] = $this->get_user_id($replyinfo['privatereplyto']);
266
            }
267
 
268
            // Create the reply post.
269
            $reply = $forumgenerator->create_post($replyinfo);
270
 
271
            // Create attachments to the post if required.
272
            if (!empty($replyinfo['attachments'])) {
273
                $attachments = array_map('trim', explode(',', $replyinfo['attachments']));
274
                $this->create_post_attachments($reply->id, $replyinfo['userid'], $attachments, $cm, 'attachment');
275
            }
276
 
277
            // Create inline attachments to the post if required.
278
            if (!empty($replyinfo['inlineattachments'])) {
279
                $inlineattachments = array_map('trim', explode(',', $replyinfo['inlineattachments']));
280
                $this->create_post_attachments($reply->id, $replyinfo['userid'], $inlineattachments, $cm, 'post');
281
            }
282
        }
283
    }
284
 
285
    /**
286
     * Checks if the user can subscribe to the forum.
287
     *
288
     * @Given /^I can subscribe to this forum$/
289
     */
290
    public function i_can_subscribe_to_this_forum() {
291
        $this->execute('behat_general::assert_page_contains_text', [get_string('subscribe', 'mod_forum')]);
292
    }
293
 
294
    /**
295
     * Checks if the user can unsubscribe from the forum.
296
     *
297
     * @Given /^I can unsubscribe from this forum$/
298
     */
299
    public function i_can_unsubscribe_from_this_forum() {
300
        $this->execute('behat_general::assert_page_contains_text', [get_string('unsubscribe', 'mod_forum')]);
301
    }
302
 
303
    /**
304
     * Subscribes to the forum.
305
     *
306
     * @Given /^I subscribe to this forum$/
307
     */
308
    public function i_subscribe_to_this_forum() {
309
        $this->execute('behat_general::click_link', [get_string('subscribe', 'mod_forum')]);
310
    }
311
 
312
    /**
313
     * Unsubscribes from the forum.
314
     *
315
     * @Given /^I unsubscribe from this forum$/
316
     */
317
    public function i_unsubscribe_from_this_forum() {
318
        $this->execute('behat_general::click_link', [get_string('unsubscribe', 'mod_forum')]);
319
    }
320
 
321
    /**
322
     * Fetch user ID from its username.
323
     *
324
     * @param string $username The username.
325
     * @return int The user ID.
326
     * @throws Exception
327
     */
328
    protected function get_user_id($username) {
329
        global $DB;
330
 
331
        if (!$userid = $DB->get_field('user', 'id', ['username' => $username])) {
332
            throw new Exception("A user with username '{$username}' does not exist");
333
        }
334
        return $userid;
335
    }
336
 
337
    /**
338
     * Fetch course ID using course name.
339
     *
340
     * @param string $coursename The name of the course.
341
     * @return int The course ID.
342
     * @throws Exception
343
     */
344
    protected function get_course_id(string $coursename): int {
345
        global $DB;
346
 
347
        if (!$courseid = $DB->get_field('course', 'id', ['fullname' => $coursename])) {
348
            throw new Exception("A course with name '{$coursename}' does not exist");
349
        }
350
 
351
        return $courseid;
352
    }
353
 
354
    /**
355
     * Fetch forum ID using forum name.
356
     *
357
     * @param int $courseid The course ID the forum exists within.
358
     * @param string $forumname The name of the forum.
359
     * @return int The forum ID.
360
     * @throws Exception
361
     */
362
    protected function get_forum_id(int $courseid, string $forumname): int {
363
        global $DB;
364
 
365
        $conditions = [
366
            'course' => $courseid,
367
            'name' => $forumname,
368
        ];
369
 
370
        if (!$forumid = $DB->get_field('forum', 'id', $conditions)) {
371
            throw new Exception("A forum with name '{$forumname}' does not exist in the provided course");
372
        }
373
 
374
        return $forumid;
375
    }
376
 
377
    /**
378
     * Fetch Group ID using group name.
379
     *
380
     * @param int $courseid The course ID the forum exists within.
381
     * @param string $groupname The short name of the group.
382
     * @return int The group ID.
383
     * @throws Exception
384
     */
385
    protected function get_group_id(int $courseid, string $groupname): int {
386
        global $DB;
387
 
388
        if ($groupname === 'All participants') {
389
            return -1;
390
        }
391
 
392
        $conditions = [
393
            'courseid' => $courseid,
394
            'idnumber' => $groupname,
395
        ];
396
 
397
        if (!$groupid = $DB->get_field('groups', 'id', $conditions)) {
398
            throw new Exception("A group with name '{$groupname}' does not exist in the provided course");
399
        }
400
 
401
        return $groupid;
402
    }
403
 
404
    /**
405
     * Fetch discussion ID and first post ID by discussion name.
406
     *
407
     * @param int $forumid The forum ID where the discussion resides.
408
     * @param string $name The name of the discussion.
409
     * @return array The discussion ID and first post ID.
410
     * @throws dml_exception If the discussion name is not unique within the forum (or doesn't exist).
411
     */
412
    protected function get_base_discussion(int $forumid, string $name): array {
413
        global $DB;
414
 
415
        $conditions = [
416
            'name' => $name,
417
            'forum' => $forumid,
418
        ];
419
 
420
        $result = $DB->get_record("forum_discussions", $conditions, 'id, firstpost', MUST_EXIST);
421
 
422
        return [
423
            'discussionid' => $result->id,
424
            'parentid' => $result->firstpost,
425
        ];
426
    }
427
 
428
    /**
429
     * Create one or more attached or inline attachments to a forum post.
430
     *
431
     * @param int $postid The ID of the forum post.
432
     * @param int $userid The user ID creating the attachment.
433
     * @param array $attachmentnames Names of all attachments to be created.
434
     * @param stdClass $cm The context module of the forum.
435
     * @param string $filearea The file area being written to, eg 'attachment' or 'post' (inline).
436
     */
437
    protected function create_post_attachments(int $postid, int $userid, array $attachmentnames, stdClass $cm, string $filearea): void {
438
        $filestorage = get_file_storage();
439
 
440
        foreach ($attachmentnames as $attachmentname) {
441
            $filestorage->create_file_from_string(
442
                [
443
                    'contextid' => context_module::instance($cm->id)->id,
444
                    'component' => 'mod_forum',
445
                    'filearea'  => $filearea,
446
                    'itemid'    => $postid,
447
                    'filepath'  => '/',
448
                    'filename'  => $attachmentname,
449
                    'userid'    => $userid,
450
                ],
451
                "File content {$attachmentname}"
452
            );
453
        }
454
    }
455
 
456
    /**
457
     * Returns the steps list to add a new discussion to a forum.
458
     *
459
     * Abstracts add a new topic and add a new discussion, as depending
460
     * on the forum type the button string changes.
461
     *
462
     * @param string $forumname
463
     * @param TableNode $table
464
     * @param string $buttonstr
465
     */
466
    protected function add_new_discussion($forumname, TableNode $table, $buttonstr) {
467
        // Navigate to forum.
468
        $this->execute('behat_navigation::i_am_on_page_instance', [$this->escape($forumname), 'forum activity']);
469
        $this->execute('behat_general::click_link', $buttonstr);
470
        $this->execute('behat_forms::press_button', get_string('showadvancededitor'));
471
 
472
        $this->fill_new_discussion_form($table);
473
    }
474
 
475
    /**
476
     * Returns the steps list to add a new discussion to a forum inline.
477
     *
478
     * Abstracts add a new topic and add a new discussion, as depending
479
     * on the forum type the button string changes.
480
     *
481
     * @param string $forumname
482
     * @param TableNode $table
483
     * @param string $buttonstr
484
     */
485
    protected function add_new_discussion_inline($forumname, TableNode $table, $buttonstr) {
486
        // Navigate to forum.
487
        $this->execute('behat_navigation::i_am_on_page_instance', [$this->escape($forumname), 'forum activity']);
488
        $this->execute('behat_general::click_link', $buttonstr);
489
        $this->fill_new_discussion_form($table);
490
    }
491
 
492
    /**
493
     * Fill in the forum's post form and submit. It assumes you've already navigated and enabled the form for view.
494
     *
495
     * @param TableNode $table
496
     * @throws coding_exception
497
     */
498
    protected function fill_new_discussion_form(TableNode $table) {
499
        // Fill form and post.
500
        $this->execute('behat_forms::i_set_the_following_fields_to_these_values', $table);
501
        $this->execute('behat_forms::press_button', get_string('posttoforum', 'forum'));
502
        $this->execute('behat_general::i_wait_to_be_redirected');
503
    }
504
 
505
    /**
506
     * Go to the default reply to post page.
507
     * This is used instead of navigating through 4-5 different steps and to solve issues where JS would be required to click
508
     * on the advanced button
509
     *
510
     * @param $postsubject
511
     * @throws coding_exception
512
     * @throws dml_exception
513
     * @throws moodle_exception
514
     */
515
    protected function goto_main_post_reply($postsubject) {
516
        global $DB;
517
        $post = $DB->get_record("forum_posts", array("subject" => $postsubject), 'id', MUST_EXIST);
518
        $url = new moodle_url('/mod/forum/post.php', ['reply' => $post->id]);
519
        $this->execute('behat_general::i_visit', [$url]);
520
    }
521
}