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