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