Akonadi

tagmodifyhandler.cpp
1/*
2 SPDX-FileCopyrightText: 2014 Daniel Vrátil <dvratil@redhat.com>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "tagmodifyhandler.h"
8
9#include "connection.h"
10#include "shared/akranges.h"
11#include "storage/datastore.h"
12#include "storage/querybuilder.h"
13#include "tagfetchhelper.h"
14
15using namespace Akonadi;
16using namespace Akonadi::Server;
17using namespace AkRanges;
18
19TagModifyHandler::TagModifyHandler(AkonadiServer &akonadi)
20 : Handler(akonadi)
21{
22}
23
24bool TagModifyHandler::parseStream()
25{
26 const auto &cmd = Protocol::cmdCast<Protocol::ModifyTagCommand>(m_command);
27
28 Tag changedTag = Tag::retrieveById(cmd.tagId());
29 if (!changedTag.isValid()) {
30 return failureResponse("No such tag");
31 }
32
33 QSet<QByteArray> changes;
34
35 // Retrieve all tag's attributes
36 const TagAttribute::List attributes = TagAttribute::retrieveFiltered(TagAttribute::tagIdFullColumnName(), cmd.tagId());
37 const auto attributesMap = attributes | Views::transform([](const auto &attr) {
38 return std::make_pair(attr.type(), attr);
39 })
40 | Actions::toQMap;
41
42 if (cmd.modifiedParts() & Protocol::ModifyTagCommand::ParentId) {
43 if (cmd.parentId() != changedTag.parentId()) {
44 changedTag.setParentId(cmd.parentId());
45 changes << AKONADI_PARAM_PARENT;
46 }
47 }
48
49 if (cmd.modifiedParts() & Protocol::ModifyTagCommand::Type) {
50 TagType type = TagType::retrieveById(changedTag.typeId());
51 const QString newTypeName = QString::fromUtf8(cmd.type());
52 if (newTypeName != type.name()) {
53 const TagType newType = TagType::retrieveByNameOrCreate(newTypeName);
54 if (!newType.isValid()) {
55 return failureResponse("Failed to create new tag type");
56 }
57 changedTag.setTagType(newType);
58 changes << AKONADI_PARAM_MIMETYPE;
59 }
60 }
61
62 bool tagRemoved = false;
63 if (cmd.modifiedParts() & Protocol::ModifyTagCommand::RemoteId) {
64 if (!connection()->context().resource().isValid()) {
65 return failureResponse("Only resources can change tag remote ID");
66 }
67
68 // Simply using remove() doesn't work since we need two arguments
69 QueryBuilder qb(TagRemoteIdResourceRelation::tableName(), QueryBuilder::Delete);
70 qb.addValueCondition(TagRemoteIdResourceRelation::tagIdColumn(), Query::Equals, cmd.tagId());
71 qb.addValueCondition(TagRemoteIdResourceRelation::resourceIdColumn(), Query::Equals, connection()->context().resource().id());
72 qb.exec();
73
74 if (!cmd.remoteId().isEmpty()) {
75 TagRemoteIdResourceRelation remoteIdRelation;
76 remoteIdRelation.setRemoteId(QString::fromUtf8(cmd.remoteId()));
77 remoteIdRelation.setResourceId(connection()->context().resource().id());
78 remoteIdRelation.setTag(changedTag);
79 if (!remoteIdRelation.insert()) {
80 return failureResponse("Failed to insert remotedid resource relation");
81 }
82 } else {
83 const int tagRidsCount = TagRemoteIdResourceRelation::count(TagRemoteIdResourceRelation::tagIdColumn(), changedTag.id());
84 // We just removed the last RID of the tag, which means that no other
85 // resource owns this tag, so we have to remove it to simulate tag
86 // removal
87 if (tagRidsCount == 0) {
88 if (!storageBackend()->removeTags(Tag::List() << changedTag)) {
89 return failureResponse("Failed to remove tag");
90 }
91 tagRemoved = true;
92 }
93 }
94 // Do not notify about remoteid changes, otherwise we bounce back and forth
95 // between resources recording it's change and updating the remote id.
96 }
97
98 if (cmd.modifiedParts() & Protocol::ModifyTagCommand::RemovedAttributes) {
99 const auto attrNames = cmd.removedAttributes();
100 for (const QByteArray &attrName : attrNames) {
101 TagAttribute attribute = attributesMap.value(attrName);
102 TagAttribute::remove(attribute.id());
103 changes << attrName;
104 }
105 }
106
107 if (cmd.modifiedParts() & Protocol::ModifyTagCommand::Attributes) {
108 const QMap<QByteArray, QByteArray> attrs = cmd.attributes();
109 for (auto iter = attrs.cbegin(), end = attrs.cend(); iter != end; ++iter) {
110 if (attributesMap.contains(iter.key())) {
111 TagAttribute attribute = attributesMap.value(iter.key());
112 attribute.setValue(iter.value());
113 if (!attribute.update()) {
114 return failureResponse("Failed to update attribute");
115 }
116 } else {
117 TagAttribute attribute;
118 attribute.setTagId(cmd.tagId());
119 attribute.setType(iter.key());
120 attribute.setValue(iter.value());
121 if (!attribute.insert()) {
122 return failureResponse("Failed to insert attribute");
123 }
124 }
125 changes << iter.key();
126 }
127 }
128
129 if (!tagRemoved) {
130 if (!changedTag.update()) {
131 return failureResponse("Failed to store changes");
132 }
133 if (!changes.isEmpty()) {
134 storageBackend()->notificationCollector()->tagChanged(changedTag);
135 }
136
137 Protocol::TagFetchScope fetchScope;
138 fetchScope.setFetchRemoteID(true);
139 fetchScope.setFetchAllAttributes(true);
140
141 TagFetchHelper helper(connection(), Scope(cmd.tagId()), fetchScope);
142 if (!helper.fetchTags()) {
143 return failureResponse("Failed to fetch response");
144 }
145 } else {
146 successResponse<Protocol::DeleteTagResponse>();
147 }
148
149 return successResponse<Protocol::ModifyTagResponse>();
150}
NotificationCollector * notificationCollector()
Returns the notification collector of this DataStore object.
The handler interfaces describes an entity capable of handling an AkonadiIMAP command.
Definition handler.h:32
void tagChanged(const Tag &tag)
Notify about a changed tag.
Helper class to construct arbitrary SQL queries.
Attribute that stores the properties that are used to display a tag.
An Akonadi Tag.
Definition tag.h:26
Id id() const
Returns the unique identifier of the tag.
Definition tag.cpp:139
Helper integration between Akonadi and Qt.
bool isValid(QStringView ifopt)
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
const QList< QKeySequence > & end()
const_iterator cbegin() const const
const_iterator cend() const const
bool isEmpty() const const
QString fromUtf8(QByteArrayView str)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:13:38 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.