Completed
Push — 7.0 ( 145a1d...51d598 )
by André
14:33
created

UserService::updateUserGroup()   B

Complexity

Conditions 6
Paths 14

Size

Total Lines 41
Code Lines 24

Duplication

Lines 10
Ratio 24.39 %

Importance

Changes 0
Metric Value
cc 6
eloc 24
nc 14
nop 2
dl 10
loc 41
rs 8.439
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * File containing the eZ\Publish\Core\Repository\UserService class.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 */
9
namespace eZ\Publish\Core\Repository;
10
11
use eZ\Publish\API\Repository\Values\Content\LocationQuery;
12
use eZ\Publish\Core\Repository\Values\User\UserCreateStruct;
13
use eZ\Publish\API\Repository\Values\User\UserCreateStruct as APIUserCreateStruct;
14
use eZ\Publish\API\Repository\Values\User\UserUpdateStruct;
15
use eZ\Publish\Core\Repository\Values\User\User;
16
use eZ\Publish\API\Repository\Values\User\User as APIUser;
17
use eZ\Publish\Core\Repository\Values\User\UserGroup;
18
use eZ\Publish\API\Repository\Values\User\UserGroup as APIUserGroup;
19
use eZ\Publish\Core\Repository\Values\User\UserGroupCreateStruct;
20
use eZ\Publish\API\Repository\Values\User\UserGroupCreateStruct as APIUserGroupCreateStruct;
21
use eZ\Publish\API\Repository\Values\User\UserGroupUpdateStruct;
22
use eZ\Publish\API\Repository\Values\Content\Location;
23
use eZ\Publish\API\Repository\Values\Content\Content as APIContent;
24
use eZ\Publish\SPI\Persistence\User\Handler;
25
use eZ\Publish\API\Repository\Repository as RepositoryInterface;
26
use eZ\Publish\API\Repository\UserService as UserServiceInterface;
27
use eZ\Publish\SPI\Persistence\User as SPIUser;
28
use eZ\Publish\Core\FieldType\User\Value as UserValue;
29
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalAnd as CriterionLogicalAnd;
30
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ContentTypeId as CriterionContentTypeId;
31
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LocationId as CriterionLocationId;
32
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ParentLocationId as CriterionParentLocationId;
33
use eZ\Publish\Core\Base\Exceptions\ContentValidationException;
34
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue;
35
use eZ\Publish\Core\Base\Exceptions\BadStateException;
36
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException;
37
use eZ\Publish\Core\Base\Exceptions\NotFoundException;
38
use eZ\Publish\Core\Base\Exceptions\UnauthorizedException;
39
use Exception;
40
41
/**
42
 * This service provides methods for managing users and user groups.
43
 *
44
 * @example Examples/user.php
45
 */
46
class UserService implements UserServiceInterface
47
{
48
    /**
49
     * @var \eZ\Publish\API\Repository\Repository
50
     */
51
    protected $repository;
52
53
    /**
54
     * @var \eZ\Publish\SPI\Persistence\User\Handler
55
     */
56
    protected $userHandler;
57
58
    /**
59
     * @var array
60
     */
61
    protected $settings;
62
63
    /**
64
     * Setups service with reference to repository object that created it & corresponding handler.
65
     *
66
     * @param \eZ\Publish\API\Repository\Repository $repository
67
     * @param \eZ\Publish\SPI\Persistence\User\Handler $userHandler
68
     * @param array $settings
69
     */
70
    public function __construct(RepositoryInterface $repository, Handler $userHandler, array $settings = array())
71
    {
72
        $this->repository = $repository;
73
        $this->userHandler = $userHandler;
74
        // Union makes sure default settings are ignored if provided in argument
75
        $this->settings = $settings + array(
76
            'defaultUserPlacement' => 12,
77
            'userClassID' => 4, // @todo Rename this settings to swap out "Class" for "Type"
78
            'userGroupClassID' => 3,
79
            'hashType' => User::PASSWORD_HASH_MD5_USER,
80
            'siteName' => 'ez.no',
81
        );
82
    }
83
84
    /**
85
     * Creates a new user group using the data provided in the ContentCreateStruct parameter.
86
     *
87
     * In 4.x in the content type parameter in the profile is ignored
88
     * - the content type is determined via configuration and can be set to null.
89
     * The returned version is published.
90
     *
91
     * @param \eZ\Publish\API\Repository\Values\User\UserGroupCreateStruct $userGroupCreateStruct a structure for setting all necessary data to create this user group
92
     * @param \eZ\Publish\API\Repository\Values\User\UserGroup $parentGroup
93
     *
94
     * @return \eZ\Publish\API\Repository\Values\User\UserGroup
95
     *
96
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to create a user group
97
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the input structure has invalid data
98
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $userGroupCreateStruct is not valid
99
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException if a required field is missing or set to an empty value
100
     */
101
    public function createUserGroup(APIUserGroupCreateStruct $userGroupCreateStruct, APIUserGroup $parentGroup)
102
    {
103
        $contentService = $this->repository->getContentService();
104
        $locationService = $this->repository->getLocationService();
105
        $contentTypeService = $this->repository->getContentTypeService();
106
107
        if ($userGroupCreateStruct->contentType === null) {
108
            $userGroupContentType = $contentTypeService->loadContentType($this->settings['userGroupClassID']);
109
            $userGroupCreateStruct->contentType = $userGroupContentType;
110
        }
111
112
        $loadedParentGroup = $this->loadUserGroup($parentGroup->id);
113
114
        if ($loadedParentGroup->getVersionInfo()->getContentInfo()->mainLocationId === null) {
115
            throw new InvalidArgumentException('parentGroup', 'parent user group has no main location');
116
        }
117
118
        $locationCreateStruct = $locationService->newLocationCreateStruct(
119
            $loadedParentGroup->getVersionInfo()->getContentInfo()->mainLocationId
120
        );
121
122
        $this->repository->beginTransaction();
123
        try {
124
            $contentDraft = $contentService->createContent($userGroupCreateStruct, array($locationCreateStruct));
125
            $publishedContent = $contentService->publishVersion($contentDraft->getVersionInfo());
126
            $this->repository->commit();
127
        } catch (Exception $e) {
128
            $this->repository->rollback();
129
            throw $e;
130
        }
131
132
        return $this->buildDomainUserGroupObject($publishedContent);
133
    }
134
135
    /**
136
     * Loads a user group for the given id.
137
     *
138
     * @param mixed $id
139
     * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object.
140
     *
141
     * @return \eZ\Publish\API\Repository\Values\User\UserGroup
142
     *
143
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to create a user group
144
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the user group with the given id was not found
145
     */
146
    public function loadUserGroup($id, array $prioritizedLanguages = [])
147
    {
148
        $content = $this->repository->getContentService()->loadContent($id, $prioritizedLanguages);
149
150
        return $this->buildDomainUserGroupObject($content);
151
    }
152
153
    /**
154
     * Loads the sub groups of a user group.
155
     *
156
     * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup
157
     * @param int $offset the start offset for paging
158
     * @param int $limit the number of user groups returned
159
     * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object.
160
     *
161
     * @return \eZ\Publish\API\Repository\Values\User\UserGroup[]
162
     *
163
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read the user group
164
     */
165
    public function loadSubUserGroups(APIUserGroup $userGroup, $offset = 0, $limit = 25, array $prioritizedLanguages = [])
166
    {
167
        $locationService = $this->repository->getLocationService();
168
169
        $loadedUserGroup = $this->loadUserGroup($userGroup->id);
170
        if (!$this->repository->canUser('content', 'read', $loadedUserGroup)) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Indicates if the current user is allowed to perform an action given by the function on the given
objects. Example: canUser( 'content', 'edit', $content, $location ); This will check edit permission on content given the specific location, if skipped if will check on all locations. Example2: canUser( 'section', 'assign', $content, $section ); Check if user has access to assign $content to $section.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
171
            throw new UnauthorizedException('content', 'read');
172
        }
173
174
        if ($loadedUserGroup->getVersionInfo()->getContentInfo()->mainLocationId === null) {
175
            return array();
176
        }
177
178
        $mainGroupLocation = $locationService->loadLocation(
179
            $loadedUserGroup->getVersionInfo()->getContentInfo()->mainLocationId
180
        );
181
182
        $searchResult = $this->searchSubGroups($mainGroupLocation, $offset, $limit);
183
        if ($searchResult->totalCount == 0) {
184
            return array();
185
        }
186
187
        $subUserGroups = array();
188 View Code Duplication
        foreach ($searchResult->searchHits as $searchHit) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
189
            $subUserGroups[] = $this->buildDomainUserGroupObject(
190
                $this->repository->getContentService()->internalLoadContent(
0 ignored issues
show
Bug introduced by
The method internalLoadContent() does not exist on eZ\Publish\API\Repository\ContentService. Did you maybe mean loadContent()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
191
                    $searchHit->valueObject->contentInfo->id,
0 ignored issues
show
Documentation introduced by
The property contentInfo does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
192
                    $prioritizedLanguages
193
                )
194
            );
195
        }
