Passed
Push — master ( 1d211a...5b86eb )
by John
14:18 queued 12s
created
apps/workflowengine/lib/Manager.php 1 patch
Indentation   +714 added lines, -714 removed lines patch added patch discarded remove patch
@@ -70,718 +70,718 @@
 block discarded – undo
70 70
 use Symfony\Component\EventDispatcher\GenericEvent;
71 71
 
72 72
 class Manager implements IManager {
73
-	/** @var IStorage */
74
-	protected $storage;
75
-
76
-	/** @var string */
77
-	protected $path;
78
-
79
-	/** @var object */
80
-	protected $entity;
81
-
82
-	/** @var array[] */
83
-	protected $operations = [];
84
-
85
-	/** @var array[] */
86
-	protected $checks = [];
87
-
88
-	/** @var IDBConnection */
89
-	protected $connection;
90
-
91
-	/** @var IServerContainer|\OC\Server */
92
-	protected $container;
93
-
94
-	/** @var IL10N */
95
-	protected $l;
96
-
97
-	/** @var LegacyDispatcher */
98
-	protected $legacyEventDispatcher;
99
-
100
-	/** @var IEntity[] */
101
-	protected $registeredEntities = [];
102
-
103
-	/** @var IOperation[] */
104
-	protected $registeredOperators = [];
105
-
106
-	/** @var ICheck[] */
107
-	protected $registeredChecks = [];
108
-
109
-	/** @var ILogger */
110
-	protected $logger;
111
-
112
-	/** @var CappedMemoryCache<int[]> */
113
-	protected CappedMemoryCache $operationsByScope;
114
-
115
-	/** @var IUserSession */
116
-	protected $session;
117
-
118
-	/** @var IEventDispatcher */
119
-	private $dispatcher;
120
-
121
-	/** @var IConfig */
122
-	private $config;
123
-	private ICacheFactory $cacheFactory;
124
-
125
-	public function __construct(
126
-		IDBConnection $connection,
127
-		IServerContainer $container,
128
-		IL10N $l,
129
-		LegacyDispatcher $eventDispatcher,
130
-		ILogger $logger,
131
-		IUserSession $session,
132
-		IEventDispatcher $dispatcher,
133
-		IConfig $config,
134
-		ICacheFactory $cacheFactory,
135
-	) {
136
-		$this->connection = $connection;
137
-		$this->container = $container;
138
-		$this->l = $l;
139
-		$this->legacyEventDispatcher = $eventDispatcher;
140
-		$this->logger = $logger;
141
-		$this->operationsByScope = new CappedMemoryCache(64);
142
-		$this->session = $session;
143
-		$this->dispatcher = $dispatcher;
144
-		$this->config = $config;
145
-		$this->cacheFactory = $cacheFactory;
146
-	}
147
-
148
-	public function getRuleMatcher(): IRuleMatcher {
149
-		return new RuleMatcher(
150
-			$this->session,
151
-			$this->container,
152
-			$this->l,
153
-			$this,
154
-			$this->container->query(Logger::class)
155
-		);
156
-	}
157
-
158
-	public function getAllConfiguredEvents() {
159
-		$cache = $this->cacheFactory->createDistributed('flow');
160
-		$cached = $cache->get('events');
161
-		if ($cached !== null) {
162
-			return $cached;
163
-		}
164
-
165
-		$query = $this->connection->getQueryBuilder();
166
-
167
-		$query->select('class', 'entity')
168
-			->selectAlias($query->expr()->castColumn('events', IQueryBuilder::PARAM_STR), 'events')
169
-			->from('flow_operations')
170
-			->where($query->expr()->neq('events', $query->createNamedParameter('[]'), IQueryBuilder::PARAM_STR))
171
-			->groupBy('class', 'entity', $query->expr()->castColumn('events', IQueryBuilder::PARAM_STR));
172
-
173
-		$result = $query->execute();
174
-		$operations = [];
175
-		while ($row = $result->fetch()) {
176
-			$eventNames = \json_decode($row['events']);
177
-
178
-			$operation = $row['class'];
179
-			$entity = $row['entity'];
180
-
181
-			$operations[$operation] = $operations[$row['class']] ?? [];
182
-			$operations[$operation][$entity] = $operations[$operation][$entity] ?? [];
183
-
184
-			$operations[$operation][$entity] = array_unique(array_merge($operations[$operation][$entity], $eventNames ?? []));
185
-		}
186
-		$result->closeCursor();
187
-
188
-		$cache->set('events', $operations, 3600);
189
-
190
-		return $operations;
191
-	}
192
-
193
-	/**
194
-	 * @param string $operationClass
195
-	 * @return ScopeContext[]
196
-	 */
197
-	public function getAllConfiguredScopesForOperation(string $operationClass): array {
198
-		static $scopesByOperation = [];
199
-		if (isset($scopesByOperation[$operationClass])) {
200
-			return $scopesByOperation[$operationClass];
201
-		}
202
-
203
-		try {
204
-			/** @var IOperation $operation */
205
-			$operation = $this->container->query($operationClass);
206
-		} catch (QueryException $e) {
207
-			return [];
208
-		}
209
-
210
-		$query = $this->connection->getQueryBuilder();
211
-
212
-		$query->selectDistinct('s.type')
213
-			->addSelect('s.value')
214
-			->from('flow_operations', 'o')
215
-			->leftJoin('o', 'flow_operations_scope', 's', $query->expr()->eq('o.id', 's.operation_id'))
216
-			->where($query->expr()->eq('o.class', $query->createParameter('operationClass')));
217
-
218
-		$query->setParameters(['operationClass' => $operationClass]);
219
-		$result = $query->execute();
220
-
221
-		$scopesByOperation[$operationClass] = [];
222
-		while ($row = $result->fetch()) {
223
-			$scope = new ScopeContext($row['type'], $row['value']);
224
-
225
-			if (!$operation->isAvailableForScope((int) $row['type'])) {
226
-				continue;
227
-			}
228
-
229
-			$scopesByOperation[$operationClass][$scope->getHash()] = $scope;
230
-		}
231
-
232
-		return $scopesByOperation[$operationClass];
233
-	}
234
-
235
-	public function getAllOperations(ScopeContext $scopeContext): array {
236
-		if (isset($this->operations[$scopeContext->getHash()])) {
237
-			return $this->operations[$scopeContext->getHash()];
238
-		}
239
-
240
-		$query = $this->connection->getQueryBuilder();
241
-
242
-		$query->select('o.*')
243
-			->selectAlias('s.type', 'scope_type')
244
-			->selectAlias('s.value', 'scope_actor_id')
245
-			->from('flow_operations', 'o')
246
-			->leftJoin('o', 'flow_operations_scope', 's', $query->expr()->eq('o.id', 's.operation_id'))
247
-			->where($query->expr()->eq('s.type', $query->createParameter('scope')));
248
-
249
-		if ($scopeContext->getScope() === IManager::SCOPE_USER) {
250
-			$query->andWhere($query->expr()->eq('s.value', $query->createParameter('scopeId')));
251
-		}
252
-
253
-		$query->setParameters(['scope' => $scopeContext->getScope(), 'scopeId' => $scopeContext->getScopeId()]);
254
-		$result = $query->execute();
255
-
256
-		$this->operations[$scopeContext->getHash()] = [];
257
-		while ($row = $result->fetch()) {
258
-			try {
259
-				/** @var IOperation $operation */
260
-				$operation = $this->container->query($row['class']);
261
-			} catch (QueryException $e) {
262
-				continue;
263
-			}
264
-
265
-			if (!$operation->isAvailableForScope((int) $row['scope_type'])) {
266
-				continue;
267
-			}
268
-
269
-			if (!isset($this->operations[$scopeContext->getHash()][$row['class']])) {
270
-				$this->operations[$scopeContext->getHash()][$row['class']] = [];
271
-			}
272
-			$this->operations[$scopeContext->getHash()][$row['class']][] = $row;
273
-		}
274
-
275
-		return $this->operations[$scopeContext->getHash()];
276
-	}
277
-
278
-	public function getOperations(string $class, ScopeContext $scopeContext): array {
279
-		if (!isset($this->operations[$scopeContext->getHash()])) {
280
-			$this->getAllOperations($scopeContext);
281
-		}
282
-		return $this->operations[$scopeContext->getHash()][$class] ?? [];
283
-	}
284
-
285
-	/**
286
-	 * @param int $id
287
-	 * @return array
288
-	 * @throws \UnexpectedValueException
289
-	 */
290
-	protected function getOperation($id) {
291
-		$query = $this->connection->getQueryBuilder();
292
-		$query->select('*')
293
-			->from('flow_operations')
294
-			->where($query->expr()->eq('id', $query->createNamedParameter($id)));
295
-		$result = $query->execute();
296
-		$row = $result->fetch();
297
-		$result->closeCursor();
298
-
299
-		if ($row) {
300
-			return $row;
301
-		}
302
-
303
-		throw new \UnexpectedValueException($this->l->t('Operation #%s does not exist', [$id]));
304
-	}
305
-
306
-	protected function insertOperation(
307
-		string $class,
308
-		string $name,
309
-		array $checkIds,
310
-		string $operation,
311
-		string $entity,
312
-		array $events
313
-	): int {
314
-		$query = $this->connection->getQueryBuilder();
315
-		$query->insert('flow_operations')
316
-			->values([
317
-				'class' => $query->createNamedParameter($class),
318
-				'name' => $query->createNamedParameter($name),
319
-				'checks' => $query->createNamedParameter(json_encode(array_unique($checkIds))),
320
-				'operation' => $query->createNamedParameter($operation),
321
-				'entity' => $query->createNamedParameter($entity),
322
-				'events' => $query->createNamedParameter(json_encode($events))
323
-			]);
324
-		$query->execute();
325
-
326
-		$this->cacheFactory->createDistributed('flow')->remove('events');
327
-
328
-		return $query->getLastInsertId();
329
-	}
330
-
331
-	/**
332
-	 * @param string $class
333
-	 * @param string $name
334
-	 * @param array[] $checks
335
-	 * @param string $operation
336
-	 * @return array The added operation
337
-	 * @throws \UnexpectedValueException
338
-	 * @throw Exception
339
-	 */
340
-	public function addOperation(
341
-		string $class,
342
-		string $name,
343
-		array $checks,
344
-		string $operation,
345
-		ScopeContext $scope,
346
-		string $entity,
347
-		array $events
348
-	) {
349
-		$this->validateOperation($class, $name, $checks, $operation, $scope, $entity, $events);
350
-
351
-		$this->connection->beginTransaction();
352
-
353
-		try {
354
-			$checkIds = [];
355
-			foreach ($checks as $check) {
356
-				$checkIds[] = $this->addCheck($check['class'], $check['operator'], $check['value']);
357
-			}
358
-
359
-			$id = $this->insertOperation($class, $name, $checkIds, $operation, $entity, $events);
360
-			$this->addScope($id, $scope);
361
-
362
-			$this->connection->commit();
363
-		} catch (Exception $e) {
364
-			$this->connection->rollBack();
365
-			throw $e;
366
-		}
367
-
368
-		return $this->getOperation($id);
369
-	}
370
-
371
-	protected function canModify(int $id, ScopeContext $scopeContext):bool {
372
-		if (isset($this->operationsByScope[$scopeContext->getHash()])) {
373
-			return in_array($id, $this->operationsByScope[$scopeContext->getHash()], true);
374
-		}
375
-
376
-		$qb = $this->connection->getQueryBuilder();
377
-		$qb = $qb->select('o.id')
378
-			->from('flow_operations', 'o')
379
-			->leftJoin('o', 'flow_operations_scope', 's', $qb->expr()->eq('o.id', 's.operation_id'))
380
-			->where($qb->expr()->eq('s.type', $qb->createParameter('scope')));
381
-
382
-		if ($scopeContext->getScope() !== IManager::SCOPE_ADMIN) {
383
-			$qb->where($qb->expr()->eq('s.value', $qb->createParameter('scopeId')));
384
-		}
385
-
386
-		$qb->setParameters(['scope' => $scopeContext->getScope(), 'scopeId' => $scopeContext->getScopeId()]);
387
-		$result = $qb->execute();
388
-
389
-		$operations = [];
390
-		while (($opId = $result->fetchOne()) !== false) {
391
-			$operations[] = (int)$opId;
392
-		}
393
-		$this->operationsByScope[$scopeContext->getHash()] = $operations;
394
-		$result->closeCursor();
395
-
396
-		return in_array($id, $this->operationsByScope[$scopeContext->getHash()], true);
397
-	}
398
-
399
-	/**
400
-	 * @param int $id
401
-	 * @param string $name
402
-	 * @param array[] $checks
403
-	 * @param string $operation
404
-	 * @return array The updated operation
405
-	 * @throws \UnexpectedValueException
406
-	 * @throws \DomainException
407
-	 * @throws Exception
408
-	 */
409
-	public function updateOperation(
410
-		int $id,
411
-		string $name,
412
-		array $checks,
413
-		string $operation,
414
-		ScopeContext $scopeContext,
415
-		string $entity,
416
-		array $events
417
-	): array {
418
-		if (!$this->canModify($id, $scopeContext)) {
419
-			throw new \DomainException('Target operation not within scope');
420
-		};
421
-		$row = $this->getOperation($id);
422
-		$this->validateOperation($row['class'], $name, $checks, $operation, $scopeContext, $entity, $events);
423
-
424
-		$checkIds = [];
425
-		try {
426
-			$this->connection->beginTransaction();
427
-			foreach ($checks as $check) {
428
-				$checkIds[] = $this->addCheck($check['class'], $check['operator'], $check['value']);
429
-			}
430
-
431
-			$query = $this->connection->getQueryBuilder();
432
-			$query->update('flow_operations')
433
-				->set('name', $query->createNamedParameter($name))
434
-				->set('checks', $query->createNamedParameter(json_encode(array_unique($checkIds))))
435
-				->set('operation', $query->createNamedParameter($operation))
436
-				->set('entity', $query->createNamedParameter($entity))
437
-				->set('events', $query->createNamedParameter(json_encode($events)))
438
-				->where($query->expr()->eq('id', $query->createNamedParameter($id)));
439
-			$query->execute();
440
-			$this->connection->commit();
441
-		} catch (Exception $e) {
442
-			$this->connection->rollBack();
443
-			throw $e;
444
-		}
445
-		unset($this->operations[$scopeContext->getHash()]);
446
-		$this->cacheFactory->createDistributed('flow')->remove('events');
447
-
448
-		return $this->getOperation($id);
449
-	}
450
-
451
-	/**
452
-	 * @param int $id
453
-	 * @return bool
454
-	 * @throws \UnexpectedValueException
455
-	 * @throws Exception
456
-	 * @throws \DomainException
457
-	 */
458
-	public function deleteOperation($id, ScopeContext $scopeContext) {
459
-		if (!$this->canModify($id, $scopeContext)) {
460
-			throw new \DomainException('Target operation not within scope');
461
-		};
462
-		$query = $this->connection->getQueryBuilder();
463
-		try {
464
-			$this->connection->beginTransaction();
465
-			$result = (bool)$query->delete('flow_operations')
466
-				->where($query->expr()->eq('id', $query->createNamedParameter($id)))
467
-				->execute();
468
-			if ($result) {
469
-				$qb = $this->connection->getQueryBuilder();
470
-				$result &= (bool)$qb->delete('flow_operations_scope')
471
-					->where($qb->expr()->eq('operation_id', $qb->createNamedParameter($id)))
472
-					->execute();
473
-			}
474
-			$this->connection->commit();
475
-		} catch (Exception $e) {
476
-			$this->connection->rollBack();
477
-			throw $e;
478
-		}
479
-
480
-		if (isset($this->operations[$scopeContext->getHash()])) {
481
-			unset($this->operations[$scopeContext->getHash()]);
482
-		}
483
-
484
-		$this->cacheFactory->createDistributed('flow')->remove('events');
485
-
486
-		return $result;
487
-	}
488
-
489
-	protected function validateEvents(string $entity, array $events, IOperation $operation) {
490
-		try {
491
-			/** @var IEntity $instance */
492
-			$instance = $this->container->query($entity);
493
-		} catch (QueryException $e) {
494
-			throw new \UnexpectedValueException($this->l->t('Entity %s does not exist', [$entity]));
495
-		}
496
-
497
-		if (!$instance instanceof IEntity) {
498
-			throw new \UnexpectedValueException($this->l->t('Entity %s is invalid', [$entity]));
499
-		}
500
-
501
-		if (empty($events)) {
502
-			if (!$operation instanceof IComplexOperation) {
503
-				throw new \UnexpectedValueException($this->l->t('No events are chosen.'));
504
-			}
505
-			return;
506
-		}
507
-
508
-		$availableEvents = [];
509
-		foreach ($instance->getEvents() as $event) {
510
-			/** @var IEntityEvent $event */
511
-			$availableEvents[] = $event->getEventName();
512
-		}
513
-
514
-		$diff = array_diff($events, $availableEvents);
515
-		if (!empty($diff)) {
516
-			throw new \UnexpectedValueException($this->l->t('Entity %s has no event %s', [$entity, array_shift($diff)]));
517
-		}
518
-	}
519
-
520
-	/**
521
-	 * @param string $class
522
-	 * @param string $name
523
-	 * @param array[] $checks
524
-	 * @param string $operation
525
-	 * @param ScopeContext $scope
526
-	 * @param string $entity
527
-	 * @param array $events
528
-	 * @throws \UnexpectedValueException
529
-	 */
530
-	public function validateOperation($class, $name, array $checks, $operation, ScopeContext $scope, string $entity, array $events) {
531
-		try {
532
-			/** @var IOperation $instance */
533
-			$instance = $this->container->query($class);
534
-		} catch (QueryException $e) {
535
-			throw new \UnexpectedValueException($this->l->t('Operation %s does not exist', [$class]));
536
-		}
537
-
538
-		if (!($instance instanceof IOperation)) {
539
-			throw new \UnexpectedValueException($this->l->t('Operation %s is invalid', [$class]));
540
-		}
541
-
542
-		if (!$instance->isAvailableForScope($scope->getScope())) {
543
-			throw new \UnexpectedValueException($this->l->t('Operation %s is invalid', [$class]));
544
-		}
545
-
546
-		$this->validateEvents($entity, $events, $instance);
547
-
548
-		if (count($checks) === 0) {
549
-			throw new \UnexpectedValueException($this->l->t('At least one check needs to be provided'));
550
-		}
551
-
552
-		if (strlen((string)$operation) > IManager::MAX_OPERATION_VALUE_BYTES) {
553
-			throw new \UnexpectedValueException($this->l->t('The provided operation data is too long'));
554
-		}
555
-
556
-		$instance->validateOperation($name, $checks, $operation);
557
-
558
-		foreach ($checks as $check) {
559
-			if (!is_string($check['class'])) {
560
-				throw new \UnexpectedValueException($this->l->t('Invalid check provided'));
561
-			}
562
-
563
-			try {
564
-				/** @var ICheck $instance */
565
-				$instance = $this->container->query($check['class']);
566
-			} catch (QueryException $e) {
567
-				throw new \UnexpectedValueException($this->l->t('Check %s does not exist', [$class]));
568
-			}
569
-
570
-			if (!($instance instanceof ICheck)) {
571
-				throw new \UnexpectedValueException($this->l->t('Check %s is invalid', [$class]));
572
-			}
573
-
574
-			if (!empty($instance->supportedEntities())
575
-				&& !in_array($entity, $instance->supportedEntities())
576
-			) {
577
-				throw new \UnexpectedValueException($this->l->t('Check %s is not allowed with this entity', [$class]));
578
-			}
579
-
580
-			if (strlen((string)$check['value']) > IManager::MAX_CHECK_VALUE_BYTES) {
581
-				throw new \UnexpectedValueException($this->l->t('The provided check value is too long'));
582
-			}
583
-
584
-			$instance->validateCheck($check['operator'], $check['value']);
585
-		}
586
-	}
587
-
588
-	/**
589
-	 * @param int[] $checkIds
590
-	 * @return array[]
591
-	 */
592
-	public function getChecks(array $checkIds) {
593
-		$checkIds = array_map('intval', $checkIds);
594
-
595
-		$checks = [];
596
-		foreach ($checkIds as $i => $checkId) {
597
-			if (isset($this->checks[$checkId])) {
598
-				$checks[$checkId] = $this->checks[$checkId];
599
-				unset($checkIds[$i]);
600
-			}
601
-		}
602
-
603
-		if (empty($checkIds)) {
604
-			return $checks;
605
-		}
606
-
607
-		$query = $this->connection->getQueryBuilder();
608
-		$query->select('*')
609
-			->from('flow_checks')
610
-			->where($query->expr()->in('id', $query->createNamedParameter($checkIds, IQueryBuilder::PARAM_INT_ARRAY)));
611
-		$result = $query->execute();
612
-
613
-		while ($row = $result->fetch()) {
614
-			$this->checks[(int) $row['id']] = $row;
615
-			$checks[(int) $row['id']] = $row;
616
-		}
617
-		$result->closeCursor();
618
-
619
-		$checkIds = array_diff($checkIds, array_keys($checks));
620
-
621
-		if (!empty($checkIds)) {
622
-			$missingCheck = array_pop($checkIds);
623
-			throw new \UnexpectedValueException($this->l->t('Check #%s does not exist', $missingCheck));
624
-		}
625
-
626
-		return $checks;
627
-	}
628
-
629
-	/**
630
-	 * @param string $class
631
-	 * @param string $operator
632
-	 * @param string $value
633
-	 * @return int Check unique ID
634
-	 */
635
-	protected function addCheck($class, $operator, $value) {
636
-		$hash = md5($class . '::' . $operator . '::' . $value);
637
-
638
-		$query = $this->connection->getQueryBuilder();
639
-		$query->select('id')
640
-			->from('flow_checks')
641
-			->where($query->expr()->eq('hash', $query->createNamedParameter($hash)));
642
-		$result = $query->execute();
643
-
644
-		if ($row = $result->fetch()) {
645
-			$result->closeCursor();
646
-			return (int) $row['id'];
647
-		}
648
-
649
-		$query = $this->connection->getQueryBuilder();
650
-		$query->insert('flow_checks')
651
-			->values([
652
-				'class' => $query->createNamedParameter($class),
653
-				'operator' => $query->createNamedParameter($operator),
654
-				'value' => $query->createNamedParameter($value),
655
-				'hash' => $query->createNamedParameter($hash),
656
-			]);
657
-		$query->execute();
658
-
659
-		return $query->getLastInsertId();
660
-	}
661
-
662
-	protected function addScope(int $operationId, ScopeContext $scope): void {
663
-		$query = $this->connection->getQueryBuilder();
664
-
665
-		$insertQuery = $query->insert('flow_operations_scope');
666
-		$insertQuery->values([
667
-			'operation_id' => $query->createNamedParameter($operationId),
668
-			'type' => $query->createNamedParameter($scope->getScope()),
669
-			'value' => $query->createNamedParameter($scope->getScopeId()),
670
-		]);
671
-		$insertQuery->execute();
672
-	}
673
-
674
-	public function formatOperation(array $operation): array {
675
-		$checkIds = json_decode($operation['checks'], true);
676
-		$checks = $this->getChecks($checkIds);
677
-
678
-		$operation['checks'] = [];
679
-		foreach ($checks as $check) {
680
-			// Remove internal values
681
-			unset($check['id']);
682
-			unset($check['hash']);
683
-
684
-			$operation['checks'][] = $check;
685
-		}
686
-		$operation['events'] = json_decode($operation['events'], true) ?? [];
687
-
688
-
689
-		return $operation;
690
-	}
691
-
692
-	/**
693
-	 * @return IEntity[]
694
-	 */
695
-	public function getEntitiesList(): array {
696
-		$this->dispatcher->dispatchTyped(new RegisterEntitiesEvent($this));
697
-		$this->legacyEventDispatcher->dispatch(IManager::EVENT_NAME_REG_ENTITY, new GenericEvent($this));
698
-
699
-		return array_values(array_merge($this->getBuildInEntities(), $this->registeredEntities));
700
-	}
701
-
702
-	/**
703
-	 * @return IOperation[]
704
-	 */
705
-	public function getOperatorList(): array {
706
-		$this->dispatcher->dispatchTyped(new RegisterOperationsEvent($this));
707
-		$this->legacyEventDispatcher->dispatch(IManager::EVENT_NAME_REG_OPERATION, new GenericEvent($this));
708
-
709
-		return array_merge($this->getBuildInOperators(), $this->registeredOperators);
710
-	}
711
-
712
-	/**
713
-	 * @return ICheck[]
714
-	 */
715
-	public function getCheckList(): array {
716
-		$this->dispatcher->dispatchTyped(new RegisterChecksEvent($this));
717
-		$this->legacyEventDispatcher->dispatch(IManager::EVENT_NAME_REG_CHECK, new GenericEvent($this));
718
-
719
-		return array_merge($this->getBuildInChecks(), $this->registeredChecks);
720
-	}
721
-
722
-	public function registerEntity(IEntity $entity): void {
723
-		$this->registeredEntities[get_class($entity)] = $entity;
724
-	}
725
-
726
-	public function registerOperation(IOperation $operator): void {
727
-		$this->registeredOperators[get_class($operator)] = $operator;
728
-	}
729
-
730
-	public function registerCheck(ICheck $check): void {
731
-		$this->registeredChecks[get_class($check)] = $check;
732
-	}
733
-
734
-	/**
735
-	 * @return IEntity[]
736
-	 */
737
-	protected function getBuildInEntities(): array {
738
-		try {
739
-			return [
740
-				File::class => $this->container->query(File::class),
741
-			];
742
-		} catch (QueryException $e) {
743
-			$this->logger->logException($e);
744
-			return [];
745
-		}
746
-	}
747
-
748
-	/**
749
-	 * @return IOperation[]
750
-	 */
751
-	protected function getBuildInOperators(): array {
752
-		try {
753
-			return [
754
-				// None yet
755
-			];
756
-		} catch (QueryException $e) {
757
-			$this->logger->logException($e);
758
-			return [];
759
-		}
760
-	}
761
-
762
-	/**
763
-	 * @return ICheck[]
764
-	 */
765
-	protected function getBuildInChecks(): array {
766
-		try {
767
-			return [
768
-				$this->container->query(FileMimeType::class),
769
-				$this->container->query(FileName::class),
770
-				$this->container->query(FileSize::class),
771
-				$this->container->query(FileSystemTags::class),
772
-				$this->container->query(RequestRemoteAddress::class),
773
-				$this->container->query(RequestTime::class),
774
-				$this->container->query(RequestURL::class),
775
-				$this->container->query(RequestUserAgent::class),
776
-				$this->container->query(UserGroupMembership::class),
777
-			];
778
-		} catch (QueryException $e) {
779
-			$this->logger->logException($e);
780
-			return [];
781
-		}
782
-	}
783
-
784
-	public function isUserScopeEnabled(): bool {
785
-		return $this->config->getAppValue(Application::APP_ID, 'user_scope_disabled', 'no') === 'no';
786
-	}
73
+    /** @var IStorage */
74
+    protected $storage;
75
+
76
+    /** @var string */
77
+    protected $path;
78
+
79
+    /** @var object */
80
+    protected $entity;
81
+
82
+    /** @var array[] */
83
+    protected $operations = [];
84
+
85
+    /** @var array[] */
86
+    protected $checks = [];
87
+
88
+    /** @var IDBConnection */
89
+    protected $connection;
90
+
91
+    /** @var IServerContainer|\OC\Server */
92
+    protected $container;
93
+
94
+    /** @var IL10N */
95
+    protected $l;
96
+
97
+    /** @var LegacyDispatcher */
98
+    protected $legacyEventDispatcher;
99
+
100
+    /** @var IEntity[] */
101
+    protected $registeredEntities = [];
102
+
103
+    /** @var IOperation[] */
104
+    protected $registeredOperators = [];
105
+
106
+    /** @var ICheck[] */
107
+    protected $registeredChecks = [];
108
+
109
+    /** @var ILogger */
110
+    protected $logger;
111
+
112
+    /** @var CappedMemoryCache<int[]> */
113
+    protected CappedMemoryCache $operationsByScope;
114
+
115
+    /** @var IUserSession */
116
+    protected $session;
117
+
118
+    /** @var IEventDispatcher */
119
+    private $dispatcher;
120
+
121
+    /** @var IConfig */
122
+    private $config;
123
+    private ICacheFactory $cacheFactory;
124
+
125
+    public function __construct(
126
+        IDBConnection $connection,
127
+        IServerContainer $container,
128
+        IL10N $l,
129
+        LegacyDispatcher $eventDispatcher,
130
+        ILogger $logger,
131
+        IUserSession $session,
132
+        IEventDispatcher $dispatcher,
133
+        IConfig $config,
134
+        ICacheFactory $cacheFactory,
135
+    ) {
136
+        $this->connection = $connection;
137
+        $this->container = $container;
138
+        $this->l = $l;
139
+        $this->legacyEventDispatcher = $eventDispatcher;
140
+        $this->logger = $logger;
141
+        $this->operationsByScope = new CappedMemoryCache(64);
142
+        $this->session = $session;
143
+        $this->dispatcher = $dispatcher;
144
+        $this->config = $config;
145
+        $this->cacheFactory = $cacheFactory;
146
+    }
147
+
148
+    public function getRuleMatcher(): IRuleMatcher {
149
+        return new RuleMatcher(
150
+            $this->session,
151
+            $this->container,
152
+            $this->l,
153
+            $this,
154
+            $this->container->query(Logger::class)
155
+        );
156
+    }
157
+
158
+    public function getAllConfiguredEvents() {
159
+        $cache = $this->cacheFactory->createDistributed('flow');
160
+        $cached = $cache->get('events');
161
+        if ($cached !== null) {
162
+            return $cached;
163
+        }
164
+
165
+        $query = $this->connection->getQueryBuilder();
166
+
167
+        $query->select('class', 'entity')
168
+            ->selectAlias($query->expr()->castColumn('events', IQueryBuilder::PARAM_STR), 'events')
169
+            ->from('flow_operations')
170
+            ->where($query->expr()->neq('events', $query->createNamedParameter('[]'), IQueryBuilder::PARAM_STR))
171
+            ->groupBy('class', 'entity', $query->expr()->castColumn('events', IQueryBuilder::PARAM_STR));
172
+
173
+        $result = $query->execute();
174
+        $operations = [];
175
+        while ($row = $result->fetch()) {
176
+            $eventNames = \json_decode($row['events']);
177
+
178
+            $operation = $row['class'];
179
+            $entity = $row['entity'];
180
+
181
+            $operations[$operation] = $operations[$row['class']] ?? [];
182
+            $operations[$operation][$entity] = $operations[$operation][$entity] ?? [];
183
+
184
+            $operations[$operation][$entity] = array_unique(array_merge($operations[$operation][$entity], $eventNames ?? []));
185
+        }
186
+        $result->closeCursor();
187
+
188
+        $cache->set('events', $operations, 3600);
189
+
190
+        return $operations;
191
+    }
192
+
193
+    /**
194
+     * @param string $operationClass
195
+     * @return ScopeContext[]
196
+     */
197
+    public function getAllConfiguredScopesForOperation(string $operationClass): array {
198
+        static $scopesByOperation = [];
199
+        if (isset($scopesByOperation[$operationClass])) {
200
+            return $scopesByOperation[$operationClass];
201
+        }
202
+
203
+        try {
204
+            /** @var IOperation $operation */
205
+            $operation = $this->container->query($operationClass);
206
+        } catch (QueryException $e) {
207
+            return [];
208
+        }
209
+
210
+        $query = $this->connection->getQueryBuilder();
211
+
212
+        $query->selectDistinct('s.type')
213
+            ->addSelect('s.value')
214
+            ->from('flow_operations', 'o')
215
+            ->leftJoin('o', 'flow_operations_scope', 's', $query->expr()->eq('o.id', 's.operation_id'))
216
+            ->where($query->expr()->eq('o.class', $query->createParameter('operationClass')));
217
+
218
+        $query->setParameters(['operationClass' => $operationClass]);
219
+        $result = $query->execute();
220
+
221
+        $scopesByOperation[$operationClass] = [];
222
+        while ($row = $result->fetch()) {
223
+            $scope = new ScopeContext($row['type'], $row['value']);
224
+
225
+            if (!$operation->isAvailableForScope((int) $row['type'])) {
226
+                continue;
227
+            }
228
+
229
+            $scopesByOperation[$operationClass][$scope->getHash()] = $scope;
230
+        }
231
+
232
+        return $scopesByOperation[$operationClass];
233
+    }
234
+
235
+    public function getAllOperations(ScopeContext $scopeContext): array {
236
+        if (isset($this->operations[$scopeContext->getHash()])) {
237
+            return $this->operations[$scopeContext->getHash()];
238
+        }
239
+
240
+        $query = $this->connection->getQueryBuilder();
241
+
242
+        $query->select('o.*')
243
+            ->selectAlias('s.type', 'scope_type')
244
+            ->selectAlias('s.value', 'scope_actor_id')
245
+            ->from('flow_operations', 'o')
246
+            ->leftJoin('o', 'flow_operations_scope', 's', $query->expr()->eq('o.id', 's.operation_id'))
247
+            ->where($query->expr()->eq('s.type', $query->createParameter('scope')));
248
+
249
+        if ($scopeContext->getScope() === IManager::SCOPE_USER) {
250
+            $query->andWhere($query->expr()->eq('s.value', $query->createParameter('scopeId')));
251
+        }
252
+
253
+        $query->setParameters(['scope' => $scopeContext->getScope(), 'scopeId' => $scopeContext->getScopeId()]);
254
+        $result = $query->execute();
255
+
256
+        $this->operations[$scopeContext->getHash()] = [];
257
+        while ($row = $result->fetch()) {
258
+            try {
259
+                /** @var IOperation $operation */
260
+                $operation = $this->container->query($row['class']);
261
+            } catch (QueryException $e) {
262
+                continue;
263
+            }
264
+
265
+            if (!$operation->isAvailableForScope((int) $row['scope_type'])) {
266
+                continue;
267
+            }
268
+
269
+            if (!isset($this->operations[$scopeContext->getHash()][$row['class']])) {
270
+                $this->operations[$scopeContext->getHash()][$row['class']] = [];
271
+            }
272
+            $this->operations[$scopeContext->getHash()][$row['class']][] = $row;
273
+        }
274
+
275
+        return $this->operations[$scopeContext->getHash()];
276
+    }
277
+
278
+    public function getOperations(string $class, ScopeContext $scopeContext): array {
279
+        if (!isset($this->operations[$scopeContext->getHash()])) {
280
+            $this->getAllOperations($scopeContext);
281
+        }
282
+        return $this->operations[$scopeContext->getHash()][$class] ?? [];
283
+    }
284
+
285
+    /**
286
+     * @param int $id
287
+     * @return array
288
+     * @throws \UnexpectedValueException
289
+     */
290
+    protected function getOperation($id) {
291
+        $query = $this->connection->getQueryBuilder();
292
+        $query->select('*')
293
+            ->from('flow_operations')
294
+            ->where($query->expr()->eq('id', $query->createNamedParameter($id)));
295
+        $result = $query->execute();
296
+        $row = $result->fetch();
297
+        $result->closeCursor();
298
+
299
+        if ($row) {
300
+            return $row;
301
+        }
302
+
303
+        throw new \UnexpectedValueException($this->l->t('Operation #%s does not exist', [$id]));
304
+    }
305
+
306
+    protected function insertOperation(
307
+        string $class,
308
+        string $name,
309
+        array $checkIds,
310
+        string $operation,
311
+        string $entity,
312
+        array $events
313
+    ): int {
314
+        $query = $this->connection->getQueryBuilder();
315
+        $query->insert('flow_operations')
316
+            ->values([
317
+                'class' => $query->createNamedParameter($class),
318
+                'name' => $query->createNamedParameter($name),
319
+                'checks' => $query->createNamedParameter(json_encode(array_unique($checkIds))),
320
+                'operation' => $query->createNamedParameter($operation),
321
+                'entity' => $query->createNamedParameter($entity),
322
+                'events' => $query->createNamedParameter(json_encode($events))
323
+            ]);
324
+        $query->execute();
325
+
326
+        $this->cacheFactory->createDistributed('flow')->remove('events');
327
+
328
+        return $query->getLastInsertId();
329
+    }
330
+
331
+    /**
332
+     * @param string $class
333
+     * @param string $name
334
+     * @param array[] $checks
335
+     * @param string $operation
336
+     * @return array The added operation
337
+     * @throws \UnexpectedValueException
338
+     * @throw Exception
339
+     */
340
+    public function addOperation(
341
+        string $class,
342
+        string $name,
343
+        array $checks,
344
+        string $operation,
345
+        ScopeContext $scope,
346
+        string $entity,
347
+        array $events
348
+    ) {
349
+        $this->validateOperation($class, $name, $checks, $operation, $scope, $entity, $events);
350
+
351
+        $this->connection->beginTransaction();
352
+
353
+        try {
354
+            $checkIds = [];
355
+            foreach ($checks as $check) {
356
+                $checkIds[] = $this->addCheck($check['class'], $check['operator'], $check['value']);
357
+            }
358
+
359
+            $id = $this->insertOperation($class, $name, $checkIds, $operation, $entity, $events);
360
+            $this->addScope($id, $scope);
361
+
362
+            $this->connection->commit();
363
+        } catch (Exception $e) {
364
+            $this->connection->rollBack();
365
+            throw $e;
366
+        }
367
+
368
+        return $this->getOperation($id);
369
+    }
370
+
371
+    protected function canModify(int $id, ScopeContext $scopeContext):bool {
372
+        if (isset($this->operationsByScope[$scopeContext->getHash()])) {
373
+            return in_array($id, $this->operationsByScope[$scopeContext->getHash()], true);
374
+        }
375
+
376
+        $qb = $this->connection->getQueryBuilder();
377
+        $qb = $qb->select('o.id')
378
+            ->from('flow_operations', 'o')
379
+            ->leftJoin('o', 'flow_operations_scope', 's', $qb->expr()->eq('o.id', 's.operation_id'))
380
+            ->where($qb->expr()->eq('s.type', $qb->createParameter('scope')));
381
+
382
+        if ($scopeContext->getScope() !== IManager::SCOPE_ADMIN) {
383
+            $qb->where($qb->expr()->eq('s.value', $qb->createParameter('scopeId')));
384
+        }
385
+
386
+        $qb->setParameters(['scope' => $scopeContext->getScope(), 'scopeId' => $scopeContext->getScopeId()]);
387
+        $result = $qb->execute();
388
+
389
+        $operations = [];
390
+        while (($opId = $result->fetchOne()) !== false) {
391
+            $operations[] = (int)$opId;
392
+        }
393
+        $this->operationsByScope[$scopeContext->getHash()] = $operations;
394
+        $result->closeCursor();
395
+
396
+        return in_array($id, $this->operationsByScope[$scopeContext->getHash()], true);
397
+    }
398
+
399
+    /**
400
+     * @param int $id
401
+     * @param string $name
402
+     * @param array[] $checks
403
+     * @param string $operation
404
+     * @return array The updated operation
405
+     * @throws \UnexpectedValueException
406
+     * @throws \DomainException
407
+     * @throws Exception
408
+     */
409
+    public function updateOperation(
410
+        int $id,
411
+        string $name,
412
+        array $checks,
413
+        string $operation,
414
+        ScopeContext $scopeContext,
415
+        string $entity,
416
+        array $events
417
+    ): array {
418
+        if (!$this->canModify($id, $scopeContext)) {
419
+            throw new \DomainException('Target operation not within scope');
420
+        };
421
+        $row = $this->getOperation($id);
422
+        $this->validateOperation($row['class'], $name, $checks, $operation, $scopeContext, $entity, $events);
423
+
424
+        $checkIds = [];
425
+        try {
426
+            $this->connection->beginTransaction();
427
+            foreach ($checks as $check) {
428
+                $checkIds[] = $this->addCheck($check['class'], $check['operator'], $check['value']);
429
+            }
430
+
431
+            $query = $this->connection->getQueryBuilder();
432
+            $query->update('flow_operations')
433
+                ->set('name', $query->createNamedParameter($name))
434
+                ->set('checks', $query->createNamedParameter(json_encode(array_unique($checkIds))))
435
+                ->set('operation', $query->createNamedParameter($operation))
436
+                ->set('entity', $query->createNamedParameter($entity))
437
+                ->set('events', $query->createNamedParameter(json_encode($events)))
438
+                ->where($query->expr()->eq('id', $query->createNamedParameter($id)));
439
+            $query->execute();
440
+            $this->connection->commit();
441
+        } catch (Exception $e) {
442
+            $this->connection->rollBack();
443
+            throw $e;
444
+        }
445
+        unset($this->operations[$scopeContext->getHash()]);
446
+        $this->cacheFactory->createDistributed('flow')->remove('events');
447
+
448
+        return $this->getOperation($id);
449
+    }
450
+
451
+    /**
452
+     * @param int $id
453
+     * @return bool
454
+     * @throws \UnexpectedValueException
455
+     * @throws Exception
456
+     * @throws \DomainException
457
+     */
458
+    public function deleteOperation($id, ScopeContext $scopeContext) {
459
+        if (!$this->canModify($id, $scopeContext)) {
460
+            throw new \DomainException('Target operation not within scope');
461
+        };
462
+        $query = $this->connection->getQueryBuilder();
463
+        try {
464
+            $this->connection->beginTransaction();
465
+            $result = (bool)$query->delete('flow_operations')
466
+                ->where($query->expr()->eq('id', $query->createNamedParameter($id)))
467
+                ->execute();
468
+            if ($result) {
469
+                $qb = $this->connection->getQueryBuilder();
470
+                $result &= (bool)$qb->delete('flow_operations_scope')
471
+                    ->where($qb->expr()->eq('operation_id', $qb->createNamedParameter($id)))
472
+                    ->execute();
473
+            }
474
+            $this->connection->commit();
475
+        } catch (Exception $e) {
476
+            $this->connection->rollBack();
477
+            throw $e;
478
+        }
479
+
480
+        if (isset($this->operations[$scopeContext->getHash()])) {
481
+            unset($this->operations[$scopeContext->getHash()]);
482
+        }
483
+
484
+        $this->cacheFactory->createDistributed('flow')->remove('events');
485
+
486
+        return $result;
487
+    }
488
+
489
+    protected function validateEvents(string $entity, array $events, IOperation $operation) {
490
+        try {
491
+            /** @var IEntity $instance */
492
+            $instance = $this->container->query($entity);
493
+        } catch (QueryException $e) {
494
+            throw new \UnexpectedValueException($this->l->t('Entity %s does not exist', [$entity]));
495
+        }
496
+
497
+        if (!$instance instanceof IEntity) {
498
+            throw new \UnexpectedValueException($this->l->t('Entity %s is invalid', [$entity]));
499
+        }
500
+
501
+        if (empty($events)) {
502
+            if (!$operation instanceof IComplexOperation) {
503
+                throw new \UnexpectedValueException($this->l->t('No events are chosen.'));
504
+            }
505
+            return;
506
+        }
507
+
508
+        $availableEvents = [];
509
+        foreach ($instance->getEvents() as $event) {
510
+            /** @var IEntityEvent $event */
511
+            $availableEvents[] = $event->getEventName();
512
+        }
513
+
514
+        $diff = array_diff($events, $availableEvents);
515
+        if (!empty($diff)) {
516
+            throw new \UnexpectedValueException($this->l->t('Entity %s has no event %s', [$entity, array_shift($diff)]));
517
+        }
518
+    }
519
+
520
+    /**
521
+     * @param string $class
522
+     * @param string $name
523
+     * @param array[] $checks
524
+     * @param string $operation
525
+     * @param ScopeContext $scope
526
+     * @param string $entity
527
+     * @param array $events
528
+     * @throws \UnexpectedValueException
529
+     */
530
+    public function validateOperation($class, $name, array $checks, $operation, ScopeContext $scope, string $entity, array $events) {
531
+        try {
532
+            /** @var IOperation $instance */
533
+            $instance = $this->container->query($class);
534
+        } catch (QueryException $e) {
535
+            throw new \UnexpectedValueException($this->l->t('Operation %s does not exist', [$class]));
536
+        }
537
+
538
+        if (!($instance instanceof IOperation)) {
539
+            throw new \UnexpectedValueException($this->l->t('Operation %s is invalid', [$class]));
540
+        }
541
+
542
+        if (!$instance->isAvailableForScope($scope->getScope())) {
543
+            throw new \UnexpectedValueException($this->l->t('Operation %s is invalid', [$class]));
544
+        }
545
+
546
+        $this->validateEvents($entity, $events, $instance);
547
+
548
+        if (count($checks) === 0) {
549
+            throw new \UnexpectedValueException($this->l->t('At least one check needs to be provided'));
550
+        }
551
+
552
+        if (strlen((string)$operation) > IManager::MAX_OPERATION_VALUE_BYTES) {
553
+            throw new \UnexpectedValueException($this->l->t('The provided operation data is too long'));
554
+        }
555
+
556
+        $instance->validateOperation($name, $checks, $operation);
557
+
558
+        foreach ($checks as $check) {
559
+            if (!is_string($check['class'])) {
560
+                throw new \UnexpectedValueException($this->l->t('Invalid check provided'));
561
+            }
562
+
563
+            try {
564
+                /** @var ICheck $instance */
565
+                $instance = $this->container->query($check['class']);
566
+            } catch (QueryException $e) {
567
+                throw new \UnexpectedValueException($this->l->t('Check %s does not exist', [$class]));
568
+            }
569
+
570
+            if (!($instance instanceof ICheck)) {
571
+                throw new \UnexpectedValueException($this->l->t('Check %s is invalid', [$class]));
572
+            }
573
+
574
+            if (!empty($instance->supportedEntities())
575
+                && !in_array($entity, $instance->supportedEntities())
576
+            ) {
577
+                throw new \UnexpectedValueException($this->l->t('Check %s is not allowed with this entity', [$class]));
578
+            }
579
+
580
+            if (strlen((string)$check['value']) > IManager::MAX_CHECK_VALUE_BYTES) {
581
+                throw new \UnexpectedValueException($this->l->t('The provided check value is too long'));
582
+            }
583
+
584
+            $instance->validateCheck($check['operator'], $check['value']);
585
+        }
586
+    }
587
+
588
+    /**
589
+     * @param int[] $checkIds
590
+     * @return array[]
591
+     */
592
+    public function getChecks(array $checkIds) {
593
+        $checkIds = array_map('intval', $checkIds);
594
+
595
+        $checks = [];
596
+        foreach ($checkIds as $i => $checkId) {
597
+            if (isset($this->checks[$checkId])) {
598
+                $checks[$checkId] = $this->checks[$checkId];
599
+                unset($checkIds[$i]);
600
+            }
601
+        }
602
+
603
+        if (empty($checkIds)) {
604
+            return $checks;
605
+        }
606
+
607
+        $query = $this->connection->getQueryBuilder();
608
+        $query->select('*')
609
+            ->from('flow_checks')
610
+            ->where($query->expr()->in('id', $query->createNamedParameter($checkIds, IQueryBuilder::PARAM_INT_ARRAY)));
611
+        $result = $query->execute();
612
+
613
+        while ($row = $result->fetch()) {
614
+            $this->checks[(int) $row['id']] = $row;
615
+            $checks[(int) $row['id']] = $row;
616
+        }
617
+        $result->closeCursor();
618
+
619
+        $checkIds = array_diff($checkIds, array_keys($checks));
620
+
621
+        if (!empty($checkIds)) {
622
+            $missingCheck = array_pop($checkIds);
623
+            throw new \UnexpectedValueException($this->l->t('Check #%s does not exist', $missingCheck));
624
+        }
625
+
626
+        return $checks;
627
+    }
628
+
629
+    /**
630
+     * @param string $class
631
+     * @param string $operator
632
+     * @param string $value
633
+     * @return int Check unique ID
634
+     */
635
+    protected function addCheck($class, $operator, $value) {
636
+        $hash = md5($class . '::' . $operator . '::' . $value);
637
+
638
+        $query = $this->connection->getQueryBuilder();
639
+        $query->select('id')
640
+            ->from('flow_checks')
641
+            ->where($query->expr()->eq('hash', $query->createNamedParameter($hash)));
642
+        $result = $query->execute();
643
+
644
+        if ($row = $result->fetch()) {
645
+            $result->closeCursor();
646
+            return (int) $row['id'];
647
+        }
648
+
649
+        $query = $this->connection->getQueryBuilder();
650
+        $query->insert('flow_checks')
651
+            ->values([
652
+                'class' => $query->createNamedParameter($class),
653
+                'operator' => $query->createNamedParameter($operator),
654
+                'value' => $query->createNamedParameter($value),
655
+                'hash' => $query->createNamedParameter($hash),
656
+            ]);
657
+        $query->execute();
658
+
659
+        return $query->getLastInsertId();
660
+    }
661
+
662
+    protected function addScope(int $operationId, ScopeContext $scope): void {
663
+        $query = $this->connection->getQueryBuilder();
664
+
665
+        $insertQuery = $query->insert('flow_operations_scope');
666
+        $insertQuery->values([
667
+            'operation_id' => $query->createNamedParameter($operationId),
668
+            'type' => $query->createNamedParameter($scope->getScope()),
669
+            'value' => $query->createNamedParameter($scope->getScopeId()),
670
+        ]);
671
+        $insertQuery->execute();
672
+    }
673
+
674
+    public function formatOperation(array $operation): array {
675
+        $checkIds = json_decode($operation['checks'], true);
676
+        $checks = $this->getChecks($checkIds);
677
+
678
+        $operation['checks'] = [];
679
+        foreach ($checks as $check) {
680
+            // Remove internal values
681
+            unset($check['id']);
682
+            unset($check['hash']);
683
+
684
+            $operation['checks'][] = $check;
685
+        }
686
+        $operation['events'] = json_decode($operation['events'], true) ?? [];
687
+
688
+
689
+        return $operation;
690
+    }
691
+
692
+    /**
693
+     * @return IEntity[]
694
+     */
695
+    public function getEntitiesList(): array {
696
+        $this->dispatcher->dispatchTyped(new RegisterEntitiesEvent($this));
697
+        $this->legacyEventDispatcher->dispatch(IManager::EVENT_NAME_REG_ENTITY, new GenericEvent($this));
698
+
699
+        return array_values(array_merge($this->getBuildInEntities(), $this->registeredEntities));
700
+    }
701
+
702
+    /**
703
+     * @return IOperation[]
704
+     */
705
+    public function getOperatorList(): array {
706
+        $this->dispatcher->dispatchTyped(new RegisterOperationsEvent($this));
707
+        $this->legacyEventDispatcher->dispatch(IManager::EVENT_NAME_REG_OPERATION, new GenericEvent($this));
708
+
709
+        return array_merge($this->getBuildInOperators(), $this->registeredOperators);
710
+    }
711
+
712
+    /**
713
+     * @return ICheck[]
714
+     */
715
+    public function getCheckList(): array {
716
+        $this->dispatcher->dispatchTyped(new RegisterChecksEvent($this));
717
+        $this->legacyEventDispatcher->dispatch(IManager::EVENT_NAME_REG_CHECK, new GenericEvent($this));
718
+
719
+        return array_merge($this->getBuildInChecks(), $this->registeredChecks);
720
+    }
721
+
722
+    public function registerEntity(IEntity $entity): void {
723
+        $this->registeredEntities[get_class($entity)] = $entity;
724
+    }
725
+
726
+    public function registerOperation(IOperation $operator): void {
727
+        $this->registeredOperators[get_class($operator)] = $operator;
728
+    }
729
+
730
+    public function registerCheck(ICheck $check): void {
731
+        $this->registeredChecks[get_class($check)] = $check;
732
+    }
733
+
734
+    /**
735
+     * @return IEntity[]
736
+     */
737
+    protected function getBuildInEntities(): array {
738
+        try {
739
+            return [
740
+                File::class => $this->container->query(File::class),
741
+            ];
742
+        } catch (QueryException $e) {
743
+            $this->logger->logException($e);
744
+            return [];
745
+        }
746
+    }
747
+
748
+    /**
749
+     * @return IOperation[]
750
+     */
751
+    protected function getBuildInOperators(): array {
752
+        try {
753
+            return [
754
+                // None yet
755
+            ];
756
+        } catch (QueryException $e) {
757
+            $this->logger->logException($e);
758
+            return [];
759
+        }
760
+    }
761
+
762
+    /**
763
+     * @return ICheck[]
764
+     */
765
+    protected function getBuildInChecks(): array {
766
+        try {
767
+            return [
768
+                $this->container->query(FileMimeType::class),
769
+                $this->container->query(FileName::class),
770
+                $this->container->query(FileSize::class),
771
+                $this->container->query(FileSystemTags::class),
772
+                $this->container->query(RequestRemoteAddress::class),
773
+                $this->container->query(RequestTime::class),
774
+                $this->container->query(RequestURL::class),
775
+                $this->container->query(RequestUserAgent::class),
776
+                $this->container->query(UserGroupMembership::class),
777
+            ];
778
+        } catch (QueryException $e) {
779
+            $this->logger->logException($e);
780
+            return [];
781
+        }
782
+    }
783
+
784
+    public function isUserScopeEnabled(): bool {
785
+        return $this->config->getAppValue(Application::APP_ID, 'user_scope_disabled', 'no') === 'no';
786
+    }
787 787
 }
Please login to merge, or discard this patch.