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 core_communication;
18
 
19
use core\context;
20
use stdClass;
21
use stored_file;
22
 
23
/**
24
 * Class processor to manage the base operations of the providers.
25
 *
26
 * This class is responsible for creating, updating, deleting and loading the communication instance, associated actions.
27
 *
28
 * @package    core_communication
29
 * @copyright  2023 Safat Shahin <safat.shahin@moodle.com>
30
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
31
 */
32
class processor {
33
    /** @var string The magic 'none' provider */
34
    public const PROVIDER_NONE = 'none';
35
 
36
    /** @var int The provider active flag */
37
    public const PROVIDER_ACTIVE = 1;
38
 
39
    /** @var int The provider inactive flag */
40
    public const PROVIDER_INACTIVE = 0;
41
 
42
    /**
43
     * @var communication_provider|room_chat_provider|room_user_provider|synchronise_provider|user_provider|null The provider class
44
     */
45
    private communication_provider|user_provider|room_chat_provider|room_user_provider|synchronise_provider|null $provider = null;
46
 
47
    /**
48
     * Communication processor constructor.
49
     *
50
     * @param stdClass $instancedata The instance data object
51
     */
52
    protected function __construct(
53
        private stdClass $instancedata,
54
    ) {
55
        $providercomponent = $this->instancedata->provider;
56
        $providerclass = $this->get_classname_for_provider($providercomponent);
57
        if (!class_exists($providerclass)) {
58
            throw new \moodle_exception('communicationproviderclassnotfound', 'core_communication', '', $providerclass);
59
        }
60
 
61
        if (!is_a($providerclass, communication_provider::class, true)) {
62
            // At the moment we only have one communication provider interface.
63
            // In the future, we may have others, at which point we will support the newest first and
64
            // emit a debugging notice for older ones.
65
            throw new \moodle_exception('communicationproviderclassinvalid', 'core_communication', '', $providerclass);
66
        }
67
 
68
        $this->provider = $providerclass::load_for_instance($this);
69
    }
70
 
71
    /**
72
     * Create communication instance.
73
     *
74
     * @param context $context The context of the item for the instance
75
     * @param string $provider The communication provider
76
     * @param int $instanceid The instance id
77
     * @param string $component The component name
78
     * @param string $instancetype The instance type
79
     * @param string $roomname The room name
80
     * @return processor|null
81
     */
82
    public static function create_instance(
83
        context $context,
84
        string $provider,
85
        int $instanceid,
86
        string $component,
87
        string $instancetype,
88
        string $roomname,
89
    ): ?self {
90
        global $DB;
91
 
92
        if ($provider === self::PROVIDER_NONE) {
93
            return null;
94
        }
95
        $record = (object) [
96
            'contextid' => $context->id,
97
            'provider' => $provider,
98
            'instanceid' => $instanceid,
99
            'component' => $component,
100
            'instancetype' => $instancetype,
101
            'roomname' => $roomname,
102
            'avatarfilename' => null,
103
            'active' => self::PROVIDER_ACTIVE,
104
            'avatarsynced' => 0,
105
        ];
106
        $record->id = $DB->insert_record('communication', $record);
107
 
108
        return new self($record);
109
    }
110
 
111
    /**
112
     * Update the communication instance with any changes.
113
     *
114
     * @param null|string $active Active state of the instance (processor::PROVIDER_ACTIVE or processor::PROVIDER_INACTIVE)
115
     * @param null|string $roomname The room name
116
     */
117
    public function update_instance(
118
        ?string $active = null,
119
        ?string $roomname = null,
120
    ): void {
121
        global $DB;
122
 
123
        if ($active !== null && in_array($active, [self::PROVIDER_ACTIVE, self::PROVIDER_INACTIVE])) {
124
            $this->instancedata->active = $active;
125
        }
126
 
127
        if ($roomname !== null) {
128
            $this->instancedata->roomname = $roomname;
129
        }
130
 
131
        $DB->update_record('communication', $this->instancedata);
132
    }
133
 
134
    /**
135
     * Delete communication data.
136
     */
137
    public function delete_instance(): void {
138
        global $DB;
139
        $DB->delete_records('communication', ['id' => $this->instancedata->id]);
140
    }
141
 
142
    /**
143
     * Get non synced instance user ids for the instance.
144
     *
145
     * @param bool $synced The synced status
146
     * @param bool $deleted The deleted status
147
     * @return array
148
     */
149
    public function get_instance_userids(bool $synced = false, bool $deleted = false): array {
150
        global $DB;
151
        return $DB->get_fieldset_select(
152
            'communication_user',
153
            'userid',
154
            'commid = ? AND synced = ? AND deleted = ?',
155
            [$this->instancedata->id, (int) $synced, (int) $deleted]
156
        );
157
    }
158
 
159
    /**
160
     * Get existing instance user ids.
161
     *
162
     * @return array
163
     */
164
    public function get_all_userids_for_instance(): array {
165
        global $DB;
166
        return $DB->get_fieldset_select(
167
            'communication_user',
168
            'userid',
169
            'commid = ?',
170
            [$this->instancedata->id]
171
        );
172
    }
173
 
174
    /**
175
     * Get all the user ids flagged as deleted.
176
     *
177
     * @return array
178
     */
179
    public function get_all_delete_flagged_userids(): array {
180
        global $DB;
181
        return $DB->get_fieldset_select(
182
            'communication_user',
183
            'userid',
184
            'commid = ? AND deleted = ?',
185
            [$this->instancedata->id, 1]
186
        );
187
    }
188
 
189
    /**
190
     * Create communication user record for mapping and sync.
191
     *
192
     * @param array $userids The user ids
193
     */
194
    public function create_instance_user_mapping(array $userids): void {
195
        global $DB;
196
 
197
        // Check if user ids exits in existing user ids.
198
        $useridstoadd = array_diff($userids, $this->get_all_userids_for_instance());
199
 
200
        foreach ($useridstoadd as $userid) {
201
            $record = (object) [
202
                'commid' => $this->instancedata->id,
203
                'userid' => $userid,
204
            ];
205
            $DB->insert_record('communication_user', $record);
206
        }
207
        $this->mark_users_as_not_deleted($userids);
208
    }
209
 
210
    /**
211
     * Mark users as not deleted for the instance.
212
     *
213
     * @param array $userids The user ids
214
     */
215
    public function mark_users_as_not_deleted(array $userids): void {
216
        global $DB;
217
 
218
        if (empty($userids)) {
219
            return;
220
        }
221
 
222
        $DB->set_field_select(
223
            'communication_user',
224
            'deleted',
225
            0,
226
            'commid = ? AND userid IN (' . implode(',', $userids) . ')',
227
            [$this->instancedata->id]
228
        );
229
    }
230
 
231
    /**
232
     * Mark users as synced for the instance.
233
     *
234
     * @param array $userids The user ids
235
     */
236
    public function mark_users_as_synced(array $userids): void {
237
        global $DB;
238
 
239
        if (empty($userids)) {
240
            return;
241
        }
242
 
243
        $DB->set_field_select(
244
            'communication_user',
245
            'synced',
246
            1,
247
            'commid = ? AND userid IN (' . implode(',', $userids) . ')',
248
            [$this->instancedata->id]
249
        );
250
    }
251
 
252
    /**
253
     * Reset users sync flag for the instance.
254
     *
255
     * @param array $userids The user ids
256
     */
257
    public function reset_users_sync_flag(array $userids): void {
258
        global $DB;
259
 
260
        if (empty($userids)) {
261
            return;
262
        }
263
 
264
        $DB->set_field_select(
265
            'communication_user',
266
            'synced',
267
            0,
268
            'commid = ? AND userid IN (' . implode(',', $userids) . ')',
269
            [$this->instancedata->id]
270
        );
271
    }
272
 
273
    /**
274
     * Delete users flag for the instance users.
275
     *
276
     * @param array $userids The user ids
277
     */
278
    public function add_delete_user_flag(array $userids): void {
279
        global $DB;
280
 
281
        if (empty($userids)) {
282
            return;
283
        }
284
 
285
        $DB->set_field_select(
286
            'communication_user',
287
            'deleted',
288
            1,
289
            'commid = ? AND userid IN (' . implode(',', $userids) . ')',
290
            [$this->instancedata->id]
291
        );
292
    }
293
 
294
    /**
295
     * Delete communication user record for userid.
296
     *
297
     * @param array $userids The user ids
298
     */
299
    public function delete_instance_user_mapping(array $userids): void {
300
        global $DB;
301
 
302
        if (empty($userids)) {
303
            return;
304
        }
305
 
306
        $DB->delete_records_select(
307
            'communication_user',
308
            'commid = ? AND userid IN (' . implode(',', $userids) . ')',
309
            [$this->instancedata->id]
310
        );
311
    }
312
 
313
    /**
314
     * Delete communication user record for userid who are not synced.
315
     *
316
     * @param array $userids The user ids
317
     */
318
    public function delete_instance_non_synced_user_mapping(array $userids): void {
319
        global $DB;
320
 
321
        if (empty($userids)) {
322
            return;
323
        }
324
 
325
        $DB->delete_records_select(
326
            'communication_user',
327
            'commid = ? AND userid IN (' . implode(',', $userids) . ') AND synced = ?',
328
            [$this->instancedata->id, 0]
329
        );
330
    }
331
 
332
    /**
333
     * Delete communication user record for instance.
334
     */
335
    public function delete_user_mappings_for_instance(): void {
336
        global $DB;
337
        $DB->delete_records('communication_user', [
338
            'commid' => $this->instancedata->id,
339
        ]);
340
    }
341
 
342
    /**
343
     * Load communication instance by id.
344
     *
345
     * @param int $id The communication instance id
346
     * @return processor|null
347
     */
348
    public static function load_by_id(int $id): ?self {
349
        global $DB;
350
        $record = $DB->get_record('communication', ['id' => $id]);
351
        if ($record && self::is_provider_available($record->provider)) {
352
            return new self($record);
353
        }
354
 
355
        return null;
356
    }
357
 
358
    /**
359
     * Load communication instance by instance id.
360
     *
361
     * @param context $context The context of the item for the instance
362
     * @param string $component The component name
363
     * @param string $instancetype The instance type
364
     * @param int $instanceid The instance id
365
     * @param string|null $provider The provider type - if null will load for this context's active provider.
366
     * @return processor|null
367
     */
368
    public static function load_by_instance(
369
        context $context,
370
        string $component,
371
        string $instancetype,
372
        int $instanceid,
373
        ?string $provider = null,
374
    ): ?self {
375
 
376
        global $DB;
377
 
378
        $params = [
379
            'contextid' => $context->id,
380
            'instanceid' => $instanceid,
381
            'component' => $component,
382
            'instancetype' => $instancetype,
383
        ];
384
 
385
        if ($provider === null) {
386
            // Fetch the active provider in this context.
387
            $params['active'] = 1;
388
        } else {
389
            // Fetch a specific provider in this context (which may be inactive).
390
            $params['provider'] = $provider;
391
        }
392
 
393
        $record = $DB->get_record('communication', $params);
394
        if ($record && self::is_provider_available($record->provider)) {
395
            return new self($record);
396
        }
397
 
398
        return null;
399
    }
400
 
401
    /**
402
     * Check if communication instance is active.
403
     *
404
     * @return bool
405
     */
406
    public function is_instance_active(): bool {
407
        return $this->instancedata->active;
408
    }
409
 
410
    /**
411
     * Get communication provider class name.
412
     *
413
     * @param string $component The component name.
414
     * @return string
415
     */
416
    private function get_classname_for_provider(string $component): string {
417
        return "{$component}\\communication_feature";
418
    }
419
 
420
    /**
421
     * Get communication instance id after creating the instance in communication table.
422
     *
423
     * @return int
424
     */
425
    public function get_id(): int {
426
        return $this->instancedata->id;
427
    }
428
 
429
    /**
430
     * Get the context of the communication instance.
431
     *
432
     * @return context
433
     */
434
    public function get_context(): context {
435
        return context::instance_by_id($this->get_context_id());
436
    }
437
 
438
    /**
439
     * Get the context id of the communication instance.
440
     *
441
     * @return int
442
     */
443
    public function get_context_id(): int {
444
        return $this->instancedata->contextid;
445
    }
446
 
447
    /**
448
     * Get communication instance type.
449
     *
450
     * @return string
451
     */
452
    public function get_instance_type(): string {
453
        return $this->instancedata->instancetype;
454
    }
455
 
456
    /**
457
     * Get communication instance id.
458
     *
459
     * @return int
460
     */
461
    public function get_instance_id(): int {
462
        return $this->instancedata->instanceid;
463
    }
464
 
465
    /**
466
     * Get communication instance component.
467
     *
468
     * @return string
469
     */
470
    public function get_component(): string {
471
        return $this->instancedata->component;
472
    }
473
 
474
    /**
475
     * Get communication provider type.
476
     *
477
     * @return string|null
478
     */
479
    public function get_provider(): ?string {
480
        return $this->instancedata->provider;
481
    }
482
 
483
    /**
484
     * Get room name.
485
     *
486
     * @return string|null
487
     */
488
    public function get_room_name(): ?string {
489
        return $this->instancedata->roomname;
490
    }
491
 
492
    /**
493
     * Get provider active status.
494
     *
495
     * @return int
496
     */
497
    public function get_provider_status(): int {
498
        return $this->instancedata->active;
499
    }
500
 
501
    /**
502
     * Get communication instance id.
503
     *
504
     * @return room_chat_provider
505
     */
506
    public function get_room_provider(): room_chat_provider {
507
        $this->require_api_enabled();
508
        $this->require_room_features();
509
        return $this->provider;
510
    }
511
 
512
    /**
513
     * Get communication instance id.
514
     *
515
     * @return user_provider
516
     */
517
    public function get_user_provider(): user_provider {
518
        $this->require_api_enabled();
519
        $this->require_user_features();
520
        return $this->provider;
521
    }
522
 
523
    /**
524
     * Get communication instance id.
525
     *
526
     * @return room_user_provider
527
     */
528
    public function get_room_user_provider(): room_user_provider {
529
        $this->require_api_enabled();
530
        $this->require_room_features();
531
        $this->require_room_user_features();
532
        return $this->provider;
533
    }
534
 
535
    /**
536
     * Get the provider after checking if it supports sync features.
537
     *
538
     * @return synchronise_provider
539
     */
540
    public function get_sync_provider(): synchronise_provider {
541
        $this->require_api_enabled();
542
        $this->require_sync_provider_features();
543
        return $this->provider;
544
    }
545
 
546
    /**
547
     * Set provider specific form definition.
548
     *
549
     * @param string $provider The provider name
550
     * @param \MoodleQuickForm $mform The moodle form
551
     */
552
    public static function set_provider_specific_form_definition(string $provider, \MoodleQuickForm $mform): void {
553
        $providerclass = "{$provider}\\communication_feature";
554
        $providerclass::set_form_definition($mform);
555
    }
556
 
557
    /**
558
     * Get communication instance for form feature.
559
     *
560
     * @return form_provider
561
     */
562
    public function get_form_provider(): form_provider {
563
        $this->requires_form_features();
564
        return $this->provider;
565
    }
566
 
567
    /**
568
     * Get communication instance id.
569
     *
570
     * @return bool
571
     */
572
    public function supports_user_features(): bool {
573
        return ($this->provider instanceof user_provider);
574
    }
575
 
576
    /**
577
     * Get communication instance id.
578
     *
579
     * @return bool
580
     */
581
    public function supports_room_user_features(): bool {
582
        if (!$this->supports_user_features()) {
583
            return false;
584
        }
585
 
586
        if (!$this->supports_room_features()) {
587
            return false;
588
        }
589
 
590
        return ($this->provider instanceof room_user_provider);
591
    }
592
 
593
    /**
594
     * Check form feature available.
595
     *
596
     * @return bool
597
     */
598
    public function requires_form_features(): void {
599
        if (!$this->supports_form_features()) {
600
            throw new \coding_exception('Form features are not supported by the provider');
601
        }
602
    }
603
 
604
    /**
605
     * Check support for form feature.
606
     *
607
     * @return bool
608
     */
609
    public function supports_form_features(): bool {
610
        return ($this->provider instanceof form_provider);
611
    }
612
 
613
    /**
614
     * Get communication instance id.
615
     */
616
    public function require_user_features(): void {
617
        if (!$this->supports_user_features()) {
618
            throw new \coding_exception('User features are not supported by the provider');
619
        }
620
    }
621
 
622
    /**
623
     * Get communication instance id.
624
     *
625
     * @return bool
626
     */
627
    public function supports_room_features(): bool {
628
        return ($this->provider instanceof room_chat_provider);
629
    }
630
 
631
    /**
632
     * Check if communication api is enabled.
633
     */
634
    public function require_api_enabled(): void {
635
        if (!api::is_available()) {
636
            throw new \coding_exception('Communication API is not enabled, please enable it from experimental features');
637
        }
638
    }
639
 
640
    /**
641
     * Get communication instance id.
642
     */
643
    public function require_room_features(): void {
644
        if (!$this->supports_room_features()) {
645
            throw new \coding_exception('room features are not supported by the provider');
646
        }
647
    }
648
 
649
    /**
650
     * Get communication instance id.
651
     */
652
    public function require_room_user_features(): void {
653
        if (!$this->supports_room_user_features()) {
654
            throw new \coding_exception('room features are not supported by the provider');
655
        }
656
    }
657
 
658
    /**
659
     * Check if the provider supports sync features.
660
     *
661
     * @return bool whether the provider supports sync features or not
662
     */
663
    public function supports_sync_provider_features(): bool {
664
        return ($this->provider instanceof synchronise_provider);
665
    }
666
 
667
    /**
668
     * Check if the provider supports sync features when required.
669
     */
670
    public function require_sync_provider_features(): void {
671
        if (!$this->supports_sync_provider_features()) {
672
            throw new \coding_exception('sync features are not supported by the provider');
673
        }
674
    }
675
 
676
    /**
677
     * Get communication instance id.
678
     *
679
     * @return bool|\stored_file
680
     */
681
    public function get_avatar(): ?stored_file {
682
        $fs = get_file_storage();
683
        $file = $fs->get_file(
684
            (\context_system::instance())->id,
685
            'core_communication',
686
            'avatar',
687
            $this->instancedata->id,
688
            '/',
689
            $this->instancedata->avatarfilename,
690
        );
691
 
692
        return $file ?: null;
693
    }
694
 
695
 
696
    /**
697
     * Set the avatar file name.
698
     *
699
     * @param string|null $filename
700
     */
701
    public function set_avatar_filename(?string $filename): void {
702
        global $DB;
703
 
704
        $this->instancedata->avatarfilename = $filename;
705
        $DB->set_field('communication', 'avatarfilename', $filename, ['id' => $this->instancedata->id]);
706
    }
707
 
708
    /**
709
     * Get the avatar file name.
710
     *
711
     * @return string|null
712
     */
713
    public function get_avatar_filename(): ?string {
714
        return $this->instancedata->avatarfilename;
715
    }
716
 
717
    /**
718
     * Check if the avatar has been synced with the provider.
719
     *
720
     * @return bool
721
     */
722
    public function is_avatar_synced(): bool {
723
        return (bool) $this->instancedata->avatarsynced;
724
    }
725
 
726
    /**
727
     * Indicate if the avatar has been synced with the provider.
728
     *
729
     * @param boolean $synced True if avatar has been synced.
730
     */
731
    public function set_avatar_synced_flag(bool $synced): void {
732
        global $DB;
733
 
734
        $this->instancedata->avatarsynced = (int) $synced;
735
        $DB->set_field('communication', 'avatarsynced', (int) $synced, ['id' => $this->instancedata->id]);
736
    }
737
 
738
    /**
739
     * Get a room url.
740
     *
741
     * @return string|null
742
     */
743
    public function get_room_url(): ?string {
744
        if ($this->provider && $this->is_instance_active()) {
745
            return $this->get_room_provider()->get_chat_room_url();
746
        }
747
        return null;
748
    }
749
 
750
    /**
751
     * Is the communication provider enabled and configured, or disabled.
752
     *
753
     * @param string $provider provider component name
754
     * @return bool
755
     */
756
    public static function is_provider_available(string $provider): bool {
757
        if (\core\plugininfo\communication::is_plugin_enabled($provider)) {
758
            $providerclass = "{$provider}\\communication_feature";
759
            return $providerclass::is_configured();
760
        }
761
        return false;
762
    }
763
}