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
 * Post exporter class.
19
 *
20
 * @package    mod_forum
21
 * @copyright  2019 Ryan Wyllie <ryan@moodle.com>
22
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
 
25
namespace mod_forum\local\exporters;
26
 
27
defined('MOODLE_INTERNAL') || die();
28
 
29
use mod_forum\local\entities\post as post_entity;
30
use mod_forum\local\entities\discussion as discussion_entity;
31
use mod_forum\local\exporters\author as author_exporter;
32
use mod_forum\local\factories\exporter as exporter_factory;
33
use core\external\exporter;
34
use core_files\external\stored_file_exporter;
35
use context;
36
use core_tag_tag;
37
use renderer_base;
38
use stdClass;
39
 
40
require_once($CFG->dirroot . '/mod/forum/lib.php');
41
 
42
/**
43
 * Post exporter class.
44
 *
45
 * @copyright  2019 Ryan Wyllie <ryan@moodle.com>
46
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
47
 */
48
class post extends exporter {
49
    /** @var post_entity $post The post to export */
50
    private $post;
51
 
52
    /**
53
     * Constructor.
54
     *
55
     * @param post_entity $post The post to export
56
     * @param array $related List of related data
57
     */
58
    public function __construct(post_entity $post, array $related = []) {
59
        $this->post = $post;
60
        return parent::__construct([], $related);
61
    }
62
 
63
    /**
64
     * Return the list of additional properties.
65
     *
66
     * @return array
67
     */
68
    protected static function define_other_properties() {
69
        $attachmentdefinition = stored_file_exporter::read_properties_definition();
70
        $attachmentdefinition['urls'] = [
71
            'type' => [
72
                'export' => [
73
                    'type' => PARAM_URL,
74
                    'description' => 'The URL used to export the attachment',
75
                    'optional' => true,
76
                    'default' => null,
77
                    'null' => NULL_ALLOWED
78
                ]
79
            ]
80
        ];
81
        $attachmentdefinition['html'] = [
82
            'type' => [
83
                'plagiarism' => [
84
                    'type' => PARAM_RAW,
85
                    'description' => 'The HTML source for the Plagiarism Response',
86
                    'optional' => true,
87
                    'default' => null,
88
                    'null' => NULL_ALLOWED
89
                ],
90
            ]
91
        ];
92
 
93
        return [
94
            'id' => ['type' => PARAM_INT],
95
            'subject' => ['type' => PARAM_TEXT],
96
            'replysubject' => ['type' => PARAM_TEXT],
97
            'message' => ['type' => PARAM_RAW],
98
            'messageformat' => ['type' => PARAM_INT],
99
            'author' => ['type' => author_exporter::read_properties_definition()],
100
            'discussionid' => ['type' => PARAM_INT],
101
            'hasparent' => ['type' => PARAM_BOOL],
102
            'parentid' => [
103
                'type' => PARAM_INT,
104
                'optional' => true,
105
                'default' => null,
106
                'null' => NULL_ALLOWED
107
            ],
108
            'timecreated' => [
109
                'type' => PARAM_INT,
110
                'default' => null,
111
                'null' => NULL_ALLOWED
112
            ],
113
            'timemodified' => [
114
                'type' => PARAM_INT,
115
                'default' => null,
116
                'null' => NULL_ALLOWED
117
            ],
118
            'unread' => [
119
                'type' => PARAM_BOOL,
120
                'optional' => true,
121
                'default' => null,
122
                'null' => NULL_ALLOWED
123
            ],
124
            'isdeleted' => ['type' => PARAM_BOOL],
125
            'isprivatereply' => ['type' => PARAM_BOOL],
126
            'haswordcount' => ['type' => PARAM_BOOL],
127
            'wordcount' => [
128
                'type' => PARAM_INT,
129
                'optional' => true,
130
                'default' => null,
131
                'null' => NULL_ALLOWED
132
            ],
133
            'charcount' => [
134
                'type' => PARAM_INT,
135
                'optional' => true,
136
                'default' => null,
137
                'null' => NULL_ALLOWED
138
            ],
139
            'capabilities' => [
140
                'type' => [
141
                    'view' => [
142
                        'type' => PARAM_BOOL,
143
                        'null' => NULL_ALLOWED,
144
                        'description' => 'Whether the user can view the post',
145
                    ],
146
                    'edit' => [
147
                        'type' => PARAM_BOOL,
148
                        'null' => NULL_ALLOWED,
149
                        'description' => 'Whether the user can edit the post',
150
                    ],
151
                    'delete' => [
152
                        'type' => PARAM_BOOL,
153
                        'null' => NULL_ALLOWED,
154
                        'description' => 'Whether the user can delete the post',
155
                    ],
156
                    'split' => [
157
                        'type' => PARAM_BOOL,
158
                        'null' => NULL_ALLOWED,
159
                        'description' => 'Whether the user can split the post',
160
                    ],
161
                    'reply' => [
162
                        'type' => PARAM_BOOL,
163
                        'null' => NULL_ALLOWED,
164
                        'description' => 'Whether the user can reply to the post',
165
                    ],
166
                    'selfenrol' => [
167
                        'type' => PARAM_BOOL,
168
                        'null' => NULL_ALLOWED,
169
                        'description' => 'Whether the user can self enrol into the course',
170
                    ],
171
                    'export' => [
172
                        'type' => PARAM_BOOL,
173
                        'null' => NULL_ALLOWED,
174
                        'description' => 'Whether the user can export the post',
175
                    ],
176
                    'controlreadstatus' => [
177
                        'type' => PARAM_BOOL,
178
                        'null' => NULL_ALLOWED,
179
                        'description' => 'Whether the user can control the read status of the post',
180
                    ],
181
                    'canreplyprivately' => [
182
                        'type' => PARAM_BOOL,
183
                        'null' => NULL_ALLOWED,
184
                        'description' => 'Whether the user can post a private reply',
185
                    ]
186
                ]
187
            ],
188
            'urls' => [
189
                'optional' => true,
190
                'default' => null,
191
                'null' => NULL_ALLOWED,
192
                'type' => [
193
                    'view' => [
194
                        'description' => 'The URL used to view the post',
195
                        'type' => PARAM_URL,
196
                        'optional' => true,
197
                        'default' => null,
198
                        'null' => NULL_ALLOWED
199
                    ],
200
                    'viewisolated' => [
201
                        'description' => 'The URL used to view the post in isolation',
202
                        'type' => PARAM_URL,
203
                        'optional' => true,
204
                        'default' => null,
205
                        'null' => NULL_ALLOWED
206
                    ],
207
                    'viewparent' => [
208
                        'description' => 'The URL used to view the parent of the post',
209
                        'type' => PARAM_URL,
210
                        'optional' => true,
211
                        'default' => null,
212
                        'null' => NULL_ALLOWED
213
                    ],
214
                    'edit' => [
215
                        'description' => 'The URL used to edit the post',
216
                        'type' => PARAM_URL,
217
                        'optional' => true,
218
                        'default' => null,
219
                        'null' => NULL_ALLOWED
220
                    ],
221
                    'delete' => [
222
                        'description' => 'The URL used to delete the post',
223
                        'type' => PARAM_URL,
224
                        'optional' => true,
225
                        'default' => null,
226
                        'null' => NULL_ALLOWED
227
                    ],
228
                    'split' => [
229
                        'description' => 'The URL used to split the discussion ' .
230
                            'with the selected post being the first post in the new discussion',
231
                        'type' => PARAM_URL,
232
                        'optional' => true,
233
                        'default' => null,
234
                        'null' => NULL_ALLOWED
235
                    ],
236
                    'reply' => [
237
                        'description' => 'The URL used to reply to the post',
238
                        'type' => PARAM_URL,
239
                        'optional' => true,
240
                        'default' => null,
241
                        'null' => NULL_ALLOWED
242
                    ],
243
                    'export' => [
244
                        'description' => 'The URL used to export the post',
245
                        'type' => PARAM_URL,
246
                        'optional' => true,
247
                        'default' => null,
248
                        'null' => NULL_ALLOWED
249
                    ],
250
                    'markasread' => [
251
                        'description' => 'The URL used to mark the post as read',
252
                        'type' => PARAM_URL,
253
                        'optional' => true,
254
                        'default' => null,
255
                        'null' => NULL_ALLOWED
256
                    ],
257
                    'markasunread' => [
258
                        'description' => 'The URL used to mark the post as unread',
259
                        'type' => PARAM_URL,
260
                        'optional' => true,
261
                        'default' => null,
262
                        'null' => NULL_ALLOWED
263
                    ],
264
                    'discuss' => [
265
                        'type' => PARAM_URL,
266
                        'optional' => true,
267
                        'default' => null,
268
                        'null' => NULL_ALLOWED
269
                    ]
270
                ]
271
            ],
272
            'attachments' => [
273
                'multiple' => true,
274
                'type' => $attachmentdefinition
275
            ],
276
            'messageinlinefiles' => [
277
                'optional' => true,
278
                'multiple' => true,
279
                'type' => stored_file_exporter::read_properties_definition(),
280
            ],
281
            'tags' => [
282
                'optional' => true,
283
                'default' => null,
284
                'null' => NULL_ALLOWED,
285
                'multiple' => true,
286
                'type' => [
287
                    'id' => [
288
                        'type' => PARAM_INT,
289
                        'description' => 'The ID of the Tag',
290
                        'null' => NULL_NOT_ALLOWED,
291
                    ],
292
                    'tagid' => [
293
                        'type' => PARAM_INT,
294
                        'description' => 'The tagid',
295
                        'null' => NULL_NOT_ALLOWED,
296
                    ],
297
                    'isstandard' => [
298
                        'type' => PARAM_BOOL,
299
                        'description' => 'Whether this is a standard tag',
300
                        'null' => NULL_NOT_ALLOWED,
301
                    ],
302
                    'displayname' => [
303
                        'type' => PARAM_TEXT,
304
                        'description' => 'The display name of the tag',
305
                        'null' => NULL_NOT_ALLOWED,
306
                    ],
307
                    'flag' => [
308
                        'type' => PARAM_BOOL,
309
                        'description' => 'Wehther this tag is flagged',
310
                        'null' => NULL_NOT_ALLOWED,
311
                    ],
312
                    'urls' => [
313
                        'description' => 'URLs associated with the tag',
314
                        'null' => NULL_NOT_ALLOWED,
315
                        'type' => [
316
                            'view' => [
317
                                'type' => PARAM_URL,
318
                                'description' => 'The URL to view the tag',
319
                                'null' => NULL_NOT_ALLOWED,
320
                            ],
321
                        ]
322
                    ]
323
                ]
324
            ],
325
            'html' => [
326
                'optional' => true,
327
                'default' => null,
328
                'null' => NULL_ALLOWED,
329
                'type' => [
330
                    'rating' => [
331
                        'optional' => true,
332
                        'default' => null,
333
                        'null' => NULL_ALLOWED,
334
                        'type' => PARAM_RAW,
335
                        'description' => 'The HTML source to rate the post',
336
                    ],
337
                    'taglist' => [
338
                        'optional' => true,
339
                        'default' => null,
340
                        'null' => NULL_ALLOWED,
341
                        'type' => PARAM_RAW,
342
                        'description' => 'The HTML source to view the list of tags',
343
                    ],
344
                    'authorsubheading' => [
345
                        'optional' => true,
346
                        'default' => null,
347
                        'null' => NULL_ALLOWED,
348
                        'type' => PARAM_RAW,
349
                        'description' => 'The HTML source to view the author details',
350
                    ],
351
                ]
352
            ]
353
        ];
354
    }
355
 
356
    /**
357
     * Get the additional values to inject while exporting.
358
     *
359
     * @param renderer_base $output The renderer.
360
     * @return array Keys are the property names, values are their values.
361
     */
362
    protected function get_other_values(renderer_base $output) {
363
        $post = $this->post;
364
        $authorgroups = $this->related['authorgroups'];
365
        $forum = $this->related['forum'];
366
        $discussion = $this->related['discussion'];
367
        $author = $this->related['author'];
368
        $authorcontextid = $this->related['authorcontextid'];
369
        $user = $this->related['user'];
370
        $readreceiptcollection = $this->related['readreceiptcollection'];
371
        $rating = $this->related['rating'];
372
        $tags = $this->related['tags'];
373
        $attachments = $this->related['attachments'];
374
        $inlineattachments = $this->related['messageinlinefiles'];
375
        $includehtml = $this->related['includehtml'];
376
        $isdeleted = $post->is_deleted();
377
        $isprivatereply = $post->is_private_reply();
378
        $hasrating = $rating != null;
379
        $hastags = !empty($tags);
380
        $discussionid = $post->get_discussion_id();
381
        $parentid = $post->get_parent_id();
382
 
383
        $capabilitymanager = $this->related['capabilitymanager'];
384
        $canview = $capabilitymanager->can_view_post($user, $discussion, $post);
385
        $canedit = $capabilitymanager->can_edit_post($user, $discussion, $post);
386
        $candelete = $capabilitymanager->can_delete_post($user, $discussion, $post);
387
        $cansplit = $capabilitymanager->can_split_post($user, $discussion, $post);
388
        $canreply = $capabilitymanager->can_reply_to_post($user, $discussion, $post);
389
        $canexport = $capabilitymanager->can_export_post($user, $post);
390
        $cancontrolreadstatus = $capabilitymanager->can_manually_control_post_read_status($user);
391
        $canselfenrol = $capabilitymanager->can_self_enrol($user);
392
        $canreplyprivately = $capabilitymanager->can_reply_privately_to_post($user, $post);
393
 
394
        $urlfactory = $this->related['urlfactory'];
395
        $viewurl = $canview ? $urlfactory->get_view_post_url_from_post($post) : null;
396
        $viewisolatedurl = $canview ? $urlfactory->get_view_isolated_post_url_from_post($post) : null;
397
        $viewparenturl = $post->has_parent() ? $urlfactory->get_view_post_url_from_post_id($discussionid, $parentid) : null;
398
        $editurl = $canedit ? $urlfactory->get_edit_post_url_from_post($forum, $post) : null;
399
        $deleteurl = $candelete ? $urlfactory->get_delete_post_url_from_post($post) : null;
400
        $spliturl = $cansplit ? $urlfactory->get_split_discussion_at_post_url_from_post($post) : null;
401
        $replyurl = $canreply || $canselfenrol ? $urlfactory->get_reply_to_post_url_from_post($post) : null;
402
        $exporturl = $canexport ? $urlfactory->get_export_post_url_from_post($post) : null;
403
        $markasreadurl = $cancontrolreadstatus ? $urlfactory->get_mark_post_as_read_url_from_post($post) : null;
404
        $markasunreadurl = $cancontrolreadstatus ? $urlfactory->get_mark_post_as_unread_url_from_post($post) : null;
405
        $discussurl = $canview ? $urlfactory->get_discussion_view_url_from_post($post) : null;
406
 
407
        $authorexporter = new author_exporter(
408
            $author,
409
            $authorcontextid,
410
            $authorgroups,
411
            $canview,
412
            $this->related
413
        );
414
        $exportedauthor = $authorexporter->export($output);
415
        // Only bother loading the content if the user can see it.
416
        $loadcontent = $canview && !$isdeleted;
417
        $exportattachments = $loadcontent && !empty($attachments);
418
        $exportinlineattachments = $loadcontent && !empty($inlineattachments);
419
 
420
        if ($loadcontent) {
421
            $subject = $post->get_subject();
422
            $timecreated = $this->get_start_time($discussion, $post);
423
            $message = $this->get_message($post);
424
        } else {
425
            $subject = $isdeleted ? get_string('forumsubjectdeleted', 'forum') : get_string('forumsubjecthidden', 'forum');
426
            $message = $isdeleted ? get_string('forumbodydeleted', 'forum') : get_string('forumbodyhidden', 'forum');
427
            $timecreated = null;
428
        }
429
 
430
        $replysubject = $subject;
431
        $strre = get_string('re', 'forum');
432
        if (!(substr($replysubject, 0, strlen($strre)) == $strre)) {
433
            $replysubject = "{$strre} {$replysubject}";
434
        }
435
 
436
        $showwordcount = $forum->should_display_word_count();
437
        if ($showwordcount) {
438
            $wordcount = $post->get_wordcount() ?? count_words($message);
439
            $charcount = $post->get_charcount() ?? count_letters($message);
440
        } else {
441
            $wordcount = null;
442
            $charcount = null;
443
        }
444
 
445
        return [
446
            'id' => $post->get_id(),
447
            'subject' => $subject,
448
            'replysubject' => $replysubject,
449
            'message' => $message,
450
            'messageformat' => $post->get_message_format(),
451
            'author' => $exportedauthor,
452
            'discussionid' => $post->get_discussion_id(),
453
            'hasparent' => $post->has_parent(),
454
            'parentid' => $post->has_parent() ? $post->get_parent_id() : null,
455
            'timecreated' => $timecreated,
456
            'timemodified' => $post->get_time_modified(),
457
            'unread' => ($loadcontent && $readreceiptcollection) ? !$readreceiptcollection->has_user_read_post($user, $post) : null,
458
            'isdeleted' => $isdeleted,
459
            'isprivatereply' => $isprivatereply,
460
            'haswordcount' => $showwordcount,
461
            'wordcount' => $wordcount,
462
            'charcount' => $charcount,
463
            'capabilities' => [
464
                'view' => $canview,
465
                'edit' => $canedit,
466
                'delete' => $candelete,
467
                'split' => $cansplit,
468
                'reply' => $canreply,
469
                'export' => $canexport,
470
                'controlreadstatus' => $cancontrolreadstatus,
471
                'canreplyprivately' => $canreplyprivately,
472
                'selfenrol' => $canselfenrol
473
            ],
474
            'urls' => [
475
                'view' => $viewurl ? $viewurl->out(false) : null,
476
                'viewisolated' => $viewisolatedurl ? $viewisolatedurl->out(false) : null,
477
                'viewparent' => $viewparenturl ? $viewparenturl->out(false) : null,
478
                'edit' => $editurl ? $editurl->out(false) : null,
479
                'delete' => $deleteurl ? $deleteurl->out(false) : null,
480
                'split' => $spliturl ? $spliturl->out(false) : null,
481
                'reply' => $replyurl ? $replyurl->out(false) : null,
482
                'export' => $exporturl && $exporturl ? $exporturl->out(false) : null,
483
                'markasread' => $markasreadurl ? $markasreadurl->out(false) : null,
484
                'markasunread' => $markasunreadurl ? $markasunreadurl->out(false) : null,
485
                'discuss' => $discussurl ? $discussurl->out(false) : null,
486
            ],
487
            'attachments' => ($exportattachments) ? $this->export_attachments($attachments, $post, $output, $canexport) : [],
488
            'messageinlinefiles' => ($exportinlineattachments) ? $this->export_inline_attachments($inlineattachments,
489
                $post, $output) : [],
490
            'tags' => ($loadcontent && $hastags) ? $this->export_tags($tags) : [],
491
            'html' => $includehtml ? [
492
                'rating' => ($loadcontent && $hasrating) ? $output->render($rating) : null,
493
                'taglist' => ($loadcontent && $hastags) ? $output->tag_list($tags) : null,
494
                'authorsubheading' => ($loadcontent) ? $this->get_author_subheading_html($exportedauthor, $timecreated) : null
495
            ] : null
496
        ];
497
    }
498
 
499
    /**
500
     * Returns a list of objects that are related.
501
     *
502
     * @return array
503
     */
504
    protected static function define_related() {
505
        return [
506
            'capabilitymanager' => 'mod_forum\local\managers\capability',
507
            'readreceiptcollection' => 'mod_forum\local\entities\post_read_receipt_collection?',
508
            'urlfactory' => 'mod_forum\local\factories\url',
509
            'forum' => 'mod_forum\local\entities\forum',
510
            'discussion' => 'mod_forum\local\entities\discussion',
511
            'author' => 'mod_forum\local\entities\author',
512
            'authorcontextid' => 'int?',
513
            'user' => 'stdClass',
514
            'context' => 'context',
515
            'authorgroups' => 'stdClass[]',
516
            'attachments' => '\stored_file[]?',
517
            'messageinlinefiles' => '\stored_file[]?',
518
            'tags' => '\core_tag_tag[]?',
519
            'rating' => 'rating?',
520
            'includehtml' => 'bool'
521
        ];
522
    }
523
 
524
    /**
525
     * This method returns the parameters for the post's message to
526
     * use with the function \core_external\util::format_text().
527
     *
528
     * @return array
529
     */
530
    protected function get_format_parameters_for_message() {
531
        return [
532
            'component' => 'mod_forum',
533
            'filearea' => 'post',
534
            'itemid' => $this->post->get_id(),
535
            'options' => [
536
                'para' => false,
537
                'trusted' => $this->post->is_message_trusted()
538
            ]
539
        ];
540
    }
541
 
542
    /**
543
     * Get the message text from a post.
544
     *
545
     * @param post_entity $post The post
546
     * @return string
547
     */
548
    private function get_message(post_entity $post): string {
549
        global $CFG;
550
 
551
        $message = $post->get_message();
552
 
553
        if (!empty($CFG->enableplagiarism)) {
554
            require_once($CFG->libdir . '/plagiarismlib.php');
555
            $forum = $this->related['forum'];
556
            $message .= plagiarism_get_links([
557
                'userid' => $post->get_author_id(),
558
                'content' => $message,
559
                'cmid' => $forum->get_course_module_record()->id,
560
                'course' => $forum->get_course_id(),
561
                'forum' => $forum->get_id()
562
            ]);
563
        }
564
 
565
        return $message;
566
    }
567
 
568
    /**
569
     * Get the exported attachments for a post.
570
     *
571
     * @param stored_file[] $attachments The list of attachments for the post
572
     * @param post_entity $post The post being exported
573
     * @param renderer_base $output Renderer base
574
     * @param bool $canexport If the user can export the post (relates to portfolios not exporters like this class)
575
     * @return array
576
     */
577
    private function export_attachments(array $attachments, post_entity $post, renderer_base $output, bool $canexport): array {
578
        global $CFG;
579
 
580
        $urlfactory = $this->related['urlfactory'];
581
        $enableplagiarism = $CFG->enableplagiarism;
582
        $forum = $this->related['forum'];
583
        $context = $this->related['context'];
584
 
585
        if ($enableplagiarism) {
586
            require_once($CFG->libdir . '/plagiarismlib.php' );
587
        }
588
 
589
        return array_map(function($attachment) use (
590
            $output,
591
            $enableplagiarism,
592
            $canexport,
593
            $context,
594
            $forum,
595
            $post,
596
            $urlfactory
597
        ) {
598
            $exporter = new stored_file_exporter($attachment, ['context' => $context]);
599
            $exportedattachment = $exporter->export($output);
600
            $exporturl = $canexport ? $urlfactory->get_export_attachment_url_from_post_and_attachment($post, $attachment) : null;
601
 
602
            if ($enableplagiarism) {
603
                $plagiarismhtml = plagiarism_get_links([
604
                    'userid' => $post->get_author_id(),
605
                    'file' => $attachment,
606
                    'cmid' => $forum->get_course_module_record()->id,
607
                    'course' => $forum->get_course_id(),
608
                    'forum' => $forum->get_id()
609
                ]);
610
            } else {
611
                $plagiarismhtml = null;
612
            }
613
 
614
            $exportedattachment->urls = [
615
                'export' => $exporturl ? $exporturl->out(false) : null
616
            ];
617
            $exportedattachment->html = [
618
                'plagiarism' => $plagiarismhtml
619
            ];
620
 
621
            return $exportedattachment;
622
        }, $attachments);
623
    }
624
 
625
    /**
626
     * Get the exported inline attachments for a post.
627
     *
628
     * @param array $inlineattachments The list of inline attachments for the post
629
     * @param post_entity $post The post being exported
630
     * @param renderer_base $output Renderer base
631
     * @return array
632
     */
633
    private function export_inline_attachments(array $inlineattachments, post_entity $post, renderer_base $output): array {
634
 
635
        return array_map(function($attachment) use (
636
            $output,
637
            $post
638
        ) {
639
            $exporter = new stored_file_exporter($attachment, ['context' => $this->related['context']]);
640
            return $exporter->export($output);;
641
        }, $inlineattachments);
642
    }
643
 
644
    /**
645
     * Export the list of tags.
646
     *
647
     * @param core_tag_tag[] $tags List of tags to export
648
     * @return array
649
     */
650
    private function export_tags(array $tags): array {
651
        $user = $this->related['user'];
652
        $context = $this->related['context'];
653
        $capabilitymanager = $this->related['capabilitymanager'];
654
        $canmanagetags = $capabilitymanager->can_manage_tags($user);
655
 
656
        return array_values(array_map(function($tag) use ($context, $canmanagetags) {
657
            $viewurl = core_tag_tag::make_url($tag->tagcollid, $tag->rawname, 0, $context->id);
658
            return [
659
                'id' => $tag->taginstanceid,
660
                'tagid' => $tag->id,
661
                'isstandard' => $tag->isstandard,
662
                'displayname' => $tag->get_display_name(),
663
                'flag' => $canmanagetags && !empty($tag->flag),
664
                'urls' => [
665
                    'view' => $viewurl->out(false)
666
                ]
667
            ];
668
        }, $tags));
669
    }
670
 
671
    /**
672
     * Get the HTML to display as a subheading in a post.
673
     *
674
     * @param stdClass $exportedauthor The exported author object
675
     * @param int $timecreated The post time created timestamp if it's to be displayed
676
     * @return string
677
     */
678
    private function get_author_subheading_html(stdClass $exportedauthor, int $timecreated): string {
679
        $fullname = $exportedauthor->fullname;
680
        $profileurl = $exportedauthor->urls['profile'] ?? null;
681
        $name = $profileurl ? "<a href=\"{$profileurl}\">{$fullname}</a>" : $fullname;
682
        $date = userdate_htmltime($timecreated, get_string('strftimedaydatetime', 'core_langconfig'));
683
        return get_string('bynameondate', 'mod_forum', ['name' => $name, 'date' => $date]);
684
    }
685
 
686
    /**
687
     * Get the start time for a post.
688
     *
689
     * @param discussion_entity $discussion entity
690
     * @param post_entity $post entity
691
     * @return int The start time (timestamp) for a post
692
     */
693
    private function get_start_time(discussion_entity $discussion, post_entity $post) {
694
        global $CFG;
695
 
696
        $posttime = $post->get_time_created();
697
        $discussiontime = $discussion->get_time_start();
698
        if (!empty($CFG->forum_enabletimedposts) && ($discussiontime > $posttime)) {
699
            return $discussiontime;
700
        }
701
        return $posttime;
702
    }
703
}