196
197
        return $subUserGroups;
198
    }
199
200
    /**
201
     * Returns (searches) subgroups of a user group described by its main location.
202
     *
203
     * @param \eZ\Publish\API\Repository\Values\Content\Location $location
204
     * @param int $offset
205
     * @param int $limit
206
     *
207
     * @return \eZ\Publish\API\Repository\Values\Content\Search\SearchResult
208
     */
209
    protected function searchSubGroups(Location $location, $offset = 0, $limit = 25)
210
    {
211
        $searchQuery = new LocationQuery();
212
213
        $searchQuery->offset = $offset;
214
        $searchQuery->limit = $limit;
215
216
        $searchQuery->filter = new CriterionLogicalAnd([
217
            new CriterionContentTypeId($this->settings['userGroupClassID']),
218
            new CriterionParentLocationId($location->id),
219
        ]);
220
221
        $searchQuery->sortClauses = $location->getSortClauses();
0 ignored issues
show
Documentation Bug introduced by
It seems like $location->getSortClauses() of type array<integer,object,{"0":"object"}> is incompatible with the declared type array<integer,object<eZ\...tent\Query\SortClause>> of property $sortClauses.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
222
223
        return $this->repository->getSearchService()->findLocations($searchQuery, array(), false);
224
    }
225
226
    /**
227
     * Removes a user group.
228
     *
229
     * the users which are not assigned to other groups will be deleted.
230
     *
231
     * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup
232
     *
233
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to create a user group
234
     */
235 View Code Duplication
    public function deleteUserGroup(APIUserGroup $userGroup)
236
    {
237
        $loadedUserGroup = $this->loadUserGroup($userGroup->id);
238
239
        $this->repository->beginTransaction();
240
        try {
241
            //@todo: what happens to sub user groups and users below sub user groups
242
            $affectedLocationIds = $this->repository->getContentService()->deleteContent($loadedUserGroup->getVersionInfo()->getContentInfo());
243
            $this->repository->commit();
244
        } catch (Exception $e) {
245
            $this->repository->rollback();
246
            throw $e;
247
        }
248
249
        return $affectedLocationIds;
250
    }
251
252
    /**
253
     * Moves the user group to another parent.
254
     *
255
     * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup
256
     * @param \eZ\Publish\API\Repository\Values\User\UserGroup $newParent
257
     *
258
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to move the user group
259
     */
260
    public function moveUserGroup(APIUserGroup $userGroup, APIUserGroup $newParent)
