aboutsummaryrefslogtreecommitdiffstats
path: root/apps/dav/lib
diff options
context:
space:
mode:
authorSebastian Krupinski <165827823+SebastianKrupinski@users.noreply.github.com>2024-11-04 13:29:44 -0500
committerGitHub <noreply@github.com>2024-11-04 13:29:44 -0500
commit5f49effcba28043050e46e1ba6be7b40f4b6dcd4 (patch)
tree779661acad56c488b056e8c19ffbae6945e8dd4a /apps/dav/lib
parent165041746e603683ff0bd11250dff0af950b584b (diff)
parent5cd3a8c9ada5ea2951eb011b449ee483b9d5a322 (diff)
downloadnextcloud-server-5f49effcba28043050e46e1ba6be7b40f4b6dcd4.tar.gz
nextcloud-server-5f49effcba28043050e46e1ba6be7b40f4b6dcd4.zip
Merge branch 'master' into feat/issue-3786-allow-shared-calendarsfeat/issue-3786-allow-shared-calendars
Diffstat (limited to 'apps/dav/lib')
-rw-r--r--apps/dav/lib/CalDAV/Activity/Provider/Base.php6
-rw-r--r--apps/dav/lib/CalDAV/EventReader.php15
-rw-r--r--apps/dav/lib/RootCollection.php6
-rw-r--r--apps/dav/lib/SystemTag/SystemTagNode.php34
-rw-r--r--apps/dav/lib/SystemTag/SystemTagObjectType.php81
-rw-r--r--apps/dav/lib/SystemTag/SystemTagPlugin.php70
-rw-r--r--apps/dav/lib/SystemTag/SystemTagsByIdCollection.php4
-rw-r--r--apps/dav/lib/SystemTag/SystemTagsInUseCollection.php8
-rw-r--r--apps/dav/lib/SystemTag/SystemTagsObjectList.php80
9 files changed, 277 insertions, 27 deletions
diff --git a/apps/dav/lib/CalDAV/Activity/Provider/Base.php b/apps/dav/lib/CalDAV/Activity/Provider/Base.php
index 43d80b4bb6a..9a75acb878c 100644
--- a/apps/dav/lib/CalDAV/Activity/Provider/Base.php
+++ b/apps/dav/lib/CalDAV/Activity/Provider/Base.php
@@ -44,14 +44,14 @@ abstract class Base implements IProvider {
$data['name'] === CalDavBackend::PERSONAL_CALENDAR_NAME) {
return [
'type' => 'calendar',
- 'id' => $data['id'],
+ 'id' => (string)$data['id'],
'name' => $l->t('Personal'),
];
}
return [
'type' => 'calendar',
- 'id' => $data['id'],
+ 'id' => (string)$data['id'],
'name' => $data['name'],
];
}
@@ -64,7 +64,7 @@ abstract class Base implements IProvider {
protected function generateLegacyCalendarParameter($id, $name) {
return [
'type' => 'calendar',
- 'id' => $id,
+ 'id' => (string)$id,
'name' => $name,
];
}
diff --git a/apps/dav/lib/CalDAV/EventReader.php b/apps/dav/lib/CalDAV/EventReader.php
index 6e845c596ae..7ff05501ea8 100644
--- a/apps/dav/lib/CalDAV/EventReader.php
+++ b/apps/dav/lib/CalDAV/EventReader.php
@@ -10,6 +10,7 @@ declare(strict_types=1);
namespace OCA\DAV\CalDAV;
use DateTime;
+use DateTimeImmutable;
use DateTimeInterface;
use DateTimeZone;
use InvalidArgumentException;
@@ -109,7 +110,7 @@ class EventReader {
unset($events[$key]);
}
}
-
+
// No base event was found. CalDAV does allow cases where only
// overridden instances are stored.
//
@@ -173,15 +174,17 @@ class EventReader {
// evaluate if duration exists
// extract duration and calculate end date
elseif (isset($this->baseEvent->DURATION)) {
- $this->baseEventDuration = $this->baseEvent->DURATION->getDateInterval();
- $this->baseEventEndDate = ((clone $this->baseEventStartDate)->add($this->baseEventDuration));
+ $this->baseEventEndDate = DateTimeImmutable::createFromInterface($this->baseEventStartDate)
+ ->add($this->baseEvent->DURATION->getDateInterval());
+ $this->baseEventDuration = $this->baseEventEndDate->getTimestamp() - $this->baseEventStartDate->getTimestamp();
}
// evaluate if start date is floating
// set duration to 24 hours and calculate the end date
// according to the rfc any event without a end date or duration is a complete day
elseif ($this->baseEventStartDateFloating == true) {
$this->baseEventDuration = 86400;
- $this->baseEventEndDate = ((clone $this->baseEventStartDate)->add($this->baseEventDuration));
+ $this->baseEventEndDate = DateTimeImmutable::createFromInterface($this->baseEventStartDate)
+ ->setTimestamp($this->baseEventStartDate->getTimestamp() + $this->baseEventDuration);
}
// otherwise, set duration to zero this should never happen
else {
@@ -220,7 +223,7 @@ class EventReader {
foreach ($events as $vevent) {
$this->recurrenceModified[$vevent->{'RECURRENCE-ID'}->getDateTime($this->baseEventStartTimeZone)->getTimeStamp()] = $vevent;
}
-
+
$this->recurrenceCurrentDate = clone $this->baseEventStartDate;
}
@@ -375,7 +378,7 @@ class EventReader {
* @return int|null
*/
public function recurringConcludesAfter(): ?int {
-
+
// construct count place holder
$count = 0;
// retrieve and add RRULE iterations count
diff --git a/apps/dav/lib/RootCollection.php b/apps/dav/lib/RootCollection.php
index 6ede8cb683c..751ab17bb7a 100644
--- a/apps/dav/lib/RootCollection.php
+++ b/apps/dav/lib/RootCollection.php
@@ -105,11 +105,7 @@ class RootCollection extends SimpleCollection {
$publicCalendarRoot = new PublicCalendarRoot($caldavBackend, $l10n, $config, $logger);
- $systemTagCollection = new SystemTagsByIdCollection(
- \OC::$server->getSystemTagManager(),
- \OC::$server->getUserSession(),
- $groupManager
- );
+ $systemTagCollection = Server::get(SystemTagsByIdCollection::class);
$systemTagRelationsCollection = new SystemTagsRelationsCollection(
\OC::$server->getSystemTagManager(),
\OC::$server->getSystemTagObjectMapper(),
diff --git a/apps/dav/lib/SystemTag/SystemTagNode.php b/apps/dav/lib/SystemTag/SystemTagNode.php
index 9357f207779..ed4eb44cbbd 100644
--- a/apps/dav/lib/SystemTag/SystemTagNode.php
+++ b/apps/dav/lib/SystemTag/SystemTagNode.php
@@ -10,8 +10,8 @@ namespace OCA\DAV\SystemTag;
use OCP\IUser;
use OCP\SystemTag\ISystemTag;
use OCP\SystemTag\ISystemTagManager;
+use OCP\SystemTag\ISystemTagObjectMapper;
use OCP\SystemTag\TagAlreadyExistsException;
-
use OCP\SystemTag\TagNotFoundException;
use Sabre\DAV\Exception\Conflict;
use Sabre\DAV\Exception\Forbidden;
@@ -21,7 +21,7 @@ use Sabre\DAV\Exception\NotFound;
/**
* DAV node representing a system tag, with the name being the tag id.
*/
-class SystemTagNode implements \Sabre\DAV\INode {
+class SystemTagNode implements \Sabre\DAV\ICollection {
protected int $numberOfFiles = -1;
protected int $referenceFileId = -1;
@@ -43,8 +43,9 @@ class SystemTagNode implements \Sabre\DAV\INode {
/**
* Whether to allow permissions for admins
*/
- protected $isAdmin,
+ protected bool $isAdmin,
protected ISystemTagManager $tagManager,
+ protected ISystemTagObjectMapper $tagMapper,
) {
}
@@ -164,4 +165,31 @@ class SystemTagNode implements \Sabre\DAV\INode {
public function setReferenceFileId(int $referenceFileId): void {
$this->referenceFileId = $referenceFileId;
}
+
+ public function createFile($name, $data = null) {
+ throw new MethodNotAllowed();
+ }
+
+ public function createDirectory($name) {
+ throw new MethodNotAllowed();
+ }
+
+ public function getChild($name) {
+ return new SystemTagObjectType($this->tag, $name, $this->tagManager, $this->tagMapper);
+ }
+
+ public function childExists($name) {
+ $objectTypes = $this->tagMapper->getAvailableObjectTypes();
+ return in_array($name, $objectTypes);
+ }
+
+ public function getChildren() {
+ $objectTypes = $this->tagMapper->getAvailableObjectTypes();
+ return array_map(
+ function ($objectType) {
+ return new SystemTagObjectType($this->tag, $objectType, $this->tagManager, $this->tagMapper);
+ },
+ $objectTypes
+ );
+ }
}
diff --git a/apps/dav/lib/SystemTag/SystemTagObjectType.php b/apps/dav/lib/SystemTag/SystemTagObjectType.php
new file mode 100644
index 00000000000..0e1368854cd
--- /dev/null
+++ b/apps/dav/lib/SystemTag/SystemTagObjectType.php
@@ -0,0 +1,81 @@
+<?php
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+namespace OCA\DAV\SystemTag;
+
+use OCP\SystemTag\ISystemTag;
+use OCP\SystemTag\ISystemTagManager;
+use OCP\SystemTag\ISystemTagObjectMapper;
+use Sabre\DAV\Exception\MethodNotAllowed;
+
+/**
+ * SystemTagObjectType property
+ * This property represent a type of object which tags are assigned to.
+ */
+class SystemTagObjectType implements \Sabre\DAV\IFile {
+ public const NS_NEXTCLOUD = 'http://nextcloud.org/ns';
+
+ /** @var string[] */
+ private array $objectsIds = [];
+
+ public function __construct(
+ private ISystemTag $tag,
+ private string $type,
+ private ISystemTagManager $tagManager,
+ private ISystemTagObjectMapper $tagMapper,
+ ) {
+ }
+
+ /**
+ * Get the list of object ids that have this tag assigned.
+ */
+ public function getObjectsIds(): array {
+ if (empty($this->objectsIds)) {
+ $this->objectsIds = $this->tagMapper->getObjectIdsForTags($this->tag->getId(), $this->type);
+ }
+
+ return $this->objectsIds;
+ }
+
+ /**
+ * Returns the system tag represented by this node
+ *
+ * @return ISystemTag system tag
+ */
+ public function getSystemTag() {
+ return $this->tag;
+ }
+
+ public function getName() {
+ return $this->type;
+ }
+
+ public function getLastModified() {
+ return null;
+ }
+
+ public function getETag() {
+ return '"' . $this->tag->getETag() . '"';
+ }
+
+ public function setName($name) {
+ throw new MethodNotAllowed();
+ }
+ public function put($data) {
+ throw new MethodNotAllowed();
+ }
+ public function get() {
+ throw new MethodNotAllowed();
+ }
+ public function delete() {
+ throw new MethodNotAllowed();
+ }
+ public function getContentType() {
+ throw new MethodNotAllowed();
+ }
+ public function getSize() {
+ throw new MethodNotAllowed();
+ }
+}
diff --git a/apps/dav/lib/SystemTag/SystemTagPlugin.php b/apps/dav/lib/SystemTag/SystemTagPlugin.php
index 3c34f4263f6..00585953b29 100644
--- a/apps/dav/lib/SystemTag/SystemTagPlugin.php
+++ b/apps/dav/lib/SystemTag/SystemTagPlugin.php
@@ -8,6 +8,7 @@
namespace OCA\DAV\SystemTag;
use OCA\DAV\Connector\Sabre\Directory;
+use OCA\DAV\Connector\Sabre\FilesPlugin;
use OCA\DAV\Connector\Sabre\Node;
use OCP\IGroupManager;
use OCP\IUser;
@@ -37,6 +38,7 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
// namespace
public const NS_OWNCLOUD = 'http://owncloud.org/ns';
+ public const NS_NEXTCLOUD = 'http://nextcloud.org/ns';
public const ID_PROPERTYNAME = '{http://owncloud.org/ns}id';
public const DISPLAYNAME_PROPERTYNAME = '{http://owncloud.org/ns}display-name';
public const USERVISIBLE_PROPERTYNAME = '{http://owncloud.org/ns}user-visible';
@@ -45,7 +47,8 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
public const CANASSIGN_PROPERTYNAME = '{http://owncloud.org/ns}can-assign';
public const SYSTEM_TAGS_PROPERTYNAME = '{http://nextcloud.org/ns}system-tags';
public const NUM_FILES_PROPERTYNAME = '{http://nextcloud.org/ns}files-assigned';
- public const FILEID_PROPERTYNAME = '{http://nextcloud.org/ns}reference-fileid';
+ public const REFERENCE_FILEID_PROPERTYNAME = '{http://nextcloud.org/ns}reference-fileid';
+ public const OBJECTIDS_PROPERTYNAME = '{http://nextcloud.org/ns}object-ids';
/**
* @var \Sabre\DAV\Server $server
@@ -78,6 +81,9 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
*/
public function initialize(\Sabre\DAV\Server $server) {
$server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc';
+ $server->xml->namespaceMap[self::NS_NEXTCLOUD] = 'nc';
+
+ $server->xml->elementMap[self::OBJECTIDS_PROPERTYNAME] = SystemTagsObjectList::class;
$server->protectedProperties[] = self::ID_PROPERTYNAME;
@@ -202,7 +208,7 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
return;
}
- if (!($node instanceof SystemTagNode) && !($node instanceof SystemTagMappingNode)) {
+ if (!($node instanceof SystemTagNode) && !($node instanceof SystemTagMappingNode) && !($node instanceof SystemTagObjectType)) {
return;
}
@@ -211,6 +217,10 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
$propFind->setPath(str_replace('systemtags-assigned/', 'systemtags/', $propFind->getPath()));
}
+ $propFind->handle(FilesPlugin::GETETAG_PROPERTYNAME, function () use ($node): string {
+ return '"' . ($node->getSystemTag()->getETag() ?? '') . '"';
+ });
+
$propFind->handle(self::ID_PROPERTYNAME, function () use ($node) {
return $node->getSystemTag()->getId();
});
@@ -251,9 +261,25 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
return $node->getNumberOfFiles();
});
- $propFind->handle(self::FILEID_PROPERTYNAME, function () use ($node): int {
+ $propFind->handle(self::REFERENCE_FILEID_PROPERTYNAME, function () use ($node): int {
return $node->getReferenceFileId();
});
+
+ $propFind->handle(self::OBJECTIDS_PROPERTYNAME, function () use ($node): SystemTagsObjectList {
+ $objectTypes = $this->tagMapper->getAvailableObjectTypes();
+ $objects = [];
+ foreach ($objectTypes as $type) {
+ $systemTagObjectType = new SystemTagObjectType($node->getSystemTag(), $type, $this->tagManager, $this->tagMapper);
+ $objects = array_merge($objects, array_fill_keys($systemTagObjectType->getObjectsIds(), $type));
+ }
+ return new SystemTagsObjectList($objects);
+ });
+ }
+
+ if ($node instanceof SystemTagObjectType) {
+ $propFind->handle(self::OBJECTIDS_PROPERTYNAME, function () use ($node): SystemTagsObjectList {
+ return new SystemTagsObjectList(array_fill_keys($node->getObjectsIds(), $node->getName()));
+ });
}
}
@@ -341,9 +367,37 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
*/
public function handleUpdateProperties($path, PropPatch $propPatch) {
$node = $this->server->tree->getNodeForPath($path);
- if (!($node instanceof SystemTagNode)) {
+ if (!($node instanceof SystemTagNode) && !($node instanceof SystemTagObjectType)) {
return;
}
+
+ $propPatch->handle([self::OBJECTIDS_PROPERTYNAME], function ($props) use ($node) {
+ if (!($node instanceof SystemTagObjectType)) {
+ return false;
+ }
+
+ if (isset($props[self::OBJECTIDS_PROPERTYNAME])) {
+ $propValue = $props[self::OBJECTIDS_PROPERTYNAME];
+ if (!($propValue instanceof SystemTagsObjectList) || count($propValue->getObjects()) === 0) {
+ throw new BadRequest('Invalid object-ids property');
+ }
+
+ $objects = $propValue->getObjects();
+ $objectTypes = array_unique(array_values($objects));
+
+ if (count($objectTypes) !== 1 || $objectTypes[0] !== $node->getName()) {
+ throw new BadRequest('Invalid object-ids property. All object types must be of the same type: ' . $node->getName());
+ }
+
+ $this->tagMapper->setObjectIdsForTag($node->getSystemTag()->getId(), $node->getName(), array_keys($objects));
+ }
+
+ if ($props[self::OBJECTIDS_PROPERTYNAME] === null) {
+ $this->tagMapper->setObjectIdsForTag($node->getSystemTag()->getId(), $node->getName(), []);
+ }
+
+ return true;
+ });
$propPatch->handle([
self::DISPLAYNAME_PROPERTYNAME,
@@ -351,8 +405,12 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
self::USERASSIGNABLE_PROPERTYNAME,
self::GROUPS_PROPERTYNAME,
self::NUM_FILES_PROPERTYNAME,
- self::FILEID_PROPERTYNAME,
+ self::REFERENCE_FILEID_PROPERTYNAME,
], function ($props) use ($node) {
+ if (!($node instanceof SystemTagNode)) {
+ return false;
+ }
+
$tag = $node->getSystemTag();
$name = $tag->getName();
$userVisible = $tag->isUserVisible();
@@ -388,7 +446,7 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
$this->tagManager->setTagGroups($tag, $groupIds);
}
- if (isset($props[self::NUM_FILES_PROPERTYNAME]) || isset($props[self::FILEID_PROPERTYNAME])) {
+ if (isset($props[self::NUM_FILES_PROPERTYNAME]) || isset($props[self::REFERENCE_FILEID_PROPERTYNAME])) {
// read-only properties
throw new Forbidden();
}
diff --git a/apps/dav/lib/SystemTag/SystemTagsByIdCollection.php b/apps/dav/lib/SystemTag/SystemTagsByIdCollection.php
index cf889985d1b..b854db7b94d 100644
--- a/apps/dav/lib/SystemTag/SystemTagsByIdCollection.php
+++ b/apps/dav/lib/SystemTag/SystemTagsByIdCollection.php
@@ -11,6 +11,7 @@ use OCP\IGroupManager;
use OCP\IUserSession;
use OCP\SystemTag\ISystemTag;
use OCP\SystemTag\ISystemTagManager;
+use OCP\SystemTag\ISystemTagObjectMapper;
use OCP\SystemTag\TagNotFoundException;
use Sabre\DAV\Exception\BadRequest;
use Sabre\DAV\Exception\Forbidden;
@@ -30,6 +31,7 @@ class SystemTagsByIdCollection implements ICollection {
private ISystemTagManager $tagManager,
private IUserSession $userSession,
private IGroupManager $groupManager,
+ protected ISystemTagObjectMapper $tagMapper,
) {
}
@@ -162,6 +164,6 @@ class SystemTagsByIdCollection implements ICollection {
* @return SystemTagNode
*/
private function makeNode(ISystemTag $tag) {
- return new SystemTagNode($tag, $this->userSession->getUser(), $this->isAdmin(), $this->tagManager);
+ return new SystemTagNode($tag, $this->userSession->getUser(), $this->isAdmin(), $this->tagManager, $this->tagMapper);
}
}
diff --git a/apps/dav/lib/SystemTag/SystemTagsInUseCollection.php b/apps/dav/lib/SystemTag/SystemTagsInUseCollection.php
index 6a3dd0c2155..9b02a5be42c 100644
--- a/apps/dav/lib/SystemTag/SystemTagsInUseCollection.php
+++ b/apps/dav/lib/SystemTag/SystemTagsInUseCollection.php
@@ -16,6 +16,7 @@ use OCP\Files\IRootFolder;
use OCP\Files\NotPermittedException;
use OCP\IUserSession;
use OCP\SystemTag\ISystemTagManager;
+use OCP\SystemTag\ISystemTagObjectMapper;
use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\Exception\NotFound;
use Sabre\DAV\SimpleCollection;
@@ -28,6 +29,7 @@ class SystemTagsInUseCollection extends SimpleCollection {
protected IUserSession $userSession,
protected IRootFolder $rootFolder,
protected ISystemTagManager $systemTagManager,
+ protected ISystemTagObjectMapper $tagMapper,
SystemTagsInFilesDetector $systemTagsInFilesDetector,
protected string $mediaType = '',
) {
@@ -46,7 +48,7 @@ class SystemTagsInUseCollection extends SimpleCollection {
if ($this->mediaType !== '') {
throw new NotFound('Invalid media type');
}
- return new self($this->userSession, $this->rootFolder, $this->systemTagManager, $this->systemTagsInFilesDetector, $name);
+ return new self($this->userSession, $this->rootFolder, $this->systemTagManager, $this->tagMapper, $this->systemTagsInFilesDetector, $name);
}
/**
@@ -71,9 +73,9 @@ class SystemTagsInUseCollection extends SimpleCollection {
$result = $this->systemTagsInFilesDetector->detectAssignedSystemTagsIn($userFolder, $this->mediaType);
$children = [];
foreach ($result as $tagData) {
- $tag = new SystemTag((string)$tagData['id'], $tagData['name'], (bool)$tagData['visibility'], (bool)$tagData['editable']);
+ $tag = new SystemTag((string)$tagData['id'], $tagData['name'], (bool)$tagData['visibility'], (bool)$tagData['editable'], $tagData['etag']);
// read only, so we can submit the isAdmin parameter as false generally
- $node = new SystemTagNode($tag, $user, false, $this->systemTagManager);
+ $node = new SystemTagNode($tag, $user, false, $this->systemTagManager, $this->tagMapper);
$node->setNumberOfFiles((int)$tagData['number_files']);
$node->setReferenceFileId((int)$tagData['ref_file_id']);
$children[] = $node;
diff --git a/apps/dav/lib/SystemTag/SystemTagsObjectList.php b/apps/dav/lib/SystemTag/SystemTagsObjectList.php
new file mode 100644
index 00000000000..0743b8d8130
--- /dev/null
+++ b/apps/dav/lib/SystemTag/SystemTagsObjectList.php
@@ -0,0 +1,80 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\SystemTag;
+
+use Sabre\Xml\Reader;
+use Sabre\Xml\Writer;
+use Sabre\Xml\XmlDeserializable;
+use Sabre\Xml\XmlSerializable;
+
+/**
+ * This property contains multiple "object-id" elements.
+ */
+class SystemTagsObjectList implements XmlSerializable, XmlDeserializable {
+
+ public const NS_NEXTCLOUD = 'http://nextcloud.org/ns';
+ public const OBJECTID_ROOT_PROPERTYNAME = '{http://nextcloud.org/ns}object-id';
+ public const OBJECTID_PROPERTYNAME = '{http://nextcloud.org/ns}id';
+ public const OBJECTTYPE_PROPERTYNAME = '{http://nextcloud.org/ns}type';
+
+ /**
+ * @param array<string, string> $objects An array of object ids and their types
+ */
+ public function __construct(
+ private array $objects,
+ ) {
+ }
+
+ public function getObjects(): array {
+ return $this->objects;
+ }
+
+ public static function xmlDeserialize(Reader $reader) {
+ $tree = $reader->parseInnerTree();
+ if ($tree === null) {
+ return null;
+ }
+
+ $objects = [];
+ foreach ($tree as $elem) {
+ if ($elem['name'] === self::OBJECTID_ROOT_PROPERTYNAME) {
+ $value = $elem['value'];
+ $id = '';
+ $type = '';
+ foreach ($value as $subElem) {
+ if ($subElem['name'] === self::OBJECTID_PROPERTYNAME) {
+ $id = $subElem['value'];
+ } elseif ($subElem['name'] === self::OBJECTTYPE_PROPERTYNAME) {
+ $type = $subElem['value'];
+ }
+ }
+ if ($id !== '' && $type !== '') {
+ $objects[$id] = $type;
+ }
+ }
+ }
+
+ return new self($objects);
+ }
+
+ /**
+ * The xmlSerialize method is called during xml writing.
+ *
+ * @param Writer $writer
+ * @return void
+ */
+ public function xmlSerialize(Writer $writer) {
+ foreach ($this->objects as $objectsId => $type) {
+ $writer->startElement(SystemTagPlugin::OBJECTIDS_PROPERTYNAME);
+ $writer->writeElement(self::OBJECTID_PROPERTYNAME, $objectsId);
+ $writer->writeElement(self::OBJECTTYPE_PROPERTYNAME, $type);
+ $writer->endElement();
+ }
+ }
+}