Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 136
0.00% covered (danger)
0.00%
0 / 17
CRAP
0.00% covered (danger)
0.00%
0 / 1
GroupAssignment
0.00% covered (danger)
0.00%
0 / 136
0.00% covered (danger)
0.00%
0 / 17
2450
0.00% covered (danger)
0.00%
0 / 1
 getAssignments
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getAssignmentFactories
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
72
 getRuntime
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getAssignedUsers
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
 onDelete
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 getDeliveryIdsByUser
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
30
 isUserExcluded
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 getGuestAccessDeliveries
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 isDeliveryGuestUser
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isDeliveryExecutionAllowed
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 verifyUserAssigned
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
20
 hasDeliveryGuestAccess
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
20
 verifyToken
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
20
 verifyTime
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
20
 areWeInRange
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
20
 orderAssignments
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 getAssignmentFactory
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3/**
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; under version 2
7 * of the License (non-upgradable).
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 *
18 * Copyright (c) 2015 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT);
19 *
20 */
21
22namespace oat\taoDeliveryRdf\model;
23
24use oat\taoGroups\models\GroupsService;
25use oat\oatbox\user\User;
26use oat\oatbox\service\ConfigurableService;
27use core_kernel_classes_Resource;
28use core_kernel_classes_Class;
29use core_kernel_classes_Property;
30use oat\taoDelivery\model\AssignmentService;
31use oat\taoDeliveryRdf\model\guest\GuestTestUser;
32use oat\taoDelivery\model\RuntimeService;
33use oat\taoDelivery\model\AttemptServiceInterface;
34
35/**
36 * Service to manage the assignment of users to deliveries
37 *
38 * @access public
39 * @author Joel Bout, <joel@taotesting.com>
40 * @package taoDelivery
41 */
42class GroupAssignment extends ConfigurableService implements AssignmentService
43{
44    /**
45     * Interface part
46     */
47    public const PROPERTY_GROUP_DELIVERY = 'http://www.tao.lu/Ontologies/TAOGroup.rdf#Deliveries';
48
49    public const DISPLAY_ATTEMPTS_OPTION = 'display_attempts';
50
51    public const DISPLAY_DATES_OPTION = 'display_dates';
52
53    /**
54     * (non-PHPdoc)
55     * @see \oat\taoDelivery\model\AssignmentService::getAssignments()
56     */
57    public function getAssignments(User $user)
58    {
59        $assignments = [];
60        foreach ($this->getAssignmentFactories($user) as $factory) {
61            $assignments[] = $factory->toAssignment();
62        }
63
64        return $this->orderAssignments($assignments);
65    }
66
67    /**
68     * @param User $user
69     * @return array
70     */
71    public function getAssignmentFactories(User $user)
72    {
73        $assignments = [];
74
75        $displayAttempts = ($this->hasOption(self::DISPLAY_ATTEMPTS_OPTION))
76            ? $this->getOption(self::DISPLAY_ATTEMPTS_OPTION)
77            : true;
78
79        $displayDates = ($this->hasOption(self::DISPLAY_DATES_OPTION))
80            ? $this->getOption(self::DISPLAY_DATES_OPTION)
81            : true;
82
83        if ($this->isDeliveryGuestUser($user)) {
84            foreach ($this->getGuestAccessDeliveries() as $id) {
85                $delivery = new \core_kernel_classes_Resource($id);
86                $startable = $this->verifyTime($delivery) && $this->verifyToken($delivery, $user);
87                $assignments[] = $this->getAssignmentFactory(
88                    $delivery,
89                    $user,
90                    $startable,
91                    $displayAttempts,
92                    $displayDates
93                );
94            }
95        } else {
96            foreach ($this->getDeliveryIdsByUser($user) as $id) {
97                $delivery = new \core_kernel_classes_Resource($id);
98                $startable = $this->verifyTime($delivery) && $this->verifyToken($delivery, $user);
99                $assignments[] = $this->getAssignmentFactory(
100                    $delivery,
101                    $user,
102                    $startable,
103                    $displayAttempts,
104                    $displayDates
105                );
106            }
107        }
108        return $assignments;
109    }
110
111    /**
112     * @deprecated
113     */
114    public function getRuntime($deliveryId)
115    {
116        return $this->getServiceLocator()->get(RuntimeService::SERVICE_ID)->getRuntime($deliveryId);
117    }
118
119
120    /**
121     *
122     * @param string $deliveryId
123     * @return array identifiers of the users
124     */
125    public function getAssignedUsers($deliveryId)
126    {
127        $groupClass = GroupsService::singleton()->getRootClass();
128        $groups = $groupClass->searchInstances([
129            self::PROPERTY_GROUP_DELIVERY => $deliveryId
130        ], ['recursive' => true, 'like' => false]);
131
132        $users = [];
133        foreach ($groups as $group) {
134            foreach (GroupsService::singleton()->getUsers($group) as $user) {
135                $users[] = $user->getUri();
136            }
137        }
138        return array_unique($users);
139    }
140
141    /**
142     * Helpers
143     */
144    /**
145     * @param core_kernel_classes_Resource $delivery
146     */
147    public function onDelete(core_kernel_classes_Resource $delivery)
148    {
149        $groupClass = GroupsService::singleton()->getRootClass();
150        $assigned = $groupClass->searchInstances([
151            self::PROPERTY_GROUP_DELIVERY => $delivery
152        ], ['like' => false, 'recursive' => true]);
153
154        $assignationProperty = new core_kernel_classes_Property(self::PROPERTY_GROUP_DELIVERY);
155        foreach ($assigned as $groupInstance) {
156            $groupInstance->removePropertyValue($assignationProperty, $delivery);
157        }
158    }
159
160    /**
161     * @param User $user
162     * @return array
163     */
164    public function getDeliveryIdsByUser(User $user)
165    {
166        $deliveryUris = [];
167        // check if really available
168        foreach (GroupsService::singleton()->getGroups($user) as $group) {
169            $groupDeliveryUris = $group->getPropertyValues(
170                new \core_kernel_classes_Property(self::PROPERTY_GROUP_DELIVERY)
171            );
172
173            foreach ($groupDeliveryUris as $deliveryUri) {
174                $candidate = new core_kernel_classes_Resource($deliveryUri);
175                if (!$this->isUserExcluded($candidate, $user) && $candidate->exists()) {
176                    $deliveryUris[$candidate->getUri()] = $candidate->getUri();
177                }
178            }
179        }
180
181        ksort($deliveryUris);
182        return $deliveryUris;
183    }
184
185    /**
186     * Check if a user is excluded from a delivery
187     * @param core_kernel_classes_Resource $delivery
188     * @param User $user the URI of the user to check
189     * @return boolean true if excluded
190     */
191    protected function isUserExcluded(\core_kernel_classes_Resource $delivery, User $user)
192    {
193        $excludedUsers = $delivery->getPropertyValues(
194            new \core_kernel_classes_Property(DeliveryContainerService::PROPERTY_EXCLUDED_SUBJECTS)
195        );
196
197        return in_array($user->getIdentifier(), $excludedUsers);
198    }
199
200    /**
201     * Search for deliveries configured for guest access
202     *
203     * @return array
204     */
205    public function getGuestAccessDeliveries()
206    {
207        $class = new core_kernel_classes_Class(DeliveryAssemblyService::CLASS_URI);
208
209        return $class->searchInstances(
210            [
211                // phpcs:disable Generic.Files.LineLength
212                DeliveryContainerService::PROPERTY_ACCESS_SETTINGS => DeliveryAssemblyService::PROPERTY_DELIVERY_GUEST_ACCESS
213                // phpcs:enable Generic.Files.LineLength
214            ],
215            ['recursive' => true]
216        );
217    }
218
219    /**
220     * Check if current user is guest
221     *
222     * @param User $user
223     * @return bool
224     */
225    public function isDeliveryGuestUser(User $user)
226    {
227        return ($user instanceof GuestTestUser);
228    }
229
230    /**
231     * @param string $deliveryIdentifier
232     * @param User $user
233     * @return bool
234     */
235    public function isDeliveryExecutionAllowed($deliveryIdentifier, User $user)
236    {
237        $delivery = new \core_kernel_classes_Resource($deliveryIdentifier);
238        return $this->verifyUserAssigned($delivery, $user)
239            && $this->verifyTime($delivery)
240            && $this->verifyToken($delivery, $user);
241    }
242
243    /**
244     * @param core_kernel_classes_Resource $delivery
245     * @param User $user
246     * @return bool
247     */
248    protected function verifyUserAssigned(core_kernel_classes_Resource $delivery, User $user)
249    {
250        $returnValue = false;
251
252        //check for guest access mode
253        if ($this->isDeliveryGuestUser($user) && $this->hasDeliveryGuestAccess($delivery)) {
254            $returnValue = true;
255        } else {
256            $userGroups = GroupsService::singleton()->getGroups($user);
257            $deliveryGroups = GroupsService::singleton()->getRootClass()->searchInstances([
258                self::PROPERTY_GROUP_DELIVERY => $delivery->getUri()
259            ], [
260                'like' => false, 'recursive' => true
261            ]);
262            $returnValue = count(array_intersect($userGroups, $deliveryGroups)) > 0
263                && !$this->isUserExcluded($delivery, $user);
264        }
265
266        return $returnValue;
267    }
268
269    /**
270     * Check if delivery configured for guest access
271     *
272     * @param core_kernel_classes_Resource $delivery
273     * @return bool
274     * @throws \common_exception_InvalidArgumentType
275     */
276    protected function hasDeliveryGuestAccess(core_kernel_classes_Resource $delivery)
277    {
278        $returnValue = false;
279
280        $properties = $delivery->getPropertiesValues([
281            new core_kernel_classes_Property(DeliveryContainerService::PROPERTY_ACCESS_SETTINGS),
282        ]);
283        $propAccessSettings = current($properties[DeliveryContainerService::PROPERTY_ACCESS_SETTINGS ]);
284        $accessSetting = (!(is_object($propAccessSettings)) or ($propAccessSettings == ""))
285            ? null
286            : $propAccessSettings->getUri();
287
288        if (!is_null($accessSetting)) {
289            $returnValue = ($accessSetting === DeliveryAssemblyService::PROPERTY_DELIVERY_GUEST_ACCESS);
290        }
291
292        return $returnValue;
293    }
294
295    /**
296     * @param core_kernel_classes_Resource $delivery
297     * @param User $user
298     * @return bool
299     */
300    protected function verifyToken(core_kernel_classes_Resource $delivery, User $user)
301    {
302        $propMaxExec = $delivery->getOnePropertyValue(
303            new \core_kernel_classes_Property(DeliveryContainerService::PROPERTY_MAX_EXEC)
304        );
305        $maxExec = is_null($propMaxExec) ? 0 : $propMaxExec->literal;
306
307        //check Tokens
308        $usedTokens = count($this->getServiceLocator()->get(AttemptServiceInterface::SERVICE_ID)
309            ->getAttempts($delivery->getUri(), $user));
310
311        if (($maxExec != 0) && ($usedTokens >= $maxExec)) {
312            \common_Logger::d("Attempt to start the compiled delivery " . $delivery->getUri() . "without tokens");
313            return false;
314        }
315        return true;
316    }
317
318    /**
319     * @param core_kernel_classes_Resource $delivery
320     * @return bool
321     */
322    protected function verifyTime(core_kernel_classes_Resource $delivery)
323    {
324        $deliveryProps = $delivery->getPropertiesValues([
325            DeliveryContainerService::PROPERTY_START,
326            DeliveryContainerService::PROPERTY_END,
327        ]);
328
329        $startExec = empty($deliveryProps[DeliveryContainerService::PROPERTY_START])
330            ? null
331            : (string)current($deliveryProps[DeliveryContainerService::PROPERTY_START]);
332        $stopExec = empty($deliveryProps[DeliveryContainerService::PROPERTY_END])
333            ? null
334            : (string)current($deliveryProps[DeliveryContainerService::PROPERTY_END]);
335
336        $startDate  =    date_create('@' . $startExec);
337        $endDate    =    date_create('@' . $stopExec);
338        if (!$this->areWeInRange($startDate, $endDate)) {
339            \common_Logger::d("Attempt to start the compiled delivery " . $delivery->getUri() . " at the wrong date");
340            return false;
341        }
342        return true;
343    }
344
345    /**
346     * Check if the date are in range
347     * @param type $startDate
348     * @param type $endDate
349     * @return boolean true if in range
350     */
351    protected function areWeInRange($startDate, $endDate)
352    {
353        return (empty($startDate) || date_create() >= $startDate)
354        && (empty($endDate) || date_create() <= $endDate);
355    }
356
357    /**
358     * Order Assignments of a given user.
359     *
360     * By default, this method relies on the taoDelivery:DisplayOrder property
361     * to order the assignments (Ascending order). However, implementers extending
362     * the GroupAssignment class are encouraged to override this method if they need
363     * another behaviour.
364     *
365     * @param array $assignments An array of assignments.
366     * @return array The $assignments array ordered.
367     */
368    protected function orderAssignments(array $assignments)
369    {
370        usort($assignments, function ($a, $b) {
371            return $a->getDisplayOrder() - $b->getDisplayOrder();
372        });
373
374        return $assignments;
375    }
376
377    /**
378     * @param core_kernel_classes_Resource $delivery
379     * @param User $user
380     * @param $startable
381     * @param bool $displayAttempts
382     * @return AssignmentFactory
383     */
384    protected function getAssignmentFactory(
385        \core_kernel_classes_Resource $delivery,
386        User $user,
387        $startable,
388        $displayAttempts = true,
389        $displayDates = true
390    ) {
391        $factory = new AssignmentFactory($delivery, $user, $startable, $displayAttempts, $displayDates);
392        $factory->setServiceLocator($this->getServiceLocator());
393        return $factory;
394    }
395}