261
    {
262
        $loadedUserGroup = $this->loadUserGroup($userGroup->id);
263
        $loadedNewParent = $this->loadUserGroup($newParent->id);
264
265
        $locationService = $this->repository->getLocationService();
266
267
        if ($loadedUserGroup->getVersionInfo()->getContentInfo()->mainLocationId === null) {
268
            throw new BadStateException('userGroup', 'existing user group is not stored and/or does not have any location yet');
269
        }
270
271
        if ($loadedNewParent->getVersionInfo()->getContentInfo()->mainLocationId === null) {
272
            throw new BadStateException('newParent', 'new user group is not stored and/or does not have any location yet');
273
        }
274
275
        $userGroupMainLocation = $locationService->loadLocation(
276
            $loadedUserGroup->getVersionInfo()->getContentInfo()->mainLocationId
277
        );
278
        $newParentMainLocation = $locationService->loadLocation(
279
            $loadedNewParent->getVersionInfo()->getContentInfo()->mainLocationId
280
        );
281
282
        $this->repository->beginTransaction();
283
        try {
284
            $locationService->moveSubtree($userGroupMainLocation, $newParentMainLocation);
285
            $this->repository->commit();
286
        } catch (Exception $e) {
287
            $this->repository->rollback();
288
            throw $e;
289
        }
290
    }
291
292
    /**
293
     * Updates the group profile with fields and meta data.
294
     *
295
     * 4.x: If the versionUpdateStruct is set in $userGroupUpdateStruct, this method internally creates a content draft, updates ts with the provided data
296
     * and publishes the draft. If a draft is explicitly required, the user group can be updated via the content service methods.
297
     *
298
     * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup
299
     * @param \eZ\Publish\API\Repository\Values\User\UserGroupUpdateStruct $userGroupUpdateStruct
300
     *
301
     * @return \eZ\Publish\API\Repository\Values\User\UserGroup
302
     *
303
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to update the user group
304
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $userGroupUpdateStruct is not valid
305
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException if a required field is set empty
306
     */
307
    public function updateUserGroup(APIUserGroup $userGroup, UserGroupUpdateStruct $userGroupUpdateStruct)
308
    {
309
        if ($userGroupUpdateStruct->contentUpdateStruct === null &&
310
            $userGroupUpdateStruct->contentMetadataUpdateStruct === null) {
311
            // both update structs are empty, nothing to do
312
            return $userGroup;
313
        }
314
315
        $contentService = $this->repository->getContentService();
316
317
        $loadedUserGroup = $this->loadUserGroup($userGroup->id);
318
319
        $this->repository->beginTransaction();
320
        try {
321
            $publishedContent = $loadedUserGroup;
322 View Code Duplication
            if ($userGroupUpdateStruct->contentUpdateStruct !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
323
                $contentDraft = $contentService->createContentDraft($loadedUserGroup->getVersionInfo()->getContentInfo());
324
325
                $contentDraft = $contentService->updateContent(
326
                    $contentDraft->getVersionInfo(),
327
                    $userGroupUpdateStruct->contentUpdateStruct
328
                );
329
330
                $publishedContent = $contentService->publishVersion($contentDraft->getVersionInfo());
331
            }
332
333
            if ($userGroupUpdateStruct->contentMetadataUpdateStruct !== null) {
334
                $publishedContent = $contentService->updateContentMetadata(
335
                    $publishedContent->getVersionInfo()->getContentInfo(),
336
                    $userGroupUpdateStruct->contentMetadataUpdateStruct
337
                );
338
            }
339
340
            $this->repository->commit();
341
        } catch (Exception $e) {
342
            $this->repository->rollback();
343
            throw $e;
344
        }
345
346
        return $this->buildDomainUserGroupObject($publishedContent);
347
    }
348
349
    /**
350
     * Create a new user. The created user is published by this method.
351
     *
352
     * @param \eZ\Publish\API\Repository\Values\User\UserCreateStruct $userCreateStruct the data used for creating the user
353
     * @param \eZ\Publish\API\Repository\Values\User\UserGroup[] $parentGroups the groups which are assigned to the user after creation
354
     *
355
     * @return \eZ\Publish\API\Repository\Values\User\User
356
     *
357
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to move the user group
358
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $userCreateStruct is not valid
359
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException if a required field is missing or set to an empty value
360
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if a user with provided login already exists
361
     */
362
    public function createUser(APIUserCreateStruct $userCreateStruct, array $parentGroups)
