ILIAS  release_8 Revision v8.23
class.ilRemoteObjectBase.php
Go to the documentation of this file.
1 <?php
2 
18 declare(strict_types=1);
19 
25 abstract class ilRemoteObjectBase extends ilObject2
26 {
27  protected ?string $local_information = null;
28  protected ?string $remote_link = null;
29  protected ?string $organization = null;
30  protected ?int $mid = null;
31  protected string $auth_hash = '';
32 
33  protected string $realm_plain = '';
34 
35  private ilLogger $logger;
36 
37  public const MAIL_SENDER = 6;
38  public const OBJECT_OWNER = 6;
39 
40  public function __construct(int $a_id = 0, bool $a_call_by_reference = true)
41  {
42  global $DIC;
43 
44  parent::__construct($a_id, $a_call_by_reference);
45 
46  $this->logger = $DIC->logger()->wsrv();
47  }
48 
54  public static function getInstanceByEventType(string $a_type)
55  {
56  switch ($a_type) {
58  return new ilObjRemoteCourse();
59 
61  return new ilObjRemoteCategory();
62 
64  return new ilObjRemoteFile();
65 
67  return new ilObjRemoteGlossary();
68 
70  return new ilObjRemoteGroup();
71 
73  return new ilObjRemoteLearningModule();
74 
76  return new ilObjRemoteWiki();
77 
79  return new ilObjRemoteTest();
80 
81  default:
82  return null;
83  }
84  }
85 
86  protected function beforeCreate(): bool
87  {
88  $this->setOwner(self::OBJECT_OWNER);
89  return parent::beforeCreate();
90  }
91 
95  abstract protected function getTableName(): string;
96 
100  abstract protected function getECSObjectType(): string;
101 
102 
106  public function getRealmPlain(): string
107  {
108  return $this->realm_plain;
109  }
110 
114  public function setOrganization(string $a_organization): void
115  {
116  $this->organization = $a_organization;
117  }
118 
122  public function getOrganization(): string
123  {
124  return $this->organization;
125  }
126 
130  public function getLocalInformation(): string
131  {
132  return $this->local_information ?? '';
133  }
134 
140  public function setLocalInformation(string $a_info): void
141  {
142  $this->local_information = $a_info;
143  }
144 
148  public function getMID(): int
149  {
150  return $this->mid;
151  }
152 
158  public function setMID(int $a_mid): void
159  {
160  $this->mid = $a_mid;
161  }
162 
168  public function setRemoteLink(string $a_link): void
169  {
170  $this->remote_link = $a_link;
171  }
172 
176  public function getRemoteLink(): string
177  {
178  return $this->remote_link;
179  }
180 
187  public function getFullRemoteLink(): string
188  {
189  $server_id = ilECSImportManager::getInstance()->lookupServerId($this->getId());
191  $setting = ilECSParticipantSetting::getInstance($server_id, $this->mid);
192  $user = new ilECSUser($this->user);
193  $ecs_user_data = $user->toGET($setting);
194  $this->logger->info(__METHOD__ . ': Using ecs user data ' . $ecs_user_data);
195 
196  // check token mechanism enabled
197  $part = new ilECSParticipantSetting($server_id, $this->getMID());
198  if (!$part->isTokenEnabled()) {
199  return $this->getRemoteLink();
200  }
201 
202  $auth_hash = $this->createAuthResource($this->getRemoteLink() . $user->toREALM());
203  $ecs_url_hash = 'ecs_hash_url=' . urlencode($server->getServerURI() . '/sys/auths/' . $auth_hash);
204 
205  if (strpos($this->getRemoteLink(), '?')) {
206  $link = $this->getRemoteLink() . '&ecs_hash=' . $auth_hash . $ecs_user_data . '&' . $ecs_url_hash;
207  } else {
208  $link = $this->getRemoteLink() . '?ecs_hash=' . $auth_hash . $ecs_user_data . '&' . $ecs_url_hash;
209  }
210  $this->logger->info(__METHOD__ . ': ECS full link: ' . $link);
211  return $link;
212  }
213 
217  public function createAuthResource(string $a_plain_realm)
218  {
219  try {
220  $server_id = ilECSImportManager::getInstance()->lookupServerId($this->getId());
221  $import_info = new ilECSImport($server_id, $this->getId());
222 
223  $connector = new ilECSConnector(ilECSSetting::getInstanceByServerId($server_id));
224  $auth = new ilECSAuth();
225  $auth->setPid($import_info->getMID());
226  //TODO URL is deprecated
227  $auth->setUrl($this->getRemoteLink());
228  $realm = sha1($a_plain_realm);
229  $this->logger->info(__METHOD__ . ': Using realm ' . $a_plain_realm);
230  $auth->setRealm($realm);
231  $this->logger->info(__METHOD__ . ' Mid is ' . $this->getMID());
232  //TODO remove @
233  $this->auth_hash = $connector->addAuth(json_encode($auth, JSON_THROW_ON_ERROR), $this->getMID());
234  return $this->auth_hash;
235  } catch (ilECSConnectorException $exc) {
236  $this->logger->info(__METHOD__ . ': Caught error from ECS Auth resource: ' . $exc->getMessage());
237  return false;
238  }
239  }
240 
241  protected function doCreate(bool $clone_mode = false): void
242  {
243  $fields = array(
244  "obj_id" => array("integer", $this->getId()),
245  "local_information" => array("text", ""),
246  "remote_link" => array("text", ""),
247  "mid" => array("integer", 0),
248  "organization" => array("text", "")
249  );
250 
251  $this->doCreateCustomFields($fields);
252 
253  $this->db->insert($this->getTableName(), $fields);
254  }
255 
259  protected function doCreateCustomFields(array &$a_fields): void
260  {
261  }
262 
266  protected function doUpdate(): void
267  {
268  $fields = array(
269  "local_information" => array("text", $this->getLocalInformation()),
270  "remote_link" => array("text", $this->getRemoteLink()),
271  "mid" => array("integer", $this->getMID()),
272  "organization" => array("text", $this->getOrganization())
273  );
274 
275  $this->doUpdateCustomFields($fields);
276 
277  $where = array("obj_id" => array("integer", $this->getId()));
278 
279  $this->db->update($this->getTableName(), $fields, $where);
280  }
281 
285  protected function doUpdateCustomFields(array &$a_fields): void
286  {
287  }
288 
292  protected function doDelete(): void
293  {
294  //put here your module specific stuff
295  ilECSImportManager::getInstance()->_deleteByObjId($this->getId());
296 
297  $query = "DELETE FROM " . $this->getTableName() .
298  " WHERE obj_id = " . $this->db->quote($this->getId(), 'integer') . " ";
299  $this->db->manipulate($query);
300  }
301 
302  protected function doRead(): void
303  {
304  $query = "SELECT * FROM " . $this->getTableName() .
305  " WHERE obj_id = " . $this->db->quote($this->getId(), 'integer') . " ";
306  $res = $this->db->query($query);
307  while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
308  if (!is_null($row->local_information)) {
309  $this->setLocalInformation($row->local_information);
310  }
311  if (!is_null($row->remote_link)) {
312  $this->setRemoteLink($row->remote_link);
313  }
314  $this->setMID((int) $row->mid);
315  if (!is_null($row->organization)) {
316  $this->setOrganization($row->organization);
317  }
318  $this->doReadCustomFields($row);
319  }
320  }
321 
325  protected function doReadCustomFields(object $a_row): void
326  {
327  }
328 
336  public function createFromECSEContent(ilECSSetting $a_server, object $a_ecs_content, int $a_owner): void
337  {
338  $this->create();
339  $this->log->info("Done calling create, creating reference");
340  // won't work for personal workspace
341  $this->createReference();
342  $this->log->info("Done creating reference, setting permissions");
343  $this->setPermissions($a_server->getImportId());
344  $this->log->info("Done setting permissions, putting object in tree");
345 
346  $matchable_content = ilECSUtils::getMatchableContent(
347  $this->getECSObjectType(),
348  $a_server->getServerId(),
349  $a_ecs_content,
350  $a_owner
351  );
352 
354  $a_server->getServerId(),
355  $matchable_content
356  ));
357  $this->log->info("Done putting object in tree, updateing object");
358 
359  $this->updateFromECSContent($a_server, $a_ecs_content, $a_owner);
360  }
361 
367  public function updateFromECSContent(ilECSSetting $a_server, object $a_ecs_content, int $a_owner): void
368  {
369  $this->logger->info('updateFromECSContent: ' . print_r($a_ecs_content, true));
370 
371  // Get organisation for owner (ObjectListGUI performance)
372  $organisation = null;
373  if ($a_owner) {
374  $organisation = ilECSCommunityReader::getInstanceByServerId($a_server->getServerId())
375  ->getParticipantNameByMid($a_owner);
376  $this->logger->info('found organisation: ' . $organisation);
377  }
378 
379  $this->setMID($a_owner); // obsolete?
380  $this->setOrganization($organisation);
381  $this->setTitle($a_ecs_content->title);
382  if (!is_null($a_ecs_content->abstract)) {
383  $this->setDescription($a_ecs_content->abstract);
384  }
385  $this->setRemoteLink($a_ecs_content->url);
386 
387  $this->logger->info('updateCustomFromECSContent');
388  $this->updateCustomFromECSContent($a_server, $a_ecs_content);
389 
390  // we are updating late so custom values can be set
391 
392  $this->logger->info('ilObject->update()');
393  $this->update();
394 
395  $matchable_content = ilECSUtils::getMatchableContent(
396  $this->getECSObjectType(),
397  $a_server->getServerId(),
398  $a_ecs_content,
399  $a_owner
400  );
401 
402  // rule-based category mapping
404  $this->getId(),
405  $a_server->getServerId(),
406  $matchable_content
407  );
408  }
409 
414  protected function importMetadataFromJson(object $a_json, ilECSSetting $a_server, array $a_definition, int $a_mapping_mode): void
415  {
416  $this->logger->info("importing metadata from json: " . print_r($a_json, true));
417 
419  $values_records = ilAdvancedMDValues::getInstancesForObjectId($this->getId(), $this->getType());
420  foreach ($values_records as $values_record) {
421  // this correctly binds group and definitions
422  $values_record->read();
423  }
424 
425  $do_save = false;
426 
427  foreach ($a_definition as $id => $type) {
428  if (is_array($type)) {
429  [$type, $target] = $type;
430  } else {
431  $target = $id;
432  }
433 
434  $timePlace = null;
435  if ($field = $mappings->getMappingByECSName($a_mapping_mode, $id)) {
436  // find element in records
437  $adv_md_def = null;
438  foreach ($values_records as $values_record) {
439  $adv_md_defs = $values_record->getDefinitions();
440  if (isset($adv_md_defs[$field])) {
441  $adv_md_def = $adv_md_defs[$field];
442  break;
443  }
444  }
445  if (!$adv_md_def) {
446  continue;
447  }
448 
449  $raw_value = $a_json->{$target};
450 
452  if (!is_object($timePlace)) {
453  $timePlace = new ilECSTimePlace();
454  if (is_object($raw_value)) {
455  $timePlace->loadFromJSON($raw_value);
456  }
457  }
458  $raw_value = $timePlace;
459  }
460 
461  if ($adv_md_def->importFromECS((string) $type, (string) $raw_value, $id)) {
462  $do_save = true;
463  }
464  }
465  }
466 
467  if ($do_save) {
468  foreach ($values_records as $values_record) {
469  $additional = array();
470  foreach ($values_record->getADTGroup()->getElements() as $element_id => $element) {
471  if (!$element->isNull()) {
472  $additional[$element_id] = array("disabled" => array("integer", 1));
473  }
474  }
475  $values_record->write($additional);
476  }
477  }
478  }
479 
486  protected function updateCustomFromECSContent(ilECSSetting $a_server, $ecs_content): void
487  {
488  }
489 
495  public function isLocalObject(): bool
496  {
497  if (ilECSExportManager::getInstance()->_isRemote(
498  ilECSImportManager::getInstance()->lookupServerId($this->getId()),
499  (int) ilECSImportManager::getInstance()->_lookupEContentId($this->getId())
500  )) {
501  return false;
502  }
503  return true;
504  }
505 
511  public function handleCreate(ilECSSetting $a_server, int $a_econtent_id, array $a_mids): bool
512  {
513  return $this->handleUpdate($a_server, $a_econtent_id, $a_mids);
514  }
515 
521  public function handleUpdate(ilECSSetting $a_server, int $a_econtent_id, array $a_mids): bool
522  {
523  // get content details
525  $a_server->getServerId(),
526  $a_econtent_id,
527  $this->getECSObjectType()
528  );
529  if (!$details instanceof ilECSEContentDetails) {
530  $this->handleDelete($a_server, $a_econtent_id);
531  $this->logger->info(__METHOD__ . ': Handling delete of deprecated remote object. DONE');
532  return true;
533  }
534 
535  $this->logger->info(__METHOD__ . ': Receivers are ' . print_r($details->getReceivers(), true));
536  $this->logger->info(__METHOD__ . ': Senders are ' . print_r($details->getSenders(), true));
537 
538  // check owner (sender mid)
539  if (!ilECSParticipantSettings::getInstanceByServerId($a_server->getServerId())->isImportAllowed($details->getSenders())) {
540  $this->logger->info('Ignoring disabled participant. MID: ' . $details->getOwner());
541  return true;
542  }
543 
544  // new mids
545  foreach (array_intersect($a_mids, $details->getReceivers()) as $mid) {
546  try {
547  $connector = new ilECSConnector($a_server);
548  $res = $connector->getResource($this->getECSObjectType(), $a_econtent_id);
549  if ($res->getHTTPCode() === ilECSConnector::HTTP_CODE_NOT_FOUND) {
550  continue;
551  }
552  $json = $res->getResult();
553  $this->logger->info(__METHOD__ . ': Received json: ' . print_r($json, true));
554  if (!is_object($json)) {
555  // try as array (workaround for invalid content)
556  $json = $json[0];
557  if (!is_object($json)) {
558  throw new ilECSConnectorException('invalid json');
559  }
560  }
561  } catch (ilECSConnectorException $exc) {
562  $this->logger->error(__METHOD__ . ': Error parsing result. ' . $exc->getMessage());
563  $this->logger->logStack(ilLogLevel::ERROR);
564  return false;
565  }
566 
567  // Update existing
568 
569  // Check receiver mid
570  if ($obj_id = ilECSImportManager::getInstance()->_isImported($a_server->getServerId(), (string) $a_econtent_id, $mid)) {
571  $this->logger->info(__METHOD__ . ': Handling update for existing object');
572  $remote = ilObjectFactory::getInstanceByObjId($obj_id, false);
573  if (!$remote instanceof self) {
574  $this->logger->info(__METHOD__ . ': Cannot instantiate remote object. Got object type ' . $remote->getType());
575  continue;
576  }
577  $remote->updateFromECSContent($a_server, $json, $details->getMySender());
578  } else {
579  $this->logger->info(__METHOD__ . ': my sender ' . $details->getMySender() . 'vs mid' . $mid);
580 
581  $this->logger->info(__METHOD__ . ': Handling create for non existing object');
582  $this->createFromECSEContent($a_server, $json, $details->getMySender());
583 
584  // update import status
585  $this->logger->info(__METHOD__ . ': Updating import status');
586  $import = new ilECSImport($a_server->getServerId(), $this->getId());
587  $import->setEContentId((string) $a_econtent_id);
588  // Store receiver mid
589  $import->setMID($mid);
590  $import->save();
591 
592  $this->logger->info(__METHOD__ . ': Sending notification');
593  $this->sendNewContentNotification($a_server->getServerId());
594  }
595  }
596 
597  $this->logger->info(__METHOD__ . ': done');
598  return true;
599  }
600 
604  protected function sendNewContentNotification($a_server_id): void
605  {
607  if (!count($settings->getEContentRecipients())) {
608  return;
609  }
610 
612  $lang->loadLanguageModule('ecs');
613 
614  $mail = new ilMail(self::MAIL_SENDER);
615  $message = $lang->txt('ecs_' . $this->getType() . '_created_body_a') . "\n\n";
616  $message .= $lang->txt('title') . ': ' . $this->getTitle() . "\n";
617  if ($desc = ($this->getDescription() !== '')) {
618  $message .= $lang->txt('desc') . ': ' . $desc . "\n";
619  }
620 
621  $href = ilLink::_getStaticLink($this->getRefId(), $this->getType(), true);
622  $message .= $lang->txt("perma_link") . ': ' . $href . "\n\n";
624 
625  $mail->enqueue(
626  $settings->getEContentRecipientsAsString(),
627  '',
628  '',
629  $lang->txt('ecs_new_econtent_subject'),
630  $message,
631  array()
632  );
633  }
634 
640  public function handleDelete(ilECSSetting $a_server, int $a_econtent_id, $a_mid = 0): bool
641  {
642  // there is no information about the original mid anymore.
643  // Therefor delete any remote objects with given econtent id
644  $obj_ids = ilECSImportManager::getInstance()->_lookupObjIds($a_server->getServerId(), $a_econtent_id);
645  $this->logger->info(__METHOD__ . ': Received obj_ids ' . print_r($obj_ids, true));
646 
647  foreach ($obj_ids as $obj_id) {
648  $references = ilObject::_getAllReferences($obj_id);
649  foreach ($references as $ref_id) {
650  if ($tmp_obj = ilObjectFactory::getInstanceByRefId($ref_id, false)) {
651  $this->logger->info(__METHOD__ . ': Deleting obsolete remote course: ' . $tmp_obj->getTitle());
652  $this->logger->info(print_r($this->tree->getNodeData($ref_id), true));
653  $this->logger->info(print_r($this->tree->getNodeData($tmp_obj->getId()), true));
654  $this->tree->deleteTree($this->tree->getNodeData($ref_id));
655  $tmp_obj->delete();
656  }
657  unset($tmp_obj);
658  }
659  }
660  return true;
661  }
662 
666  public function getAllResourceIds(ilECSSetting $a_server, bool $a_sender_only = false): array
667  {
668  try {
669  $connector = new ilECSConnector($a_server);
670  $connector->addHeader('X-EcsQueryStrings', $a_sender_only ? 'sender=true' : 'all=true'); // #11301
671  $list = $connector->getResourceList($this->getECSObjectType());
672  if ($list instanceof ilECSResult) {
673  return $list->getResult()->getLinkIds();
674  }
675  } catch (ilECSConnectorException $exc) {
676  $this->logger->error(__METHOD__ . ': Error getting resource list for type . ' . $this->getECSObjectType() . ' with message: ' . $exc->getMessage());
677  }
678 
679  return [];
680  }
681 }
static getInstancesForObjectId(int $a_obj_id, ?string $a_obj_type=null, string $a_sub_type="-", int $a_sub_id=0)
Remote test app class.
static getInstance(int $a_server_id, int $mid)
Get instance by server id and mid.
doDelete()
Delete remote object.
$res
Definition: ltiservices.php:69
sendNewContentNotification($a_server_id)
send notifications about new EContent
string $type
static getInstanceByServerId(int $a_server_id)
Get singleton instance.
array $settings
Setting values (LTI parameters, custom parameters and local parameters).
Definition: System.php:200
getTableName()
Get db table name.
getOrganization()
get organization
doCreateCustomFields(array &$a_fields)
Add custom fields to db insert.
setRemoteLink(string $a_link)
set remote link
getRemoteLink()
get remote link
static getInstanceByServerId(int $a_server_id)
Get instance by server id.
Representation of ECS EContent Time Place.
Remote file app class.
createAuthResource(string $a_plain_realm)
create authentication resource on ecs server
Remote glossary app class.
setOrganization(string $a_organization)
set organization
getServerId()
Get current server id.
string $desc
static getInstance()
Get the singelton instance of this ilECSExportManager.
doUpdateCustomFields(array &$a_fields)
Add custom fields to db update.
static _getAllReferences(int $id)
get all reference ids for object ID
updateFromECSContent(ilECSSetting $a_server, object $a_ecs_content, int $a_owner)
update remote object settings from ecs content
doReadCustomFields(object $a_row)
Read custom fields from db row.
updateCustomFromECSContent(ilECSSetting $a_server, $ecs_content)
update remote object settings from ecs content
static getInstanceByEventType(string $a_type)
Get instance by ilECSEvent(QueueReader) type.
setTitle(string $title)
Presentation of ecs content details (http://...campusconnect/courselinks/id/details) ...
Remote learning module app class.
getImportId()
get import id
getECSObjectType()
Get ECS resource identifier, e.g.
getRealmPlain()
Get realm plain.
setPermissions(int $parent_ref_id)
__construct(int $a_id=0, bool $a_call_by_reference=true)
Remote wiki app class.
importMetadataFromJson(object $a_json, ilECSSetting $a_server, array $a_definition, int $a_mapping_mode)
Add advanced metadata to json (export)
setLocalInformation(string $a_info)
set local information
isLocalObject()
Is remote object from same installation?
const TYPE_TIMEPLACE
getAllResourceIds(ilECSSetting $a_server, bool $a_sender_only=false)
Get all available resources.
global $DIC
Definition: feed.php:28
$auth
Definition: metadata.php:76
doCreate(bool $clone_mode=false)
handleUpdate(ilECSSetting $a_server, int $a_econtent_id, array $a_mids)
Handle update event.
array $details
Details for error message relating to last request processed.
Definition: System.php:109
createReference()
creates reference for object
setDescription(string $desc)
static getInstance()
Get the singleton instance of this ilECSImportManager.
create()
note: title, description and type should be set when this function is called
static getInstanceByServerId(int $a_server_id)
Get instance by server id.
static getInstanceByServerId(int $a_server_id)
Get singleton instance per server.
static getInstanceByRefId(int $ref_id, bool $stop_on_error=true)
get an instance of an Ilias object by reference id
getFullRemoteLink()
get full remote link Including ecs generated hash and auth mode
Storage of ECS imported objects.
handleCreate(ilECSSetting $a_server, int $a_econtent_id, array $a_mids)
Handle creation.
$query
static _getLanguage(string $a_lang_key='')
Get language object.
Remote object app base class.
Remote course app class.
$additional
Definition: goto.php:53
static getMatchingCategory(int $a_server_id, array $a_matchable_content)
get matching category
setMID(int $a_mid)
set mid
Remote group app class.
$lang
Definition: xapiexit.php:26
handleDelete(ilECSSetting $a_server, int $a_econtent_id, $a_mid=0)
Handle delete event.
static getInstanceByObjId(?int $obj_id, bool $stop_on_error=true)
get an instance of an Ilias object by object id
putInTree(int $parent_ref_id)
maybe this method should be in tree object!?
$server
__construct(Container $dic, ilPlugin $plugin)
static getInstanceFromServer(int $a_server_id, int $a_econtent_id, string $a_resource_type)
Get data from server.
static _getAutoGeneratedMessageString(ilLanguage $lang=null)
setEContentId($a_id)
set econtent id
doUpdate()
Update remote object.
$message
Definition: xapiexit.php:32
createFromECSEContent(ilECSSetting $a_server, object $a_ecs_content, int $a_owner)
create remote object from ECSContent object
Remote category app class.
static handleUpdate(int $a_obj_id, int $a_server_id, array $a_matchable_content)
Handle update of ecs content and create references.
Stores relevant user data.
setOwner(int $usr_id)
getLocalInformation()
get local information
ilObjUser $user
static getMatchableContent(string $a_resource_id, int $a_server_id, object $a_ecs_content, int $a_owner)
Convert ECS content to rule matchable values.