Akonadi

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

KDE's Doxygen guidelines are available online.