363
    {
364
        if (empty($parentGroups)) {
365
            throw new InvalidArgumentValue('parentGroups', $parentGroups);
366
        }
367
368
        if (!is_string($userCreateStruct->login) || empty($userCreateStruct->login)) {
369
            throw new InvalidArgumentValue('login', $userCreateStruct->login, 'UserCreateStruct');
370
        }
371
372 View Code Duplication
        if (!is_string($userCreateStruct->email) || empty($userCreateStruct->email)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
373
            throw new InvalidArgumentValue('email', $userCreateStruct->email, 'UserCreateStruct');
374
        }
375
376
        if (!preg_match('/^.+@.+\..+$/', $userCreateStruct->email)) {
377
            throw new InvalidArgumentValue('email', $userCreateStruct->email, 'UserCreateStruct');
378
        }
379
380
        if (!is_string($userCreateStruct->password) || empty($userCreateStruct->password)) {
381
            throw new InvalidArgumentValue('password', $userCreateStruct->password, 'UserCreateStruct');
382
        }
383
384
        if (!is_bool($userCreateStruct->enabled)) {
385
            throw new InvalidArgumentValue('enabled', $userCreateStruct->enabled, 'UserCreateStruct');
386
        }
387
388
        try {
389
            $this->userHandler->loadByLogin($userCreateStruct->login);
390
            throw new InvalidArgumentException('userCreateStruct', 'User with provided login already exists');
391
        } catch (NotFoundException $e) {
392
            // Do nothing
393
        }
394
395
        $contentService = $this->repository->getContentService();
396
        $locationService = $this->repository->getLocationService();
397
        $contentTypeService = $this->repository->getContentTypeService();
398
399
        if ($userCreateStruct->contentType === null) {
400
            $userContentType = $contentTypeService->loadContentType($this->settings['userClassID']);
401
            $userCreateStruct->contentType = $userContentType;
402
        }
403
404
        $locationCreateStructs = array();
405
        foreach ($parentGroups as $parentGroup) {
406
            $parentGroup = $this->loadUserGroup($parentGroup->id);
407
            if ($parentGroup->getVersionInfo()->getContentInfo()->mainLocationId !== null) {
408
                $locationCreateStructs[] = $locationService->newLocationCreateStruct(
409
                    $parentGroup->getVersionInfo()->getContentInfo()->mainLocationId
410
                );
411
            }
412
        }
413
414
        // Search for the first ezuser field type in content type
415
        $userFieldDefinition = null;
416
        foreach ($userCreateStruct->contentType->getFieldDefinitions() as $fieldDefinition) {
417
            if ($fieldDefinition->fieldTypeIdentifier == 'ezuser') {
418
                $userFieldDefinition = $fieldDefinition;
419
                break;
420
            }
421
        }
422
423
        if ($userFieldDefinition === null) {
424
            throw new ContentValidationException('Provided content type does not contain ezuser field type');
425
        }
426
427
        $fixUserFieldType = true;
428
        foreach ($userCreateStruct->fields as $index => $field) {
429
            if ($field->fieldDefIdentifier == $userFieldDefinition->identifier) {
430
                if ($field->value instanceof UserValue) {
0 ignored issues
show
Documentation introduced by
The property $value is declared protected in eZ\Publish\API\Repository\Values\Content\Field. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
431
                    $userCreateStruct->fields[$index]->value->login = $userCreateStruct->login;
0 ignored issues
show
Documentation introduced by
The property $value is declared protected in eZ\Publish\API\Repository\Values\Content\Field. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
432
                } else {
433
                    $userCreateStruct->fields[$index]->value = new UserValue(
0 ignored issues
show
Documentation introduced by
The property $value is declared protected in eZ\Publish\API\Repository\Values\Content\Field. Since you implemented __set(), maybe consider adding a @property or @property-write annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
434
                        array(
435
                            'login' => $userCreateStruct->login,
436
                        )
437
                    );
438
                }
439
440
                $fixUserFieldType = false;
441
            }
442
        }
443
444
        if ($fixUserFieldType) {
445
            $userCreateStruct->setField(
446
                $userFieldDefinition->identifier,
447
                new UserValue(
448
                    array(
449
                        'login' => $userCreateStruct->login,
450
                    )
451
                )
452
            );
453
        }
454
455
        $this->repository->beginTransaction();
456
        try {
457
            $contentDraft = $contentService->createContent($userCreateStruct, $locationCreateStructs);
458
            // Create user before publishing, so that external data can be returned
459
            $spiUser = $this->userHandler->create(
460
                new SPIUser(
461
                    array(
462
                        'id' => $contentDraft->id,
463
                        'login' => $userCreateStruct->login,
464
                        'email' => $userCreateStruct->email,
465
                        'passwordHash' => $this->createPasswordHash(
466
                            $userCreateStruct->login,
467
                            $userCreateStruct->password,
468
                            $this->settings['siteName'],
469
                            $this->settings['hashType']
470
                        ),
471
                        'hashAlgorithm' => $this->settings['hashType'],
472
                        'isEnabled' => $userCreateStruct->enabled,
473
                        'maxLogin' => 0,
474
                    )
475
                )
476
            );
477
            $publishedContent = $contentService->publishVersion($contentDraft->getVersionInfo());
478
479
            $this->repository->commit();
480
        } catch (Exception $e) {
481
            $this->repository->rollback();
482
            throw $e;
483
        }
484
485
        return $this->buildDomainUserObject($spiUser, $publishedContent);
486
    }
487
488
    /**
489
     * Loads a user.
490
     *
491
     * @param mixed $userId
492
     * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object.
493
     *
494
     * @return \eZ\Publish\API\Repository\Values\User\User
495
     *
496
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if a user with the given id was not found
497
     */
498
    public function loadUser($userId, array $prioritizedLanguages = [])
499
    {
500
        /** @var \eZ\Publish\API\Repository\Values\Content\Content $content */
501
        $content = $this->repository->getContentService()->internalLoadContent($userId, $prioritizedLanguages);
0 ignored issues
show
Bug introduced by
The method internalLoadContent() does not exist on eZ\Publish\API\Repository\ContentService. Did you maybe mean loadContent()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
502
        // Get spiUser value from Field Value
503
        foreach ($content->getFields() as $field) {
504
            if (!$field->value instanceof UserValue) {
0 ignored issues
show
Documentation introduced by
The property $value is declared protected in eZ\Publish\API\Repository\Values\Content\Field. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
505
                continue;
506
            }
507
508
            /** @var \eZ\Publish\Core\FieldType\User\Value $value */
509
            $value = $field->value;
0 ignored issues
show
Documentation introduced by
The property $value is declared protected in eZ\Publish\API\Repository\Values\Content\Field. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
510
            $spiUser = new SPIUser();
511
            $spiUser->id = $value->contentId;
512
            $spiUser->login = $value->login;
513
            $spiUser->email = $value->email;
514
            $spiUser->hashAlgorithm = $value->passwordHashType;
515
            $spiUser->passwordHash = $value->passwordHash;
516
            $spiUser->isEnabled = $value->enabled;
517
            $spiUser->maxLogin = $value->maxLogin;
518
            break;
519
        }
520
521
        // If for some reason not found, load it
522
        if (!isset($spiUser)) {
523
            $spiUser = $this->userHandler->load($userId);
524
        }
525
526
        return $this->buildDomainUserObject($spiUser, $content);
527
    }
528
529
    /**
530
     * Loads anonymous user.
531
     *
532
     * @deprecated since 5.3, use loadUser( $anonymousUserId ) instead
533
     *
534
     * @uses ::loadUser()
535
     *
536
     * @return \eZ\Publish\API\Repository\Values\User\User
537
     */
538
    public function loadAnonymousUser()
539
    {
540
        return $this->loadUser($this->settings['anonymousUserID']);
541
    }
542
543
    /**
544
     * Loads a user for the given login and password.
545
     *
546
     * {@inheritdoc}
547
     *
548
     * @param string $login
549
     * @param string $password the plain password
550
     * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object.
551
     *
552
     * @return \eZ\Publish\API\Repository\Values\User\User
553
     *
554
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if credentials are invalid
555
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if a user with the given credentials was not found
556
     */
557
    public function loadUserByCredentials($login, $password, array $prioritizedLanguages = [])
