Akonadi

tagmodifyhandler.cpp
1 /*
2  Copyright (c) 2014 Daniel Vrátil <[email protected]>
3 
4  This library is free software; you can redistribute it and/or modify it
5  under the terms of the GNU Library General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or (at your
7  option) any later version.
8 
9  This library is distributed in the hope that it will be useful, but WITHOUT
10  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12  License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB. If not, write to the
16  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  02110-1301, USA.
18 */
19 
20 #include "tagmodifyhandler.h"
21 
22 #include "tagfetchhelper.h"
23 #include "connection.h"
24 #include "storage/datastore.h"
25 #include "storage/querybuilder.h"
26 #include <shared/akranges.h>
27 
28 #include <private/imapset_p.h>
29 
30 using namespace Akonadi;
31 using namespace Akonadi::Server;
32 using namespace AkRanges;
33 
34 TagModifyHandler::TagModifyHandler(AkonadiServer &akonadi)
35  : Handler(akonadi)
36 {}
37 
38 bool TagModifyHandler::parseStream()
39 {
40  const auto &cmd = Protocol::cmdCast<Protocol::ModifyTagCommand>(m_command);
41 
42  Tag changedTag = Tag::retrieveById(cmd.tagId());
43  if (!changedTag.isValid()) {
44  return failureResponse("No such tag");
45  }
46 
47  QSet<QByteArray> changes;
48 
49  // Retrieve all tag's attributes
50  const TagAttribute::List attributes = TagAttribute::retrieveFiltered(TagAttribute::tagIdFullColumnName(), cmd.tagId());
51  const auto attributesMap = attributes | Views::transform([](const auto &attr) { return std::make_pair(attr.type(), attr); }) | Actions::toQMap;
52 
53  if (cmd.modifiedParts() & Protocol::ModifyTagCommand::ParentId) {
54  if (cmd.parentId() != changedTag.parentId()) {
55  changedTag.setParentId(cmd.parentId());
56  changes << AKONADI_PARAM_PARENT;
57  }
58  }
59 
60  if (cmd.modifiedParts() & Protocol::ModifyTagCommand::Type) {
61  TagType type = TagType::retrieveById(changedTag.typeId());
62  const QString newTypeName = QString::fromUtf8(cmd.type());
63  if (newTypeName != type.name()) {
64  const TagType newType = TagType::retrieveByNameOrCreate(newTypeName);
65  if (!newType.isValid()) {
66  return failureResponse("Failed to create new tag type");
67  }
68  changedTag.setTagType(newType);
69  changes << AKONADI_PARAM_MIMETYPE;
70  }
71  }
72 
73  bool tagRemoved = false;
74  if (cmd.modifiedParts() & Protocol::ModifyTagCommand::RemoteId) {
75  if (!connection()->context().resource().isValid()) {
76  return failureResponse("Only resources can change tag remote ID");
77  }
78 
79  //Simply using remove() doesn't work since we need two arguments
80  QueryBuilder qb(TagRemoteIdResourceRelation::tableName(), QueryBuilder::Delete);
81  qb.addValueCondition(TagRemoteIdResourceRelation::tagIdColumn(), Query::Equals, cmd.tagId());
82  qb.addValueCondition(TagRemoteIdResourceRelation::resourceIdColumn(), Query::Equals, connection()->context().resource().id());
83  qb.exec();
84 
85  if (!cmd.remoteId().isEmpty()) {
86  TagRemoteIdResourceRelation remoteIdRelation;
87  remoteIdRelation.setRemoteId(QString::fromUtf8(cmd.remoteId()));
88  remoteIdRelation.setResourceId(connection()->context().resource().id());
89  remoteIdRelation.setTag(changedTag);
90  if (!remoteIdRelation.insert()) {
91  return failureResponse("Failed to insert remotedid resource relation");
92  }
93  } else {
94  const int tagRidsCount = TagRemoteIdResourceRelation::count(TagRemoteIdResourceRelation::tagIdColumn(), changedTag.id());
95  // We just removed the last RID of the tag, which means that no other
96  // resource owns this tag, so we have to remove it to simulate tag
97  // removal
98  if (tagRidsCount == 0) {
99  if (!storageBackend()->removeTags(Tag::List() << changedTag)) {
100  return failureResponse("Failed to remove tag");
101  }
102  tagRemoved = true;
103  }
104  }
105  // Do not notify about remoteid changes, otherwise we bounce back and forth
106  // between resources recording it's change and updating the remote id.
107  }
108 
109  if (cmd.modifiedParts() & Protocol::ModifyTagCommand::RemovedAttributes) {
110  Q_FOREACH (const QByteArray &attrName, cmd.removedAttributes()) {
111  TagAttribute attribute = attributesMap.value(attrName);
112  TagAttribute::remove(attribute.id());
113  changes << attrName;
114  }
115  }
116 
117  if (cmd.modifiedParts() & Protocol::ModifyTagCommand::Attributes) {
118  const QMap<QByteArray, QByteArray> attrs = cmd.attributes();
119  for (auto iter = attrs.cbegin(), end = attrs.cend(); iter != end; ++iter) {
120  if (attributesMap.contains(iter.key())) {
121  TagAttribute attribute = attributesMap.value(iter.key());
122  attribute.setValue(iter.value());
123  if (!attribute.update()) {
124  return failureResponse("Failed to update attribute");
125  }
126  } else {
127  TagAttribute attribute;
128  attribute.setTagId(cmd.tagId());
129  attribute.setType(iter.key());
130  attribute.setValue(iter.value());
131  if (!attribute.insert()) {
132  return failureResponse("Failed to insert attribute");
133  }
134  }
135  changes << iter.key();
136  }
137  }
138 
139  if (!tagRemoved) {
140  if (!changedTag.update()) {
141  return failureResponse("Failed to store changes");
142  }
143  if (!changes.isEmpty()) {
144  storageBackend()->notificationCollector()->tagChanged(changedTag);
145  }
146 
147  ImapSet set;
148  set.add(QVector<qint64>() << cmd.tagId());
149 
150  Protocol::TagFetchScope fetchScope;
151  fetchScope.setFetchRemoteID(true);
152  fetchScope.setFetchAllAttributes(true);
153 
154  Scope scope;
155  scope.setUidSet(set);
156  TagFetchHelper helper(connection(), scope, fetchScope);
157  if (!helper.fetchTags()) {
158  return failureResponse("Failed to fetch response");
159  }
160  } else {
161  successResponse<Protocol::DeleteTagResponse>();
162  }
163 
164  return successResponse<Protocol::ModifyTagResponse>();
165 }
void setTag(const Tag &value)
Set the Tag record referred to by the tagId column of this record.
Definition: entities.cpp:9775
Type type(const QString &mimeType)
QMap::const_iterator cend() const const
The handler interfaces describes an entity capable of handling an AkonadiIMAP command.
Definition: handler.h:48
QString fromUtf8(const char *str, int size)
Attribute that stores the properties that are used to display a tag.
Definition: tagattribute.h:37
QMap::const_iterator cbegin() const const
Representation of a record in the TagRemoteIdResourceRelation table.
Definition: entities.h:3098
Representation of a record in the TagType table.
Definition: entities.h:2470
const QList< QKeySequence > & end()
QString name() const
Returns the value of the name column of this record.
Definition: entities.cpp:7790
Definition: item.h:44
void setResourceId(qint64 resourceId)
Sets the value of the resourceId column of this record.
Definition: entities.cpp:9600
Helper integration between Akonadi and Qt.
An Akonadi Tag.
Definition: tag.h:39
bool isEmpty() const const
void setRemoteId(const QString &remoteId)
Sets the value of the remoteId column of this record.
Definition: entities.cpp:9613
bool insert(qint64 *insertId=nullptr)
Inserts this record into the DataStore.
Definition: entities.cpp:9821
Helper class to construct arbitrary SQL queries.
Definition: querybuilder.h:45
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon May 25 2020 22:46:12 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.