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