558
    {
559
        if (!is_string($login) || empty($login)) {
560
            throw new InvalidArgumentValue('login', $login);
561
        }
562
563
        if (!is_string($password)) {
564
            throw new InvalidArgumentValue('password', $password);
565
        }
566
567
        // Randomize login time to protect against timing attacks
568
        usleep(mt_rand(0, 30000));
569
570
        $spiUser = $this->userHandler->loadByLogin($login);
571
        $passwordHash = $this->createPasswordHash(
572
            $login,
573
            $password,
574
            $this->settings['siteName'],
575
            $spiUser->hashAlgorithm
576
        );
577
578
        if ($spiUser->passwordHash !== $passwordHash) {
579
            throw new NotFoundException('user', $login);
580
        }
581
582
        return $this->buildDomainUserObject($spiUser, null, $prioritizedLanguages);
583
    }
584
585
    /**
586
     * Loads a user for the given login.
587
     *
588
     * {@inheritdoc}
589
     *
590
     * @param string $login
591
     * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object.
592
     *
593
     * @return \eZ\Publish\API\Repository\Values\User\User
594
     *
595
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if a user with the given credentials was not found
596
     */
597
    public function loadUserByLogin($login, array $prioritizedLanguages = [])
598
    {
599
        if (!is_string($login) || empty($login)) {
600
            throw new InvalidArgumentValue('login', $login);
601
        }
602
603
        $spiUser = $this->userHandler->loadByLogin($login);
604
605
        return $this->buildDomainUserObject($spiUser, null, $prioritizedLanguages);
606
    }
607
608
    /**
609
     * Loads a user for the given email.
610
     *
611
     * {@inheritdoc}
612
     *
613
     * @param string $email
614
     * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object.
615
     *
616
     * @return \eZ\Publish\API\Repository\Values\User\User[]
617
     */
618
    public function loadUsersByEmail($email, array $prioritizedLanguages = [])
619
    {
620
        if (!is_string($email) || empty($email)) {
621
            throw new InvalidArgumentValue('email', $email);
622
        }
623
624
        $users = array();
625
        foreach ($this->userHandler->loadByEmail($email) as $spiUser) {
626
            $users[] = $this->buildDomainUserObject($spiUser, null, $prioritizedLanguages);
627
        }
628
629
        return $users;
630
    }
631
632
    /**
633
     * This method deletes a user.
634
     *
635
     * @param \eZ\Publish\API\Repository\Values\User\User $user
636
     *
637
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to delete the user
638
     */
639 View Code Duplication
    public function deleteUser(APIUser $user)
640
    {
641
        $loadedUser = $this->loadUser($user->id);
642
643
        $this->repository->beginTransaction();
644
        try {
645
            $affectedLocationIds = $this->repository->getContentService()->deleteContent($loadedUser->getVersionInfo()->getContentInfo());
646
            $this->userHandler->delete($loadedUser->id);
647
            $this->repository->commit();
648
        } catch (Exception $e) {
649
            $this->repository->rollback();
650
            throw $e;
651
        }
652
653
        return $affectedLocationIds;
654
    }
655
656
    /**
657
     * Updates a user.
658
     *
659
     * 4.x: If the versionUpdateStruct is set in the user update structure, this method internally creates a content draft, updates ts with the provided data
660
     * and publishes the draft. If a draft is explicitly required, the user group can be updated via the content service methods.
661
     *
662
     * @param \eZ\Publish\API\Repository\Values\User\User $user
663
     * @param \eZ\Publish\API\Repository\Values\User\UserUpdateStruct $userUpdateStruct
664
     *
665
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to update the user
666
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $userUpdateStruct is not valid
667
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException if a required field is set empty
668
     *
669
     * @return \eZ\Publish\API\Repository\Values\User\User
670
     */
671
    public function updateUser(APIUser $user, UserUpdateStruct $userUpdateStruct)
672
    {
673
        $loadedUser = $this->loadUser($user->id);
674
675
        // We need to determine if we have anything to update.
676
        // UserUpdateStruct is specific as some of the new content is in
677
        // content update struct and some of it is in additional fields like
678
        // email, password and so on
679
        $doUpdate = false;
680
        foreach ($userUpdateStruct as $propertyValue) {
0 ignored issues
show
Bug introduced by
The expression $userUpdateStruct of type object<eZ\Publish\API\Re...\User\UserUpdateStruct> is not traversable.
Loading history...
681
            if ($propertyValue !== null) {
682
                $doUpdate = true;
683
                break;
684
            }
685
        }
686
687
        if (!$doUpdate) {
688
            // Nothing to update, so we just quit
689
            return $user;
690
        }
691
692
        if ($userUpdateStruct->email !== null) {
693 View Code Duplication
            if (!is_string($userUpdateStruct->email) || empty($userUpdateStruct->email)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
694
                throw new InvalidArgumentValue('email', $userUpdateStruct->email, 'UserUpdateStruct');
695
            }
696
697
            if (!preg_match('/^.+@.+\..+$/', $userUpdateStruct->email)) {
698
                throw new InvalidArgumentValue('email', $userUpdateStruct->email, 'UserUpdateStruct');
699
            }
700
        }
701
702 View Code Duplication
        if ($userUpdateStruct->password !== null && (!is_string($userUpdateStruct->password) || empty($userUpdateStruct->password))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
703
            throw new InvalidArgumentValue('password', $userUpdateStruct->password, 'UserUpdateStruct');
704
        }
705
706
        if ($userUpdateStruct->enabled !== null && !is_bool($userUpdateStruct->enabled)) {
707
            throw new InvalidArgumentValue('enabled', $userUpdateStruct->enabled, 'UserUpdateStruct');
708
        }
709
710
        if ($userUpdateStruct->maxLogin !== null && !is_int($userUpdateStruct->maxLogin)) {
711
            throw new InvalidArgumentValue('maxLogin', $userUpdateStruct->maxLogin, 'UserUpdateStruct');
712
        }
713
714
        $contentService = $this->repository->getContentService();
715
716
        if (!$this->repository->canUser('content', 'edit', $loadedUser)) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Indicates if the current user is allowed to perform an action given by the function on the given
objects. Example: canUser( 'content', 'edit', $content, $location ); This will check edit permission on content given the specific location, if skipped if will check on all locations. Example2: canUser( 'section', 'assign', $content, $section ); Check if user has access to assign $content to $section.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
717
            throw new UnauthorizedException('content', 'edit');
718
        }
