ILIAS  release_8 Revision v8.19
All Data Structures Namespaces Files Functions Variables Modules Pages
class.ilECSConnector.php
Go to the documentation of this file.
1 <?php
2 
18 declare(strict_types=1);
19 
25 {
26  public const HTTP_CODE_CREATED = 201;
27  public const HTTP_CODE_OK = 200;
28  public const HTTP_CODE_NOT_FOUND = 404;
29 
30  public const HEADER_MEMBERSHIPS = 'X-EcsReceiverMemberships';
31  public const HEADER_COMMUNITIES = 'X-EcsReceiverCommunities';
32 
33 
34  protected string $path_postfix = '';
35 
36  protected ?ilECSSetting $settings = null;
37  protected ?ilCurlConnection $curl = null;
38 
39  protected array $header_strings = [];
40 
41  protected ilLogger $logger;
42 
43  public function __construct(ilECSSetting $settings = null)
44  {
45  global $DIC;
46 
47  $this->logger = $DIC->logger()->wsrv();
48  if ($settings) {
49  $this->settings = $settings;
50  } else {
51  $this->logger->warning('Using deprecated call.');
52  $this->logger->logStack(ilLogLevel::WARNING);
53  }
54  }
55 
56  // Header methods
62  public function addHeader(string $a_name, string $a_value): void
63  {
64  $this->header_strings[] = ($a_name . ': ' . $a_value);
65  }
66 
67  public function getHeader(): array
68  {
69  return $this->header_strings;
70  }
71 
72  public function setHeader(array $a_header_strings): void
73  {
74  $this->header_strings = $a_header_strings;
75  }
76 
80  public function getServer(): ilECSSetting
81  {
82  return $this->settings;
83  }
84 
85 
87  // auths methods
89 
99  public function addAuth(string $a_post, int $a_target_mid): string
100  {
101  $this->logger->info(__METHOD__ . ': Add new Auth resource...');
102 
103  $this->path_postfix = '/sys/auths';
104 
105  try {
106  $this->prepareConnection();
107 
108  $this->addHeader('Content-Type', 'application/json');
109  $this->addHeader('Accept', 'application/json');
110  $this->addHeader(self::HEADER_MEMBERSHIPS, (string) $a_target_mid);
111 
112  $this->curl->setOpt(CURLOPT_HTTPHEADER, $this->getHeader());
113  $this->curl->setOpt(CURLOPT_POST, true);
114  $this->curl->setOpt(CURLOPT_POSTFIELDS, $a_post);
115  $ret = $this->call();
116 
117  $info = $this->curl->getInfo(CURLINFO_HTTP_CODE);
118 
119  $this->logger->info(__METHOD__ . ': Checking HTTP status...');
120  if ($info !== self::HTTP_CODE_CREATED) {
121  $this->logger->info(__METHOD__ . ': Cannot create auth resource, did not receive HTTP 201. ');
122  $this->logger->info(__METHOD__ . ': POST was: ' . $a_post);
123  $this->logger->info(__METHOD__ . ': HTTP code: ' . $info);
124  throw new ilECSConnectorException('Received HTTP status code: ' . $info);
125  }
126  $this->logger->info(__METHOD__ . ': ... got HTTP 201 (created)');
127  $this->logger->info(__METHOD__ . ': POST was: ' . $a_post);
128 
129  $result = new ilECSResult($ret);
130  $auth = $result->getResult();
131 
132  $this->logger->info(__METHOD__ . ': ... got hash: ' . $auth->hash);
133 
134  return $auth->hash;
135  } catch (ilCurlConnectionException $exc) {
136  throw new ilECSConnectorException('Error calling ECS service: ' . $exc->getMessage());
137  }
138  }
139 
146  public function getAuth(string $a_hash, bool $a_details_only = false)
147  {
148  if ($a_hash === '') {
149  $this->logger->error(__METHOD__ . ': No auth hash given. Aborting.');
150  throw new ilECSConnectorException('No auth hash given.');
151  }
152 
153  $this->path_postfix = '/sys/auths/' . $a_hash;
154 
155  if ($a_details_only) {
156  $this->path_postfix .= ('/details');
157  }
158 
159 
160  try {
161  $this->prepareConnection();
162  $res = $this->call();
163  $info = $this->curl->getInfo(CURLINFO_HTTP_CODE);
164 
165  $this->logger->info(__METHOD__ . ': Checking HTTP status...');
166  if ($info !== self::HTTP_CODE_OK) {
167  $this->logger->info(__METHOD__ . ': Cannot get auth resource, did not receive HTTP 200. ');
168  throw new ilECSConnectorException('Received HTTP status code: ' . $info);
169  }
170  $this->logger->info(__METHOD__ . ': ... got HTTP 200 (ok)');
171 
172  $ecs_result = new ilECSResult($res);
173  // Return ECSEContentDetails for details switch
174  if ($a_details_only) {
176  $details->loadFromJson($ecs_result->getResult());
177  return $details;
178  }
179  return $ecs_result;
180  } catch (ilCurlConnectionException $exc) {
181  throw new ilECSConnectorException('Error calling ECS service: ' . $exc->getMessage());
182  }
183  }
184 
185  #######################################################
186  # event fifo methods
187  #####################################################
188 
194  public function readEventFifo(bool $a_delete = false): ilECSResult
195  {
196  $this->path_postfix = '/sys/events/fifo';
197 
198  try {
199  $this->prepareConnection();
200  $this->addHeader('Content-Type', 'application/json');
201  $this->addHeader('Accept', 'application/json');
202 
203  if ($a_delete) {
204  $this->curl->setOpt(CURLOPT_POST, true);
205  $this->curl->setOpt(CURLOPT_POSTFIELDS, '');
206  }
207  $res = $this->call();
208 
209  // Checking status code
210  $info = $this->curl->getInfo(CURLINFO_HTTP_CODE);
211  if ($info !== self::HTTP_CODE_OK) {
212  $this->logger->info(__METHOD__ . ': Cannot read event fifo, did not receive HTTP 200. ');
213  throw new ilECSConnectorException('Received HTTP status code: ' . $info);
214  }
215  //TODO check if this return needs to be moved after the finally
216  return new ilECSResult($res);
217  } catch (ilCurlConnectionException $exc) {
218  throw new ilECSConnectorException('Error calling ECS service: ' . $exc->getMessage());
219  } finally {
220  $this->curl->close();
221  }
222  }
223 
225  // econtents methods
227 
228  public function getResourceList(string $a_path): ilECSResult
229  {
230  $this->path_postfix = $a_path;
231 
232  try {
233  $this->prepareConnection();
234  $this->curl->setOpt(CURLOPT_HTTPHEADER, $this->getHeader());
235  $res = $this->call();
236 
237  // Checking status code
238  $info = $this->curl->getInfo(CURLINFO_HTTP_CODE);
239  $this->logger->debug(__METHOD__ . ': Checking HTTP status...');
240  if ($info !== self::HTTP_CODE_OK) {
241  $this->logger->debug(__METHOD__ . ': Cannot get ressource list, did not receive HTTP 200. ');
242  throw new ilECSConnectorException('Received HTTP status code: ' . $info);
243  }
244  $this->logger->debug(__METHOD__ . ': ... got HTTP 200 (ok)');
245 
247  } catch (ilCurlConnectionException $exc) {
248  throw new ilECSConnectorException('Error calling ECS service: ' . $exc->getMessage());
249  }
250  }
251 
252 
258  public function getResource(string $a_path, int $a_econtent_id, bool $a_details_only = false): ilECSResult
259  {
260  // TODO make handling of a_econtent_id explict like setting it to null
261  if ($a_econtent_id) {
262  $this->logger->debug(__METHOD__ . ': Get resource with ID: ' . $a_econtent_id);
263  } else {
264  $this->logger->debug(__METHOD__ . ': Get all resources ...');
265  }
266 
267  $this->path_postfix = $a_path;
268  if ($a_econtent_id) {
269  $this->path_postfix .= ('/' . $a_econtent_id);
270  }
271  if ($a_details_only) {
272  $this->path_postfix .= ('/details');
273  }
274 
275  try {
276  $this->prepareConnection();
277  $res = $this->call();
278 
279  // Checking status code
280  $info = (int) $this->curl->getInfo(CURLINFO_HTTP_CODE);
281  $this->logger->debug(__METHOD__ . ': Checking HTTP status...');
282  if ($info !== self::HTTP_CODE_OK) {
283  $this->logger->debug(__METHOD__ . ': Cannot get ressource, did not receive HTTP 200. ');
284  throw new ilECSConnectorException('Received HTTP status code: ' . $info);
285  }
286  $this->logger->debug(__METHOD__ . ': ... got HTTP 200 (ok)');
287 
288  $result = new ilECSResult($res);
289  $result->setHeaders($this->curl->getResponseHeaderArray());
290  $result->setHTTPCode($info);
291 
292  return $result;
293  } catch (ilCurlConnectionException $exc) {
294  throw new ilECSConnectorException('Error calling ECS service: ' . $exc->getMessage());
295  }
296  }
297 
308  public function addResource(string $a_path, $a_post): int
309  {
310  $this->logger->info(__METHOD__ . ': Add new EContent...');
311 
312  $this->path_postfix = $a_path;
313 
314  try {
315  $this->prepareConnection();
316 
317  $this->addHeader('Content-Type', 'application/json');
318 
319  $this->curl->setOpt(CURLOPT_HTTPHEADER, $this->getHeader());
320  $this->curl->setOpt(CURLOPT_HEADER, true);
321  $this->curl->setOpt(CURLOPT_POST, true);
322  $this->curl->setOpt(CURLOPT_POSTFIELDS, $a_post);
323  $this->call();
324 
325  $info = $this->curl->getInfo(CURLINFO_HTTP_CODE);
326 
327  $this->logger->debug(__METHOD__ . ': Checking HTTP status...');
328  if ($info !== self::HTTP_CODE_CREATED) {
329  $this->logger->debug(__METHOD__ . ': Cannot create econtent, did not receive HTTP 201. ');
330  throw new ilECSConnectorException('Received HTTP status code: ' . $info);
331  }
332  $this->logger->debug(__METHOD__ . ': ... got HTTP 201 (created)');
333 
334  return $this->_fetchEContentIdFromHeader($this->curl->getResponseHeaderArray());
335  } catch (ilCurlConnectionException $exc) {
336  throw new ilECSConnectorException('Error calling ECS service: ' . $exc->getMessage());
337  }
338  }
339 
348  public function updateResource(string $a_path, int $a_econtent_id, string $a_post_string): void
349  {
350  $this->logger->debug(__METHOD__ . ': Update resource with id ' . $a_econtent_id);
351 
352  $this->path_postfix = $a_path;
353 
354  if ($a_econtent_id) {
355  $this->path_postfix .= ('/' . $a_econtent_id);
356  } else {
357  throw new ilECSConnectorException('Error calling updateResource: No content id given.');
358  }
359  try {
360  $this->prepareConnection();
361  $this->addHeader('Content-Type', 'application/json');
362  $this->addHeader('Accept', 'application/json');
363  $this->addHeader('Expect', '');
364  $this->curl->setOpt(CURLOPT_HTTPHEADER, $this->getHeader());
365  $this->curl->setOpt(CURLOPT_HEADER, true);
366  $this->curl->setOpt(CURLOPT_PUT, true);
367  //TODO migrate to filesystem->tempfile
368  $tempfile = ilFileUtils::ilTempnam();
369  $this->logger->info(__METHOD__ . ': Created new tempfile: ' . $tempfile);
370 
371  $fp = fopen($tempfile, 'wb');
372  fwrite($fp, $a_post_string);
373  fclose($fp);
374 
375  $this->curl->setOpt(CURLOPT_UPLOAD, true);
376  $this->curl->setOpt(CURLOPT_INFILESIZE, filesize($tempfile));
377  $fp = fopen($tempfile, 'rb');
378  $this->curl->setOpt(CURLOPT_INFILE, $fp);
379 
380  $res = $this->call();
381 
382  fclose($fp);
383  unlink($tempfile);
384 
385  $info = $this->curl->getInfo(CURLINFO_HTTP_CODE);
386  $this->logger->debug(__METHOD__ . ': Checking HTTP status...');
387  if ($info !== self::HTTP_CODE_OK) {
388  $this->logger->debug(__METHOD__ . ': Cannot update resource. ', $a_path, $a_econtent_id);
389  throw new ilECSConnectorException('Received HTTP status code: ' . $info);
390  }
391  } catch (ilCurlConnectionException $exc) {
392  throw new ilECSConnectorException('Error calling ECS service: ' . $exc->getMessage());
393  }
394  }
395 
403  public function deleteResource(string $a_path, int $a_econtent_id): ?ilECSResult
404  {
405  $this->logger->debug(__METHOD__ . ': Delete resource with id ' . $a_econtent_id);
406 
407  $this->path_postfix = $a_path;
408 
409  if ($a_econtent_id) {
410  $this->path_postfix .= ('/' . $a_econtent_id);
411  } else {
412  throw new ilECSConnectorException('Error calling deleteResource: No content id given.');
413  }
414 
415  try {
416  $this->prepareConnection();
417  $this->curl->setOpt(CURLOPT_CUSTOMREQUEST, 'DELETE');
418  $res = $this->call();
419  $info = $this->curl->getInfo(CURLINFO_HTTP_CODE);
420  if (200 === $info) {
421  return new ilECSResult($res);
422  }
423  return null;
424  } catch (ilCurlConnectionException $exc) {
425  throw new ilECSConnectorException('Error calling ECS service: ' . $exc->getMessage());
426  }
427  }
428 
430  // membership methods
432 
437  public function getMemberships(int $a_mid = 0): ilECSResult
438  {
439  $this->logger->debug(__METHOD__ . ': Get existing memberships');
440 
441  $this->path_postfix = '/sys/memberships';
442  if ($a_mid) {
443  $this->logger->debug(__METHOD__ . ': Read membership with id: ' . $a_mid);
444  $this->path_postfix .= ('/' . $a_mid);
445  }
446  try {
447  $this->prepareConnection();
448  $res = $this->call();
449 
450  $this->curl->setOpt(CURLOPT_HTTPHEADER, array(0 => 'X-EcsQueryStrings: sender=true'));
451 
452  // Checking status code
453  $info = $this->curl->getInfo(CURLINFO_HTTP_CODE);
454  if ($info !== self::HTTP_CODE_OK) {
455  $this->logger->debug(__METHOD__ . ': Cannot get memberships, did not receive HTTP 200. ');
456  throw new ilECSConnectorException('Received HTTP status code: ' . $info);
457  }
458 
459  return new ilECSResult($res);
460  } catch (ilCurlConnectionException $exc) {
461  throw new ilECSConnectorException('Error calling ECS service: ' . $exc->getMessage() . $exc->getTraceAsString(), 0, $exc);
462  }
463  }
464 
470  protected function prepareConnection(): void
471  {
472  try {
473  $this->curl = new ilCurlConnection($this->settings->getServerURI() . $this->path_postfix);
474  $this->curl->init(true);
475  $this->curl->setOpt(CURLOPT_HTTPHEADER, array(0 => 'Accept: application/json'));
476  $this->curl->setOpt(CURLOPT_RETURNTRANSFER, 1);
477  $this->curl->setOpt(CURLOPT_TIMEOUT_MS, 2000);
478  $this->curl->setOpt(CURLOPT_FORBID_REUSE, true);
479  $this->curl->setOpt(CURLOPT_FRESH_CONNECT, true);
480 
481  if ($this->logger->isHandling(ilLogLevel::DEBUG)) {
482  $this->curl->setOpt(CURLOPT_VERBOSE, 1);
483  }
484 
485  switch ($this->getServer()->getAuthType()) {
487  $this->curl->setOpt(CURLOPT_SSL_VERIFYPEER, 1);
488  #$this->curl->setOpt(CURLOPT_SSL_VERIFYHOST,0);
489  $this->curl->setOpt(CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
490  $this->curl->setOpt(
491  CURLOPT_USERPWD,
492  $this->getServer()->getAuthUser() . ':' . $this->getServer()->getAuthPass()
493  );
494  break;
495 
497  $this->curl->setOpt(CURLOPT_SSL_VERIFYPEER, 1);
498  // use default 2 for libcurl 7.28.1 support
499  $this->curl->setOpt(CURLOPT_SSL_VERIFYHOST, 2);
500  $this->curl->setOpt(CURLOPT_CAINFO, $this->settings->getCACertPath());
501  $this->curl->setOpt(CURLOPT_SSLCERT, $this->settings->getClientCertPath());
502  $this->curl->setOpt(CURLOPT_SSLKEY, $this->settings->getKeyPath());
503  $this->curl->setOpt(CURLOPT_SSLKEYPASSWD, $this->settings->getKeyPassword());
504  break;
505 
506  }
507  } catch (ilCurlConnectionException $exc) {
508  throw($exc);
509  }
510  }
511 
519  protected function call()
520  {
521  try {
522  return $this->curl->exec();
523  } catch (ilCurlConnectionException $exc) {
524  $this->logger->error($exc->getMessage());
525  throw($exc);
526  }
527  }
528 
534  private function _fetchEContentIdFromHeader(array $a_header): int
535  {
536  $location_parts = [];
537  foreach ($a_header as $header => $value) {
538  if (strcasecmp('Location', $header) === 0) {
539  $location_parts = explode('/', $value);
540  break;
541  }
542  }
543  if (!$location_parts) {
544  $this->logger->error(__METHOD__ . ': Cannot find location headers.');
545  throw new ilECSConnectorException("Cannot find location header in response");
546  }
547  if (count($location_parts) === 1) {
548  $this->logger->warning(__METHOD__ . ': Cannot find path seperator.');
549  throw new ilECSConnectorException("Location header has wrong format: " . $location_parts[0]);
550  }
551  $econtent_id = end($location_parts);
552  $this->logger->info(__METHOD__ . ': Received EContentId ' . $econtent_id);
553  return (int) $econtent_id;
554  }
555 }
updateResource(string $a_path, int $a_econtent_id, string $a_post_string)
update resource
const RESULT_TYPE_URL_LIST
$res
Definition: ltiservices.php:69
getMemberships(int $a_mid=0)
__construct(ilECSSetting $settings=null)
readEventFifo(bool $a_delete=false)
Read event fifo.
addHeader(string $a_name, string $a_value)
Add Header.
Presentation of ecs content details (http://...campusconnect/courselinks/id/details) ...
init(bool $set_proxy=true)
global $DIC
Definition: feed.php:28
$auth
Definition: metadata.php:76
array $details
Details for error message relating to last request processed.
Definition: System.php:109
addAuth(string $a_post, int $a_target_mid)
Add auth resource.
getResource(string $a_path, int $a_econtent_id, bool $a_details_only=false)
Get resources from ECS server.
addResource(string $a_path, $a_post)
Add resource.
static ilTempnam(?string $a_temp_path=null)
Returns a unique and non existing Path for e temporary file or directory.
_fetchEContentIdFromHeader(array $a_header)
fetch new econtent id from location header
getResourceList(string $a_path)
getServer()
Get current server setting.
setHeader(array $a_header_strings)
ilECSSetting $settings
deleteResource(string $a_path, int $a_econtent_id)
Delete resource.
prepareConnection()
prepare connection
ilCurlConnection $curl
getAuth(string $a_hash, bool $a_details_only=false)
get auth resource