Akonadi

tagcreatehandler.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 "tagcreatehandler.h"
8 
9 #include "tagfetchhelper.h"
10 #include "connection.h"
11 #include "storage/datastore.h"
12 #include "storage/querybuilder.h"
13 #include "storage/countquerybuilder.h"
14 #include "storage/transaction.h"
15 
16 #include <private/scope_p.h>
17 #include <private/imapset_p.h>
18 
19 using namespace Akonadi;
20 using namespace Akonadi::Server;
21 
22 TagCreateHandler::TagCreateHandler(AkonadiServer &akonadi)
23  : Handler(akonadi)
24 {}
25 
26 bool TagCreateHandler::parseStream()
27 {
28  const auto &cmd = Protocol::cmdCast<Protocol::CreateTagCommand>(m_command);
29 
30  if (!cmd.remoteId().isEmpty() && !connection()->context().resource().isValid()) {
31  return failureResponse(QStringLiteral("Only resources can create tags with remote ID"));
32  }
33 
34  Transaction trx(storageBackend(), QStringLiteral("TAGAPPEND"));
35 
36  TagType tagType;
37  if (!cmd.type().isEmpty()) {
38  const QString typeName = QString::fromUtf8(cmd.type());
39  tagType = TagType::retrieveByNameOrCreate(typeName);
40  if (!tagType.isValid()) {
41  return failureResponse(QStringLiteral("Unable to create tagtype '") % typeName % QStringLiteral("'"));
42  }
43  }
44 
45  qint64 tagId = -1;
46  const QString gid = QString::fromUtf8(cmd.gid());
47  if (cmd.merge()) {
48  QueryBuilder qb(Tag::tableName());
49  qb.addColumn(Tag::idColumn());
50  qb.addValueCondition(Tag::gidColumn(), Query::Equals, gid);
51  if (!qb.exec()) {
52  return failureResponse("Unable to list tags");
53  }
54  if (qb.query().next()) {
55  tagId = qb.query().value(0).toLongLong();
56  }
57  qb.query().finish();
58  }
59  if (tagId < 0) {
60  Tag insertedTag;
61  insertedTag.setGid(gid);
62  if (cmd.parentId() >= 0) {
63  insertedTag.setParentId(cmd.parentId());
64  }
65  if (tagType.isValid()) {
66  insertedTag.setTypeId(tagType.id());
67  }
68  if (!insertedTag.insert(&tagId)) {
69  return failureResponse("Failed to store tag");
70  }
71 
72  const Protocol::Attributes attrs = cmd.attributes();
73  for (auto iter = attrs.cbegin(), end = attrs.cend(); iter != end; ++iter) {
74  TagAttribute attribute;
75  attribute.setTagId(tagId);
76  attribute.setType(iter.key());
77  attribute.setValue(iter.value());
78  if (!attribute.insert()) {
79  return failureResponse("Failed to store tag attribute");
80  }
81  }
82 
83  storageBackend()->notificationCollector()->tagAdded(insertedTag);
84  }
85 
86  if (!cmd.remoteId().isEmpty()) {
87  const qint64 resourceId = connection()->context().resource().id();
88 
89  CountQueryBuilder qb(TagRemoteIdResourceRelation::tableName());
90  qb.addValueCondition(TagRemoteIdResourceRelation::tagIdColumn(), Query::Equals, tagId);
91  qb.addValueCondition(TagRemoteIdResourceRelation::resourceIdColumn(), Query::Equals, resourceId);
92  if (!qb.exec()) {
93  return failureResponse("Failed to query for existing TagRemoteIdResourceRelation entries");
94  }
95  const bool exists = (qb.result() > 0);
96 
97  //If the relation is already existing simply update it (can happen if a resource simply creates the tag again while enabling merge)
98  bool ret = false;
99  if (exists) {
100  //Simply using update() doesn't work since TagRemoteIdResourceRelation only takes the tagId for identification of the column
101  QueryBuilder qb(TagRemoteIdResourceRelation::tableName(), QueryBuilder::Update);
102  qb.addValueCondition(TagRemoteIdResourceRelation::tagIdColumn(), Query::Equals, tagId);
103  qb.addValueCondition(TagRemoteIdResourceRelation::resourceIdColumn(), Query::Equals, resourceId);
104  qb.setColumnValue(TagRemoteIdResourceRelation::remoteIdColumn(), QString::fromUtf8(cmd.remoteId()));
105  ret = qb.exec();
106  } else {
107  TagRemoteIdResourceRelation rel;
108  rel.setTagId(tagId);
109  rel.setResourceId(resourceId);
110  rel.setRemoteId(QString::fromUtf8(cmd.remoteId()));
111  ret = rel.insert();
112  }
113  if (!ret) {
114  return failureResponse("Failed to store tag remote ID");
115  }
116  }
117 
118  trx.commit();
119 
120  Scope scope;
121  ImapSet set;
122  set.add(QVector<qint64>() << tagId);
123  scope.setUidSet(set);
124 
125  Protocol::TagFetchScope fetchScope;
126  fetchScope.setFetchRemoteID(true);
127  fetchScope.setFetchAllAttributes(true);
128 
129  TagFetchHelper helper(connection(), scope, fetchScope);
130  if (!helper.fetchTags()) {
131  return failureResponse("Failed to fetch the new tag");
132  }
133 
134  return successResponse<Protocol::CreateTagResponse>();
135 }
The handler interfaces describes an entity capable of handling an AkonadiIMAP command.
Definition: handler.h:35
QString fromUtf8(const char *str, int size)
Helper class for DataStore transaction handling.
Definition: transaction.h:24
Attribute that stores the properties that are used to display a tag.
Definition: tagattribute.h:24
const QList< QKeySequence > & end()
Helper integration between Akonadi and Qt.
An Akonadi Tag.
Definition: tag.h:26
Helper class for creating queries to count elements in a database.
Helper class to construct arbitrary SQL queries.
Definition: querybuilder.h:32
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Sun Jul 12 2020 23:16:58 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.