719
720
        $this->repository->beginTransaction();
721
        try {
722
            $publishedContent = $loadedUser;
723 View Code Duplication
            if ($userUpdateStruct->contentUpdateStruct !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
724
                $contentDraft = $contentService->createContentDraft($loadedUser->getVersionInfo()->getContentInfo());
725
                $contentDraft = $contentService->updateContent(
726
                    $contentDraft->getVersionInfo(),
727
                    $userUpdateStruct->contentUpdateStruct
728
                );
729
                $publishedContent = $contentService->publishVersion($contentDraft->getVersionInfo());
730
            }
731
732
            if ($userUpdateStruct->contentMetadataUpdateStruct !== null) {
733
                $contentService->updateContentMetadata(
734
                    $publishedContent->getVersionInfo()->getContentInfo(),
735
                    $userUpdateStruct->contentMetadataUpdateStruct
736
                );
737
            }
738
739
            $this->userHandler->update(
740
                new SPIUser(
741
                    array(
742
                        'id' => $loadedUser->id,
743
                        'login' => $loadedUser->login,
0 ignored issues
show
Documentation introduced by
The property $login is declared protected in eZ\Publish\API\Repository\Values\User\User. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
744
                        'email' => $userUpdateStruct->email ?: $loadedUser->email,
0 ignored issues
show
Documentation introduced by
The property $email is declared protected in eZ\Publish\API\Repository\Values\User\User. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
745
                        'passwordHash' => $userUpdateStruct->password ?
746
                            $this->createPasswordHash(
747
                                $loadedUser->login,
0 ignored issues
show
Documentation introduced by
The property $login is declared protected in eZ\Publish\API\Repository\Values\User\User. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
748
                                $userUpdateStruct->password,
749
                                $this->settings['siteName'],
750
                                $this->settings['hashType']
751
                            ) :
752
                            $loadedUser->passwordHash,
0 ignored issues
show
Documentation introduced by
The property $passwordHash is declared protected in eZ\Publish\API\Repository\Values\User\User. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
753
                        'hashAlgorithm' => $this->settings['hashType'],
754
                        'isEnabled' => $userUpdateStruct->enabled !== null ? $userUpdateStruct->enabled : $loadedUser->enabled,
0 ignored issues
show
Documentation introduced by
The property $enabled is declared protected in eZ\Publish\API\Repository\Values\User\User. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
755
                        'maxLogin' => $userUpdateStruct->maxLogin !== null ? (int)$userUpdateStruct->maxLogin : $loadedUser->maxLogin,
0 ignored issues
show
Documentation introduced by
The property $maxLogin is declared protected in eZ\Publish\API\Repository\Values\User\User. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
756
                    )
757
                )
758
            );
759
760
            $this->repository->commit();
761
        } catch (Exception $e) {
762
            $this->repository->rollback();
763
            throw $e;
764
        }
765
766
        return $this->loadUser($loadedUser->id);
767
    }
768
769
    /**
770
     * Assigns a new user group to the user.
771
     *
772
     * @param \eZ\Publish\API\Repository\Values\User\User $user
773
     * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup
774
     *
775
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to assign the user group to the user
776
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the user is already in the given user group
777
     */
778
    public function assignUserToUserGroup(APIUser $user, APIUserGroup $userGroup)
779
    {
780
        $loadedUser = $this->loadUser($user->id);
781
        $loadedGroup = $this->loadUserGroup($userGroup->id);
782
        $locationService = $this->repository->getLocationService();
783
784
        $existingGroupIds = array();
785
        $userLocations = $locationService->loadLocations($loadedUser->getVersionInfo()->getContentInfo());
786
        foreach ($userLocations as $userLocation) {
787
            $existingGroupIds[] = $userLocation->parentLocationId;
788
        }
789
790
        if ($loadedGroup->getVersionInfo()->getContentInfo()->mainLocationId === null) {
791
            throw new BadStateException('userGroup', 'user group has no main location or no locations');
792
        }
793
794
        if (in_array($loadedGroup->getVersionInfo()->getContentInfo()->mainLocationId, $existingGroupIds)) {
795
            // user is already assigned to the user group
796
            throw new InvalidArgumentException('user', 'user is already in the given user group');
797
        }
798
799
        $locationCreateStruct = $locationService->newLocationCreateStruct(
800
            $loadedGroup->getVersionInfo()->getContentInfo()->mainLocationId
801
        );
802
803
        $this->repository->beginTransaction();
804
        try {
805
            $locationService->createLocation(
806
                $loadedUser->getVersionInfo()->getContentInfo(),
807
                $locationCreateStruct
808
            );
809
            $this->repository->commit();
810
        } catch (Exception $e) {
811
            $this->repository->rollback();
812
            throw $e;
813
        }
814
    }
815
816
    /**
817
     * Removes a user group from the user.
818
     *
819
     * @param \eZ\Publish\API\Repository\Values\User\User $user
820
     * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup
821
     *
822
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to remove the user group from the user
823
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the user is not in the given user group
824
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If $userGroup is the last assigned user group
825
     */
826
    public function unAssignUserFromUserGroup(APIUser $user, APIUserGroup $userGroup)
