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
namespace tool_usertours;
18
 
19
use context_system;
20
use stdClass;
21
 
22
/**
23
 * Step class.
24
 *
25
 * @package    tool_usertours
26
 * @copyright  2016 Andrew Nicols <andrew@nicols.co.uk>
27
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
28
 */
29
class step {
30
    /**
31
     * @var     int     $id         The id of the step.
32
     */
33
    protected $id;
34
 
35
    /**
36
     * @var     int     $tourid     The id of the tour that this step belongs to.
37
     */
38
    protected $tourid;
39
 
40
    /**
41
     * @var     tour    $tour       The tour class that this step belongs to.
42
     */
43
    protected $tour;
44
 
45
    /**
46
     * @var     string  $title      The title of the step.
47
     */
48
    protected $title;
49
 
50
    /**
51
     * @var     string  $content    The content of this step.
52
     */
53
    protected $content;
54
 
55
    /**
56
     * @var     int  $contentformat    The content format: FORMAT_MOODLE/FORMAT_HTML/FORMAT_PLAIN/FORMAT_MARKDOWN.
57
     */
58
    protected $contentformat;
59
 
60
    /**
61
     * @var     int     $targettype The type of target.
62
     */
63
    protected $targettype;
64
 
65
    /**
66
     * @var     string  $targetvalue    The value for this type of target.
67
     */
68
    protected $targetvalue;
69
 
70
    /**
71
     * @var     int     $sortorder  The sort order.
72
     */
73
    protected $sortorder;
74
 
75
    /**
76
     * @var     object  $config     The configuration as an object.
77
     */
78
    protected $config;
79
 
80
    /**
81
     * @var     bool    $dirty      Whether the step has been changed since it was loaded
82
     */
83
    protected $dirty = false;
84
 
85
    /**
86
     * @var bool $isimporting Whether the step is being imported or not.
87
     */
88
    protected $isimporting;
89
 
90
    /**
91
     * @var stdClass[] $files The list of attached files for this step.
92
     */
93
    protected $files = [];
94
 
95
    /**
96
     * Fetch the step instance.
97
     *
98
     * @param   int             $id         The id of the step to be retrieved.
99
     * @return  step
100
     */
101
    public static function instance($id) {
102
        $step = new step();
103
        return $step->fetch($id);
104
    }
105
 
106
    /**
107
     * Load the step instance.
108
     *
109
     * @param stdClass $record The step record to be loaded.
110
     * @param bool $clean Clean the values.
111
     * @param bool $isimporting Whether the step is being imported or not.
112
     * @return step
113
     */
114
    public static function load_from_record($record, $clean = false, bool $isimporting = false) {
115
        $step = new self();
116
        $step->set_importing($isimporting);
117
        return $step->reload_from_record($record, $clean);
118
    }
119
 
120
    /**
121
     * Fetch the step instance.
122
     *
123
     * @param   int             $id         The id of the step to be retrieved.
124
     * @return  step
125
     */
126
    protected function fetch($id) {
127
        global $DB;
128
 
129
        return $this->reload_from_record(
130
            $DB->get_record('tool_usertours_steps', ['id' => $id])
131
        );
132
    }
133
 
134
    /**
135
     * Refresh the current step from the datbase.
136
     *
137
     * @return  step
138
     */
139
    protected function reload() {
140
        return $this->fetch($this->id);
141
    }
142
 
143
    /**
144
     * Reload the current step from the supplied record.
145
     *
146
     * @param stdClass $record The step record to be loaded.
147
     * @param bool $clean Clean the values.
148
     * @return step
149
     */
150
    protected function reload_from_record($record, $clean = false) {
151
        $this->id           = $record->id;
152
        $this->tourid       = $record->tourid;
153
        if ($clean) {
154
            $this->title    = clean_param($record->title, PARAM_TEXT);
155
            $this->content  = clean_text($record->content);
156
        } else {
157
            $this->title    = $record->title;
158
            $this->content  = $record->content;
159
        }
160
        $this->contentformat = isset($record->contentformat) ? $record->contentformat : FORMAT_MOODLE;
161
        $this->targettype   = $record->targettype;
162
        $this->targetvalue  = $record->targetvalue;
163
        $this->sortorder    = $record->sortorder;
164
        $this->config       = json_decode($record->configdata);
165
        $this->dirty        = false;
166
 
167
        if ($this->isimporting && isset($record->files)) {
168
            // We are importing/exporting the step.
169
            $this->files = $record->files;
170
        }
171
 
172
        return $this;
173
    }
174
 
175
    /**
176
     * Set the import state for the step.
177
     *
178
     * @param bool $isimporting True if the step is imported, otherwise false.
179
     * @return void
180
     */
181
    protected function set_importing(bool $isimporting = false): void {
182
        $this->isimporting = $isimporting;
183
    }
184
 
185
    /**
186
     * Get the ID of the step.
187
     *
188
     * @return  int
189
     */
190
    public function get_id() {
191
        return $this->id;
192
    }
193
 
194
    /**
195
     * Get the Tour ID of the step.
196
     *
197
     * @return  int
198
     */
199
    public function get_tourid() {
200
        return $this->tourid;
201
    }
202
 
203
    /**
204
     * Get the Tour instance that this step belongs to.
205
     *
206
     * @return  tour
207
     */
208
    public function get_tour() {
209
        if ($this->tour === null) {
210
            $this->tour = tour::instance($this->tourid);
211
        }
212
        return $this->tour;
213
    }
214
 
215
    /**
216
     * Set the id of the tour.
217
     *
218
     * @param   int             $value      The id of the tour.
219
     * @return  self
220
     */
221
    public function set_tourid($value) {
222
        $this->tourid = $value;
223
        $this->tour = null;
224
        $this->dirty = true;
225
 
226
        return $this;
227
    }
228
 
229
    /**
230
     * Get the Title of the step.
231
     *
232
     * @return  string
233
     */
234
    public function get_title() {
235
        return $this->title;
236
    }
237
 
238
    /**
239
     * Set the title for this step.
240
     *
241
     * @param   string      $value      The new title to use.
242
     * @return  $this
243
     */
244
    public function set_title($value) {
245
        $this->title = clean_text($value);
246
        $this->dirty = true;
247
 
248
        return $this;
249
    }
250
 
251
    /**
252
     * Get the content format of the step.
253
     *
254
     * @return  int
255
     */
256
    public function get_contentformat(): int {
257
        return $this->contentformat;
258
    }
259
 
260
    /**
261
     * Get the body content of the step.
262
     *
263
     * @return  string
264
     */
265
    public function get_content() {
266
        return $this->content;
267
    }
268
 
269
    /**
270
     * Set the content value for this step.
271
     *
272
     * @param   string      $value      The new content to use.
273
     * @param   int         $format     The new format to use: FORMAT_MOODLE/FORMAT_HTML/FORMAT_PLAIN/FORMAT_MARKDOWN.
274
     * @return  $this
275
     */
276
    public function set_content($value, $format = FORMAT_HTML) {
277
        $this->content = clean_text($value);
278
        $this->contentformat = $format;
279
        $this->dirty = true;
280
 
281
        return $this;
282
    }
283
 
284
    /**
285
     * Get the content value for this step.
286
     *
287
     * @return  string
288
     */
289
    public function get_targettype() {
290
        return $this->targettype;
291
    }
292
 
293
    /**
294
     * Set the type of target for this step.
295
     *
296
     * @param   string      $value      The new target to use.
297
     * @return  $this
298
     */
299
    public function set_targettype($value) {
300
        $this->targettype = $value;
301
        $this->dirty = true;
302
 
303
        return $this;
304
    }
305
 
306
    /**
307
     * Get the target value for this step.
308
     *
309
     * @return  string
310
     */
311
    public function get_targetvalue() {
312
        return $this->targetvalue;
313
    }
314
 
315
    /**
316
     * Set the target value for this step.
317
     *
318
     * @param   string      $value      The new target value to use.
319
     * @return  $this
320
     */
321
    public function set_targetvalue($value) {
322
        $this->targetvalue = $value;
323
        $this->dirty = true;
324
 
325
        return $this;
326
    }
327
 
328
    /**
329
     * Get the target instance for this step.
330
     *
331
     * @return  target
332
     */
333
    public function get_target() {
334
        return target::get_target_instance($this);
335
    }
336
 
337
    /**
338
     * Get the current sortorder for this step.
339
     *
340
     * @return  int
341
     */
342
    public function get_sortorder() {
343
        return (int) $this->sortorder;
344
    }
345
 
346
    /**
347
     * Whether this step is the first step in the tour.
348
     *
349
     * @return  boolean
350
     */
351
    public function is_first_step() {
352
        return ($this->get_sortorder() === 0);
353
    }
354
 
355
    /**
356
     * Whether this step is the last step in the tour.
357
     *
358
     * @return  boolean
359
     */
360
    public function is_last_step() {
361
        $stepcount = $this->get_tour()->count_steps();
362
        return ($this->get_sortorder() === $stepcount - 1);
363
    }
364
 
365
    /**
366
     * Set the sortorder for this step.
367
     *
368
     * @param   int         $value      The new sortorder to use.
369
     * @return  $this
370
     */
371
    public function set_sortorder($value) {
372
        $this->sortorder = $value;
373
        $this->dirty = true;
374
 
375
        return $this;
376
    }
377
 
378
    /**
379
     * Get the link to move this step up in the sortorder.
380
     *
381
     * @return  \moodle_url
382
     */
383
    public function get_moveup_link() {
384
        return helper::get_move_step_link($this->get_id(), helper::MOVE_UP);
385
    }
386
 
387
    /**
388
     * Get the link to move this step down in the sortorder.
389
     *
390
     * @return  \moodle_url
391
     */
392
    public function get_movedown_link() {
393
        return helper::get_move_step_link($this->get_id(), helper::MOVE_DOWN);
394
    }
395
 
396
    /**
397
     * Get the value of the specified configuration item.
398
     *
399
     * If notvalue was found, and no default was specified, the default for the tour will be used.
400
     *
401
     * @param   string      $key        The configuration key to set.
402
     * @param   mixed       $default    The default value to use if a value was not found.
403
     * @return  mixed
404
     */
405
    public function get_config($key = null, $default = null) {
406
        if ($this->config === null) {
407
            $this->config = (object) [];
408
        }
409
 
410
        if ($key === null) {
411
            return $this->config;
412
        }
413
 
414
        if ($this->get_targettype() !== null) {
415
            $target = $this->get_target();
416
            if ($target->is_setting_forced($key)) {
417
                return $target->get_forced_setting_value($key);
418
            }
419
        }
420
 
421
        if (property_exists($this->config, $key)) {
422
            return $this->config->$key;
423
        }
424
 
425
        if ($default !== null) {
426
            return $default;
427
        }
428
 
429
        return $this->get_tour()->get_config($key);
430
    }
431
 
432
    /**
433
     * Set the configuration item as specified.
434
     *
435
     * @param   string      $key        The configuration key to set.
436
     * @param   mixed       $value      The new value for the configuration item.
437
     * @return  $this
438
     */
439
    public function set_config($key, $value) {
440
        if ($this->config === null) {
441
            $this->config = (object) [];
442
        }
443
 
444
        if ($value === null) {
445
            unset($this->config->$key);
446
        } else {
447
            $this->config->$key = $value;
448
        }
449
        $this->dirty = true;
450
 
451
        return $this;
452
    }
453
 
454
    /**
455
     * Get the edit link for this step.
456
     *
457
     * @return  \moodle_url
458
     */
459
    public function get_edit_link() {
460
        return helper::get_edit_step_link($this->tourid, $this->id);
461
    }
462
 
463
    /**
464
     * Get the delete link for this step.
465
     *
466
     * @return  \moodle_url
467
     */
468
    public function get_delete_link() {
469
        return helper::get_delete_step_link($this->id);
470
    }
471
 
472
    /**
473
     * Embed attached file to the json file for step.
474
     *
475
     * @return array List of files.
476
     */
477
    protected function embed_files(): array {
478
        $systemcontext = context_system::instance();
479
        $fs = get_file_storage();
480
        $areafiles = $fs->get_area_files($systemcontext->id, 'tool_usertours', 'stepcontent', $this->id);
481
        $files = [];
482
        foreach ($areafiles as $file) {
483
            if ($file->is_directory()) {
484
                continue;
485
            }
486
            $files[] = [
487
                'name' => $file->get_filename(),
488
                'path' => $file->get_filepath(),
489
                'content' => base64_encode($file->get_content()),
490
                'encode' => 'base64',
491
            ];
492
        }
493
 
494
        return $files;
495
    }
496
 
497
    /**
498
     * Get the embed files information and create store_file for this step.
499
     *
500
     * @return void
501
     */
502
    protected function extract_files() {
503
        $fs = get_file_storage();
504
        $systemcontext = context_system::instance();
505
        foreach ($this->files as $file) {
506
            $filename = $file->name;
507
            $filepath = $file->path;
508
            $filecontent = $file->content;
509
            $filerecord = [
510
                'contextid' => $systemcontext->id,
511
                'component' => 'tool_usertours',
512
                'filearea' => 'stepcontent',
513
                'itemid' => $this->get_id(),
514
                'filepath' => $filepath,
515
                'filename' => $filename,
516
            ];
517
            $fs->create_file_from_string($filerecord, base64_decode($filecontent));
518
        }
519
    }
520
 
521
    /**
522
     * Prepare this step for saving to the database.
523
     *
524
     * @param bool $isexporting Whether the step is being exported or not.
525
     * @return  object
526
     */
527
    public function to_record(bool $isexporting = false) {
528
        $record = [
529
            'id'            => $this->id,
530
            'tourid'        => $this->tourid,
531
            'title'         => $this->title,
532
            'content'       => $this->content,
533
            'contentformat' => $this->contentformat,
534
            'targettype'    => $this->targettype,
535
            'targetvalue'   => $this->targetvalue,
536
            'sortorder'     => $this->sortorder,
537
            'configdata'    => json_encode($this->config),
538
        ];
539
        if ($isexporting) {
540
            // We are exporting the step, adding files node to the json record.
541
            $record['files'] = $this->embed_files();
542
        }
543
        return (object) $record;
544
    }
545
 
546
    /**
547
     * Calculate the next sort-order value.
548
     *
549
     * @return  int
550
     */
551
    protected function calculate_sortorder() {
552
        $count = $this->get_tour()->count_steps();
553
        $this->sortorder = $count;
554
 
555
        return $this;
556
    }
557
 
558
    /**
559
     * Save the tour and it's configuration to the database.
560
     *
561
     * @param   boolean     $force      Whether to force writing to the database.
562
     * @return  $this
563
     */
564
    public function persist($force = false) {
565
        global $DB;
566
 
567
        if (!$this->dirty && !$force) {
568
            return $this;
569
        }
570
 
571
        if ($this->id) {
572
            $record = $this->to_record();
573
            $DB->update_record('tool_usertours_steps', $record);
574
        } else {
575
            $this->calculate_sortorder();
576
            $record = $this->to_record();
577
            unset($record->id);
578
            $this->id = $DB->insert_record('tool_usertours_steps', $record);
579
            $this->get_tour()->reset_step_sortorder();
580
        }
581
 
582
        $systemcontext = context_system::instance();
583
        if ($draftid = file_get_submitted_draft_itemid('content')) {
584
            // Take any files added to the stepcontent draft file area and
585
            // convert them into the proper event description file area. Also
586
            // parse the content text and replace the URLs to the draft files
587
            // with the @@PLUGIN_FILE@@ placeholder to be persisted in the DB.
588
            $this->content = file_save_draft_area_files(
589
                $draftid,
590
                $systemcontext->id,
591
                'tool_usertours',
592
                'stepcontent',
593
                $this->id,
594
                ['subdirs' => true],
595
                $this->content
596
            );
597
            $DB->set_field('tool_usertours_steps', 'content', $this->content, ['id' => $this->id]);
598
        }
599
 
600
        if ($this->isimporting) {
601
            // We are importing the step, we need to create store_file from the json record.
602
            $this->extract_files();
603
        }
604
        $this->reload();
605
 
606
        // Notify of a change to the step configuration.
607
        // This must be done separately to tour change notifications.
608
        cache::notify_step_change($this->get_tourid());
609
 
610
        // Notify the cache that a tour has changed.
611
        // Tours are only stored in the cache if there are steps.
612
        // If there step count has changed for some reason, this will change the potential cache results.
613
        cache::notify_tour_change();
614
 
615
        return $this;
616
    }
617
 
618
    /**
619
     * Remove this step.
620
     */
621
    public function remove() {
622
        global $DB;
623
 
624
        if ($this->id === null) {
625
            return;
626
        }
627
 
628
        $DB->delete_records('tool_usertours_steps', ['id' => $this->id]);
629
        $this->get_tour()->reset_step_sortorder();
630
 
631
        // Notify of a change to the step configuration.
632
        // This must be done separately to tour change notifications.
633
        cache::notify_step_change($this->get_id());
634
 
635
        // Notify the cache that a tour has changed.
636
        // Tours are only stored in the cache if there are steps.
637
        // If there step count has changed for some reason, this will change the potential cache results.
638
        cache::notify_tour_change();
639
    }
640
 
641
    /**
642
     * Get the list of possible placement options.
643
     *
644
     * @return  array
645
     */
646
    public function get_placement_options() {
647
        return configuration::get_placement_options(true);
648
    }
649
 
650
    /**
651
     * The list of possible configuration keys.
652
     *
653
     * @return  array
654
     */
655
    public static function get_config_keys() {
656
        return [
657
            'placement',
658
            'orphan',
659
            'backdrop',
660
            'reflex',
661
        ];
662
    }
663
 
664
    /**
665
     * Add the step configuration to the form.
666
     *
667
     * @param   \MoodleQuickForm $mform     The form to add configuration to.
668
     * @return  $this
669
     */
670
    public function add_config_to_form(\MoodleQuickForm $mform) {
671
        $tour = $this->get_tour();
672
 
673
        $options = configuration::get_placement_options($tour->get_config('placement'));
674
        $mform->addElement('select', 'placement', get_string('placement', 'tool_usertours'), $options);
675
        $mform->addHelpButton('placement', 'placement', 'tool_usertours');
676
 
677
        $this->add_config_field_to_form($mform, 'orphan');
678
        $this->add_config_field_to_form($mform, 'backdrop');
679
        $this->add_config_field_to_form($mform, 'reflex');
680
 
681
        return $this;
682
    }
683
 
684
    /**
685
     * Add the specified step field configuration to the form.
686
     *
687
     * @param   \MoodleQuickForm $mform     The form to add configuration to.
688
     * @param   string          $key        The key to add.
689
     * @return  $this
690
     */
691
    public function add_config_field_to_form(\MoodleQuickForm $mform, $key) {
692
        $tour = $this->get_tour();
693
 
694
        $default = (bool) $tour->get_config($key);
695
 
696
        $options = [
697
            true    => get_string('yes'),
698
            false   => get_string('no'),
699
        ];
700
 
701
        if (!isset($options[$default])) {
702
            $default = configuration::get_default_value($key);
703
        }
704
 
705
        $options = array_reverse($options, true);
706
        $options[configuration::TOURDEFAULT] = get_string('defaultvalue', 'tool_usertours', $options[$default]);
707
        $options = array_reverse($options, true);
708
 
709
        $mform->addElement('select', $key, get_string($key, 'tool_usertours'), $options);
710
        $mform->setDefault($key, configuration::TOURDEFAULT);
711
        $mform->addHelpButton($key, $key, 'tool_usertours');
712
 
713
        return $this;
714
    }
715
 
716
    /**
717
     * Prepare the configuration data for the moodle form.
718
     *
719
     * @return  object
720
     */
721
    public function prepare_data_for_form() {
722
        $data = $this->to_record();
723
        foreach (self::get_config_keys() as $key) {
724
            $data->$key = $this->get_config($key, configuration::get_step_default_value($key));
725
        }
726
 
727
        if ($this->get_targettype() !== null) {
728
            $this->get_target()->prepare_data_for_form($data);
729
        }
730
 
731
        // Prepare content for editing in a form 'editor' field type.
732
        $draftitemid = file_get_submitted_draft_itemid('tool_usertours');
733
        $systemcontext = context_system::instance();
734
        $data->content = [
735
            'format' => $data->contentformat,
736
            'itemid' => $draftitemid,
737
            'text' => file_prepare_draft_area(
738
                $draftitemid,
739
                $systemcontext->id,
740
                'tool_usertours',
741
                'stepcontent',
742
                $this->id,
743
                ['subdirs' => true],
744
                $data->content
745
            ),
746
        ];
747
 
748
        return $data;
749
    }
750
 
751
    /**
752
     * Handle submission of the step editing form.
753
     *
754
     * @param   local\forms\editstep  $mform      The sumitted form.
755
     * @param   stdClass        $data       The submitted data.
756
     * @return  $this
757
     */
758
    public function handle_form_submission(local\forms\editstep &$mform, stdClass $data) {
759
        $this->set_title($data->title);
760
        $this->set_content($data->content['text'], $data->content['format']);
761
        $this->set_targettype($data->targettype);
762
 
763
        $this->set_targetvalue($this->get_target()->get_value_from_form($data));
764
 
765
        foreach (self::get_config_keys() as $key) {
766
            if (!$this->get_target()->is_setting_forced($key)) {
767
                if (isset($data->$key)) {
768
                    $value = $data->$key;
769
                } else {
770
                    $value = configuration::TOURDEFAULT;
771
                }
772
                if ($value === configuration::TOURDEFAULT) {
773
                    $this->set_config($key, null);
774
                } else {
775
                    $this->set_config($key, $value);
776
                }
777
            }
778
        }
779
 
780
        $this->persist();
781
 
782
        return $this;
783
    }
784
 
785
    /**
786
     * Attempt to fetch any matching langstring if the string is in the
787
     * format identifier,component.
788
     *
789
     * @deprecated since Moodle 4.0 MDL-72783. Please use helper::get_string_from_input() instead.
790
     * @param   string  $string
791
     * @return  string
792
     */
793
    public static function get_string_from_input($string) {
794
        debugging('Use of ' . __FUNCTION__ .
795
            '() have been deprecated, please update your code to use helper::get_string_from_input()', DEBUG_DEVELOPER);
796
        return helper::get_string_from_input($string);
797
    }
798
 
799
    /**
800
     * Attempt to replace PIXICON placeholder with the correct images for tour step content.
801
     *
802
     * @param string $content Tour content
803
     * @return string Processed tour content
804
     */
805
    public static function get_step_image_from_input(string $content): string {
806
        if (strpos($content, '@@PIXICON') === false) {
807
            return $content;
808
        }
809
 
810
        $content = preg_replace_callback(
811
            '%@@PIXICON::(?P<identifier>([^::]*))::(?P<component>([^@@]*))@@%',
812
            function (array $matches) {
813
                global $OUTPUT;
814
                $component = $matches['component'];
815
                if ($component == 'moodle') {
816
                    $component = 'core';
817
                }
818
                return \html_writer::img(
819
                    $OUTPUT->image_url($matches['identifier'], $component)->out(false),
820
                    '',
821
                    ['class' => 'img-fluid']
822
                );
823
            },
824
            $content
825
        );
826
 
827
        return $content;
828
    }
829
}