Akonadi

itemmovehandler.cpp
1 /*
2  Copyright (c) 2009 Volker Krause <[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 "itemmovehandler.h"
21 
22 #include "akonadi.h"
23 #include "connection.h"
24 #include "handlerhelper.h"
25 #include "cachecleaner.h"
26 #include "storage/datastore.h"
27 #include "storage/itemretrievalmanager.h"
28 #include "storage/itemretriever.h"
29 #include "storage/itemqueryhelper.h"
30 #include "storage/selectquerybuilder.h"
31 #include "storage/transaction.h"
32 #include "storage/collectionqueryhelper.h"
33 #include "akonadiserver_debug.h"
34 
35 using namespace Akonadi;
36 using namespace Akonadi::Server;
37 
38 ItemMoveHandler::ItemMoveHandler(AkonadiServer &akonadi)
39  : Handler(akonadi)
40 {}
41 
42 void ItemMoveHandler::itemsRetrieved(const QVector<qint64> &ids)
43 {
44  DataStore *store = connection()->storageBackend();
45  Transaction transaction(store, QStringLiteral("MOVE"));
46 
48  qb.setForUpdate();
49  ItemQueryHelper::itemSetToQuery(ImapSet(ids), qb);
50  qb.addValueCondition(PimItem::collectionIdFullColumnName(), Query::NotEquals, mDestination.id());
51 
52 
53  if (!qb.exec()) {
54  failureResponse("Unable to execute query");
55  return;
56  }
57 
58  const QVector<PimItem> items = qb.result();
59  if (items.isEmpty()) {
60  return;
61  }
62 
64  // Split the list by source collection
65  QMap<Entity::Id /* collection */, PimItem> toMove;
66  QMap<Entity::Id /* collection */, Collection> sources;
67  ImapSet toMoveIds;
68  for (PimItem item : items) { //krazy:exclude=foreach
69  if (!item.isValid()) {
70  failureResponse("Invalid item in result set!?");
71  return;
72  }
73 
74  const Collection source = item.collection();
75  if (!source.isValid()) {
76  failureResponse("Item without collection found!?");
77  return;
78  }
79  if (!sources.contains(source.id())) {
80  sources.insert(source.id(), source);
81  }
82 
83  Q_ASSERT(item.collectionId() != mDestination.id());
84 
85  item.setCollectionId(mDestination.id());
86  item.setAtime(mtime);
87  item.setDatetime(mtime);
88  // if the resource moved itself, we assume it did so because the change happenned in the backend
89  if (connection()->context().resource().id() != mDestination.resourceId()) {
90  item.setDirty(true);
91  }
92 
93  if (!item.update()) {
94  failureResponse("Unable to update item");
95  return;
96  }
97 
98  toMove.insertMulti(source.id(), item);
99  toMoveIds.add(QVector<qint64>{ item.id() });
100  }
101 
102  if (!transaction.commit()) {
103  failureResponse("Unable to commit transaction.");
104  return;
105  }
106 
107  // Emit notification for each source collection separately
108  Collection source;
109  PimItem::List itemsToMove;
110  for (auto it = toMove.cbegin(), end = toMove.cend(); it != end; ++it) {
111  if (source.id() != it.key()) {
112  if (!itemsToMove.isEmpty()) {
113  store->notificationCollector()->itemsMoved(itemsToMove, source, mDestination);
114  }
115  source = sources.value(it.key());
116  itemsToMove.clear();
117  }
118 
119  itemsToMove.push_back(*it);
120  }
121 
122  if (!itemsToMove.isEmpty()) {
123  store->notificationCollector()->itemsMoved(itemsToMove, source, mDestination);
124  }
125 
126  // Batch-reset RID
127  // The item should have an empty RID in the destination collection to avoid
128  // RID conflicts with existing items (see T3904 in Phab).
129  // We do it after emitting notification so that the FetchHelper can still
130  // retrieve the RID
131  QueryBuilder qb2(PimItem::tableName(), QueryBuilder::Update);
132  qb2.setColumnValue(PimItem::remoteIdColumn(), QString());
133  ItemQueryHelper::itemSetToQuery(toMoveIds, connection()->context(), qb2);
134  if (!qb2.exec()) {
135  failureResponse("Unable to update RID");
136  return;
137  }
138 
139 }
140 
141 bool ItemMoveHandler::parseStream()
142 {
143  const auto &cmd = Protocol::cmdCast<Protocol::MoveItemsCommand>(m_command);
144 
145  mDestination = HandlerHelper::collectionFromScope(cmd.destination(), connection()->context());
146  if (mDestination.isVirtual()) {
147  return failureResponse("Moving items into virtual collection is not allowed");
148  }
149  if (!mDestination.isValid()) {
150  return failureResponse("Invalid destination collection");
151  }
152 
153  CommandContext context = connection()->context();
154  context.setScopeContext(cmd.itemsContext());
155  if (cmd.items().scope() == Scope::Rid) {
156  if (!context.collection().isValid()) {
157  return failureResponse("RID move requires valid source collection");
158  }
159  }
160 
161  CacheCleanerInhibitor inhibitor(akonadi());
162 
163  // make sure all the items we want to move are in the cache
164  ItemRetriever retriever(akonadi().itemRetrievalManager(), connection(), context);
165  retriever.setScope(cmd.items());
166  retriever.setRetrieveFullPayload(true);
167  QObject::connect(&retriever, &ItemRetriever::itemsRetrieved,
168  [this](const QVector<qint64> &ids) {
169  itemsRetrieved(ids);
170  });
171  if (!retriever.exec()) {
172  return failureResponse(retriever.lastError());
173  }
174 
175  return successResponse<Protocol::MoveItemsResponse>();
176 }
void addValueCondition(const QString &column, Query::CompareOperator op, const QVariant &value, ConditionType type=WhereCondition)
Add a WHERE or HAVING condition which compares a column with a given value.
NotificationCollector * notificationCollector()
Returns the notification collector of this DataStore object.
Definition: datastore.cpp:226
bool isValid() const
Returns whether the collection is valid.
Definition: collection.cpp:137
void setForUpdate(bool forUpdate=true)
Indicate to the database to acquire an exclusive lock on the rows already during SELECT statement...
Helper class for retrieving missing items parts from remote resources.
Definition: itemretriever.h:53
The handler interfaces describes an entity capable of handling an AkonadiIMAP command.
Definition: handler.h:48
Represents a collection of PIM items.
Definition: collection.h:76
A RAII helper class to temporarily stop the CacheCleaner.
Definition: cachecleaner.h:46
void itemsMoved(const PimItem::List &items, const Collection &collectionSrc=Collection(), const Collection &collectionDest=Collection(), const QByteArray &sourceResource=QByteArray())
Notify about moved items Provide as many parameters as you have at hand currently, everything that is missing will be looked up in the database later.
void clear()
Helper class for DataStore transaction handling.
Definition: transaction.h:37
QVector< T > result()
Returns the result of this SELECT query.
void setScope(const Scope &scope)
Retrieve all items matching the given item scope.
Item item() const
Returns the currently monitored item.
Definition: itemmonitor.cpp:62
const QList< QKeySequence > & end()
Helper class for creating and executing database SELECT queries.
Id id() const
Returns the unique identifier of the collection.
Definition: collection.cpp:112
bool isEmpty() const const
Helper integration between Akonadi and Qt.
void push_back(const T &value)
This class handles all the database access.
Definition: datastore.h:102
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
Helper class to construct arbitrary SQL queries.
Definition: querybuilder.h:45
QDateTime currentDateTimeUtc()
Representation of a record in the PimItem table.
Definition: entities.h:1194
bool exec()
Executes the query, returns true on success.
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Wed May 27 2020 22:43:39 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.