827
    {
828
        $loadedUser = $this->loadUser($user->id);
829
        $loadedGroup = $this->loadUserGroup($userGroup->id);
830
        $locationService = $this->repository->getLocationService();
831
832
        $userLocations = $locationService->loadLocations($loadedUser->getVersionInfo()->getContentInfo());
833
        if (empty($userLocations)) {
834
            throw new BadStateException('user', 'user has no locations, cannot unassign from group');
835
        }
836
837
        if ($loadedGroup->getVersionInfo()->getContentInfo()->mainLocationId === null) {
838
            throw new BadStateException('userGroup', 'user group has no main location or no locations, cannot unassign');
839
        }
840
841
        foreach ($userLocations as $userLocation) {
842
            if ($userLocation->parentLocationId == $loadedGroup->getVersionInfo()->getContentInfo()->mainLocationId) {
843
                // Throw this specific BadState when we know argument is valid
844
                if (count($userLocations) === 1) {
845
                    throw new BadStateException('user', 'user only has one user group, cannot unassign from last group');
846
                }
847
848
                $this->repository->beginTransaction();
849
                try {
850
                    $locationService->deleteLocation($userLocation);
851
                    $this->repository->commit();
852
853
                    return;
854
                } catch (Exception $e) {
855
                    $this->repository->rollback();
856
                    throw $e;
857
                }
858
            }
859
        }
860
861
        throw new InvalidArgumentException('userGroup', 'user is not in the given user group');
862
    }
863
864
    /**
865
     * Loads the user groups the user belongs to.
866
     *
867
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed read the user or user group
868
     *
869
     * @param \eZ\Publish\API\Repository\Values\User\User $user
870
     * @param int $offset the start offset for paging
871
     * @param int $limit the number of user groups returned
872
     * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object.
873
     *
874
     * @return \eZ\Publish\API\Repository\Values\User\UserGroup[]
875
     */
876
    public function loadUserGroupsOfUser(APIUser $user, $offset = 0, $limit = 25, array $prioritizedLanguages = [])
877
    {
878
        $locationService = $this->repository->getLocationService();
879
880
        if (!$this->repository->getPermissionResolver()->canUser('content', 'read', $user)) {
881
            throw new UnauthorizedException('content', 'read');
882
        }
883
884
        $userLocations = $locationService->loadLocations(
885
            $user->getVersionInfo()->getContentInfo()
886
        );
887
888
        $parentLocationIds = array();
889
        foreach ($userLocations as $userLocation) {
890
            if ($userLocation->parentLocationId !== null) {
891
                $parentLocationIds[] = $userLocation->parentLocationId;
892
            }
893
        }
894
895
        $searchQuery = new LocationQuery();
896
897
        $searchQuery->offset = $offset;
898
        $searchQuery->limit = $limit;
899
        $searchQuery->performCount = false;
900
901
        $searchQuery->filter = new CriterionLogicalAnd(
902
            [
903
                new CriterionContentTypeId($this->settings['userGroupClassID']),
904
                new CriterionLocationId($parentLocationIds),
905
            ]
906
        );
907
908
        $searchResult = $this->repository->getSearchService()->findLocations($searchQuery);
909
910
        $userGroups = [];
911 View Code Duplication
        foreach ($searchResult->searchHits as $resultItem) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
912
            $userGroups[] = $this->buildDomainUserGroupObject(
913
                $this->repository->getContentService()->internalLoadContent(
0 ignored issues
show
Bug introduced by
The method internalLoadContent() does not exist on eZ\Publish\API\Repository\ContentService. Did you maybe mean loadContent()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
914
                    $resultItem->valueObject->contentInfo->id,
0 ignored issues
show
Documentation introduced by
The property contentInfo does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
915
                    $prioritizedLanguages
916
                )
917
            );
918
        }
919
920
        return $userGroups;
921
    }
922
923
    /**
924
     * Loads the users of a user group.
925
     *
926
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read the users or user group
927
     *
928
     * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup
929
     * @param int $offset the start offset for paging
930
     * @param int $limit the number of users returned
931
     * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object.
932
     *
933
     * @return \eZ\Publish\API\Repository\Values\User\User[]
934
     */
935
    public function loadUsersOfUserGroup(
936
        APIUserGroup $userGroup,
937
        $offset = 0,
938
        $limit = 25,
939
        array $prioritizedLanguages = []
940
    ) {
941
        $loadedUserGroup = $this->loadUserGroup($userGroup->id);
942
943
        if ($loadedUserGroup->getVersionInfo()->getContentInfo()->mainLocationId === null) {
944
            return [];
945
        }
946
947
        $mainGroupLocation = $this->repository->getLocationService()->loadLocation(
948
            $loadedUserGroup->getVersionInfo()->getContentInfo()->mainLocationId
949
        );
950
951
        $searchQuery = new LocationQuery();
952
953
        $searchQuery->filter = new CriterionLogicalAnd(
954
            [
955
                new CriterionContentTypeId($this->settings['userClassID']),
956
                new CriterionParentLocationId($mainGroupLocation->id),
957
            ]
958
        );
959
960
        $searchQuery->offset = $offset;
961
        $searchQuery->limit = $limit;
962
        $searchQuery->performCount = false;
963
        $searchQuery->sortClauses = $mainGroupLocation->getSortClauses();
0 ignored issues
show
Documentation Bug introduced by
It seems like $mainGroupLocation->getSortClauses() of type array<integer,object,{"0":"object"}> is incompatible with the declared type array<integer,object<eZ\...tent\Query\SortClause>> of property $sortClauses.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
964
965
        $searchResult = $this->repository->getSearchService()->findLocations($searchQuery);
966
967
        $users = [];
968
        foreach ($searchResult->searchHits as $resultItem) {
969
            $users[] = $this->buildDomainUserObject(
970
                $this->userHandler->load($resultItem->valueObject->contentInfo->id),
0 ignored issues
show
Documentation introduced by
The property contentInfo does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
971
                $this->repository->getContentService()->internalLoadContent(
0 ignored issues
show
Bug introduced by
The method internalLoadContent() does not exist on eZ\Publish\API\Repository\ContentService. Did you maybe mean loadContent()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
972
                    $resultItem->valueObject->contentInfo->id,
0 ignored issues
show
Documentation introduced by
The property contentInfo does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
973
                    $prioritizedLanguages
974
                )
975
            );
976
        }
977
978
        return $users;
979
    }
980
981
    /**
982
     * Instantiate a user create class.
983
     *
984
     * @param string $login the login of the new user
985
     * @param string $email the email of the new user
986
     * @param string $password the plain password of the new user
987
     * @param string $mainLanguageCode the main language for the underlying content object
988
     * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType 5.x the content type for the underlying content object. In 4.x it is ignored and taken from the configuration
989
     *
990
     * @return \eZ\Publish\API\Repository\Values\User\UserCreateStruct
991
     */
992
    public function newUserCreateStruct($login, $email, $password, $mainLanguageCode, $contentType = null)
993
    {
994
        if ($contentType === null) {
995
            $contentType = $this->repository->getContentTypeService()->loadContentType(
996
                $this->settings['userClassID']
997
            );
998
        }
999
1000
        return new UserCreateStruct(
1001
            array(
1002
                'contentType' => $contentType,
1003
                'mainLanguageCode' => $mainLanguageCode,
1004
                'login' => $login,
1005
                'email' => $email,
1006
                'password' => $password,
1007
                'enabled' => true,
1008
                'fields' => array(),
1009
            )
1010
        );
1011
    }
1012
1013
    /**
1014
     * Instantiate a user group create class.
1015
     *
1016
     * @param string $mainLanguageCode The main language for the underlying content object
1017
     * @param null|\eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType 5.x the content type for the underlying content object. In 4.x it is ignored and taken from the configuration
1018
     *
1019
     * @return \eZ\Publish\API\Repository\Values\User\UserGroupCreateStruct
1020
     */
1021
    public function newUserGroupCreateStruct($mainLanguageCode, $contentType = null)
1022
    {
1023
        if ($contentType === null) {
1024
            $contentType = $this->repository->getContentTypeService()->loadContentType(
1025
                $this->settings['userGroupClassID']
1026
            );
1027
        }
1028
1029
        return new UserGroupCreateStruct(
1030
            array(
1031
                'contentType' => $contentType,
1032
                'mainLanguageCode' => $mainLanguageCode,
1033
                'fields' => array(),
1034
            )
1035
        );
1036
    }
1037
1038
    /**
1039
     * Instantiate a new user update struct.
1040
     *
1041
     * @return \eZ\Publish\API\Repository\Values\User\UserUpdateStruct
1042
     */
1043
    public function newUserUpdateStruct()
1044
    {
1045
        return new UserUpdateStruct();
1046
    }
1047
1048
    /**
1049
     * Instantiate a new user group update struct.
1050
     *
1051
     * @return \eZ\Publish\API\Repository\Values\User\UserGroupUpdateStruct
1052
     */
1053
    public function newUserGroupUpdateStruct()
1054
    {
1055
        return new UserGroupUpdateStruct();
1056
    }
1057
1058
    /**
1059
     * Builds the domain UserGroup object from provided Content object.
1060
     *
1061
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
1062
     *
1063
     * @return \eZ\Publish\API\Repository\Values\User\UserGroup
1064
     */
1065
    protected function buildDomainUserGroupObject(APIContent $content)
1066
    {
1067
        $locationService = $this->repository->getLocationService();
1068
1069
        $subGroupCount = 0;
1070
        if ($content->getVersionInfo()->getContentInfo()->mainLocationId !== null) {
1071
            $mainLocation = $locationService->loadLocation(
1072
                $content->getVersionInfo()->getContentInfo()->mainLocationId
1073
            );
1074
            $parentLocation = $locationService->loadLocation($mainLocation->parentLocationId);
1075
            $subGroups = $this->searchSubGroups($mainLocation, 0, 0);
1076
            $subGroupCount = $subGroups->totalCount;
1077
        }
1078
1079
        return new UserGroup(
1080
            array(
1081
                'content' => $content,
1082
                'parentId' => isset($parentLocation) ? $parentLocation->contentId : null,
1083
                'subGroupCount' => $subGroupCount,
1084
            )
1085
        );
1086
    }
1087
1088
    /**
1089
     * Builds the domain user object from provided persistence user object.
1090
     *
1091
     * @param \eZ\Publish\SPI\Persistence\User $spiUser
1092
     * @param \eZ\Publish\API\Repository\Values\Content\Content|null $content
1093
     * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object.
1094
     *
1095
     * @return \eZ\Publish\API\Repository\Values\User\User
1096
     */
1097
    protected function buildDomainUserObject(
1098
        SPIUser $spiUser,
1099
        APIContent $content = null,
1100
        array $prioritizedLanguages = []
1101
    ) {
1102
        if ($content === null) {
1103
            $content = $this->repository->getContentService()->internalLoadContent(
0 ignored issues
show
Bug introduced by
The method internalLoadContent() does not exist on eZ\Publish\API\Repository\ContentService. Did you maybe mean loadContent()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1104
                $spiUser->id,
1105
                $prioritizedLanguages
1106
            );
1107
        }
1108
1109
        return new User(
1110
            array(
1111
                'content' => $content,
1112
                'login' => $spiUser->login,
1113
                'email' => $spiUser->email,
1114
                'passwordHash' => $spiUser->passwordHash,
1115
                'hashAlgorithm' => (int)$spiUser->hashAlgorithm,
1116
                'enabled' => $spiUser->isEnabled,
1117
                'maxLogin' => (int)$spiUser->maxLogin,
1118
            )
1119
        );
1120
    }
1121
1122
    /**
1123
     * Returns password hash based on user data and site settings.
1124
     *
1125
     * @param string $login User login
1126
     * @param string $password User password
1127
     * @param string $site The name of the site
1128
     * @param int $type Type of password to generate
1129
     *
1130
     * @return string Generated password hash
1131
     */
1132
    protected function createPasswordHash($login, $password, $site, $type)
1133
    {
1134
        switch ($type) {
1135
            case User::PASSWORD_HASH_MD5_PASSWORD:
1136
                return md5($password);
1137
1138
            case User::PASSWORD_HASH_MD5_USER:
1139
                return md5("$login\n$password");
1140
1141
            case User::PASSWORD_HASH_MD5_SITE:
1142
                return md5("$login\n$password\n$site");
1143
1144
            case User::PASSWORD_HASH_PLAINTEXT:
1145
                return $password;
1146
1147
            default:
1148
                return md5($password);
1149
        }
1150
    }
1151
}
1152