7#include "itemmodifyjob.h"
8#include "akonadicore_debug.h"
9#include "itemmodifyjob_p.h"
11#include "changemediator_p.h"
12#include "collection.h"
13#include "conflicthandler_p.h"
15#include "itemserializer_p.h"
18#include "gidextractor_p.h"
19#include "protocolhelper_p.h"
29ItemModifyJobPrivate::ItemModifyJobPrivate(
ItemModifyJob *parent)
34void ItemModifyJobPrivate::setClean()
36 mOperations.insert(Dirty);
39Protocol::PartMetaData ItemModifyJobPrivate::preparePart(
const QByteArray &partName)
41 ProtocolHelper::PartNamespace ns;
42 const QByteArray partLabel = ProtocolHelper::decodePartIdentifier(partName, ns);
43 if (!mParts.contains(partLabel)) {
45 return Protocol::PartMetaData();
50 const auto item = mItems.
first();
51 if (mForeignParts.contains(partLabel)) {
52 mPendingData = item.d_ptr->mPayloadPath.toUtf8();
53 const auto size =
QFile(item.d_ptr->mPayloadPath).
size();
54 return Protocol::PartMetaData(partName, size, version, Protocol::PartMetaData::Foreign);
56 ItemSerializer::serialize(mItems.first(), partLabel, mPendingData, version);
57 return Protocol::PartMetaData(partName, mPendingData.size(), version);
61void ItemModifyJobPrivate::conflictResolved()
65 q->setError(KJob::NoError);
70void ItemModifyJobPrivate::conflictResolveError(
const QString &message)
74 q->setErrorText(q->errorText() + message);
78void ItemModifyJobPrivate::doUpdateItemRevision(
Akonadi::Item::Id itemId,
int oldRevision,
int newRevision)
80 auto it = std::find_if(mItems.begin(), mItems.end(), [&itemId](
const Item &item) ->
bool {
81 return item.id() == itemId;
83 if (it != mItems.end() && (*it).revision() == oldRevision) {
84 (*it).setRevision(newRevision);
88QString ItemModifyJobPrivate::jobDebuggingString()
const
91 mRemainingItems = std::span(mItems);
92 return Protocol::debugString(fullCommand());
98void ItemModifyJobPrivate::setSilent(
bool silent)
103bool ItemModifyJobPrivate::nextBatch()
107 if (mRemainingItems.empty()) {
111 Protocol::ModifyItemsCommandPtr command;
113 command = fullCommand();
121 if (command->modifiedParts() == Protocol::ModifyItemsCommand::None) {
131 :
Job(new ItemModifyJobPrivate(this),
parent)
135 d->mItems.append(
item);
136 d->mParts =
item.loadedPayloadParts();
138 d->mOperations.insert(ItemModifyJobPrivate::RemoteId);
139 d->mOperations.insert(ItemModifyJobPrivate::RemoteRevision);
141 if (!
item.payloadPath().isEmpty()) {
142 d->mForeignParts = ItemSerializer::allowedForeignParts(
item);
147 :
Job(new ItemModifyJobPrivate(this),
parent)
149 Q_ASSERT(!
items.isEmpty());
154 if (d->mItems.size() == 1) {
155 d->mParts =
items.first().loadedPayloadParts();
156 d->mOperations.insert(ItemModifyJobPrivate::RemoteId);
157 d->mOperations.insert(ItemModifyJobPrivate::RemoteRevision);
159 d->mIgnorePayload =
true;
160 d->mRevCheck =
false;
166Protocol::ModifyItemsCommandPtr ItemModifyJobPrivate::fullCommand()
const
168 auto cmd = Protocol::ModifyItemsCommandPtr::create();
171 for (
int op : std::as_const(mOperations)) {
173 case ItemModifyJobPrivate::RemoteId:
178 case ItemModifyJobPrivate::Gid: {
179 const QString gid = GidExtractor::getGid(item);
185 case ItemModifyJobPrivate::RemoteRevision:
190 case ItemModifyJobPrivate::Dirty:
191 cmd->setDirty(
false);
196 if (item.d_ptr->mClearPayload) {
197 cmd->setInvalidateCache(
true);
200 cmd->setNotify(
true);
203 if (item.d_ptr->mFlagsOverwritten) {
204 cmd->setFlags(item.
flags());
206 const auto addedFlags = ItemChangeLog::instance()->addedFlags(item.d_ptr);
207 if (!addedFlags.isEmpty()) {
208 cmd->setAddedFlags(addedFlags);
210 const auto deletedFlags = ItemChangeLog::instance()->deletedFlags(item.d_ptr);
211 if (!deletedFlags.isEmpty()) {
212 cmd->setRemovedFlags(deletedFlags);
216 if (item.d_ptr->mTagsOverwritten) {
217 const auto tags = item.tags();
218 if (!tags.isEmpty()) {
219 cmd->setTags(ProtocolHelper::entitySetToScope(tags));
222 const auto addedTags = ItemChangeLog::instance()->addedTags(item.d_ptr);
223 if (!addedTags.isEmpty()) {
224 cmd->setAddedTags(ProtocolHelper::entitySetToScope(addedTags));
226 const auto deletedTags = ItemChangeLog::instance()->deletedTags(item.d_ptr);
227 if (!deletedTags.isEmpty()) {
228 cmd->setRemovedTags(ProtocolHelper::entitySetToScope(deletedTags));
232 if (!mParts.isEmpty()) {
233 QSet<QByteArray> parts;
235 for (
const QByteArray &part : std::as_const(mParts)) {
236 parts.
insert(ProtocolHelper::encodePartIdentifier(ProtocolHelper::PartPayload, part));
238 cmd->setParts(parts);
241 const AttributeStorage &attributeStorage = ItemChangeLog::instance()->attributeStorage(item.d_ptr);
242 const QSet<QByteArray> deletedAttributes = attributeStorage.deletedAttributes();
243 if (!deletedAttributes.
isEmpty()) {
244 QSet<QByteArray> removedParts;
246 for (
const QByteArray &part : deletedAttributes) {
247 removedParts.
insert(
"ATR:" + part);
249 cmd->setRemovedParts(removedParts);
251 if (attributeStorage.hasModifiedAttributes()) {
252 cmd->setAttributes(ProtocolHelper::attributesToProtocol(attributeStorage.modifiedAttributes()));
256 if (cmd->modifiedParts() == Protocol::ModifyItemsCommand::None && mParts.isEmpty() && !cmd->invalidateCache()) {
260 const auto batchSize = qMin(MaxBatchSize, mRemainingItems.size());
261 const auto batch = mRemainingItems.subspan(0, batchSize);
262 mRemainingItems = mRemainingItems.subspan(batch.size());
264 cmd->setItems(ProtocolHelper::entitySetToScope(QList(batch.begin(), batch.end())));
265 if (mRevCheck && item.
revision() >= 0) {
266 cmd->setOldRevision(item.
revision());
269 if (item.d_ptr->mSizeChanged) {
270 cmd->setItemSize(item.
size());
280 d->mRemainingItems = std::span(d->mItems);
288 if (!response->isResponse() && response->type() == Protocol::Command::StreamPayload) {
289 const auto &streamCmd = Protocol::cmdCast<Protocol::StreamPayloadCommand>(response);
290 auto streamResp = Protocol::StreamPayloadResponsePtr::create();
291 if (streamCmd.request() == Protocol::StreamPayloadCommand::MetaData) {
292 streamResp->setMetaData(d->preparePart(streamCmd.payloadName()));
294 if (streamCmd.destination().isEmpty()) {
295 streamResp->setData(d->mPendingData);
298 if (!ProtocolHelper::streamPayloadToFile(streamCmd.destination(), d->mPendingData,
error)) {
303 d->sendCommand(tag, streamResp);
307 if (response->isResponse() && response->type() == Protocol::Command::ModifyItems) {
308 const auto &resp = Protocol::cmdCast<Protocol::ModifyItemsResponse>(response);
309 if (resp.errorCode()) {
316 if (d->mAutomaticConflictHandlingEnabled) {
317 auto handler =
new ConflictHandler(ConflictHandler::LocalLocalConflict,
this);
318 handler->setConflictingItems(d->mItems.first(), d->mItems.first());
319 connect(handler, &ConflictHandler::conflictResolved,
this, [d]() {
320 d->conflictResolved();
322 connect(handler, &ConflictHandler::error,
this, [d](
const QString &str) {
323 d->conflictResolveError(str);
330 if (resp.modificationDateTime().isValid()) {
332 item.setModificationTime(resp.modificationDateTime());
333 item.d_ptr->resetChangeLog();
334 }
else if (resp.id() > -1) {
335 auto it = std::find_if(d->mItems.begin(), d->mItems.end(), [&resp](
const Item &
item) ->
bool {
336 return item.id() == resp.id();
338 if (it == d->mItems.end()) {
339 qCDebug(AKONADICORE_LOG) <<
"Received STORE response for an item we did not modify: " << tag << Protocol::debugString(response);
343 const int newRev = resp.newRevision();
344 const int oldRev = (*it).revision();
345 if (newRev >= oldRev && newRev >= 0) {
346 d->itemRevisionChanged((*it).id(), oldRev, newRev);
347 (*it).setRevision(newRev);
354 if (!d->mRemainingItems.empty()) {
355 if (!d->nextBatch()) {
360 for (
const Item &
item : std::as_const(d->mItems)) {
361 ChangeMediator::invalidateItem(
item);
374 if (d->mIgnorePayload == ignore) {
378 d->mIgnorePayload = ignore;
379 if (d->mIgnorePayload) {
382 Q_ASSERT(!d->mItems.first().mimeType().isEmpty());
383 d->mParts = d->mItems.first().loadedPayloadParts();
391 return d->mIgnorePayload;
398 d->mOperations.insert(ItemModifyJobPrivate::Gid);
400 d->mOperations.remove(ItemModifyJobPrivate::Gid);
407 return d->mOperations.contains(ItemModifyJobPrivate::Gid);
414 d->mRevCheck =
false;
421 d->mAutomaticConflictHandlingEnabled =
false;
427 Q_ASSERT(d->mItems.size() == 1);
429 return d->mItems.first();
438#include "moc_itemmodifyjob.cpp"
Base class for exceptions used by the Akonadi library.
const char * what() const noexcept override
Returns the error message associated with this exception.
Job that modifies an existing item in the Akonadi storage.
void setIgnorePayload(bool ignore)
Sets whether the payload of the modified item shall be omitted from transmission to the Akonadi stora...
void disableRevisionCheck()
Disables the check of the revision number.
bool updateGid() const
Returns whether the GID should be updated.
~ItemModifyJob() override
Destroys the item modify job.
ItemModifyJob(const Item &item, QObject *parent=nullptr)
Creates a new item modify job.
bool ignorePayload() const
Returns whether the payload of the modified item shall be omitted from transmission to the Akonadi st...
void disableAutomaticConflictHandling()
Disables the automatic handling of conflicts.
Item item() const
Returns the modified and stored item including the changed revision number.
void setUpdateGid(bool update)
Sets whether the GID shall be updated either from the gid parameter or by extracting it from the payl...
void doStart() override
This method must be reimplemented in the concrete jobs.
Item::List items() const
Returns the modified and stored items including the changed revision number.
bool doHandleResponse(qint64 tag, const Protocol::CommandPtr &response) override
This method should be reimplemented in the concrete jobs in case you want to handle incoming data.
Represents a PIM item stored in Akonadi storage.
QString remoteRevision() const
Returns the remote revision of the item.
qint64 Id
Describes the unique id type.
qint64 size() const
Returns the size of the items in bytes.
Flags flags() const
Returns all flags of this item.
int revision() const
Returns the revision number of the item.
QString remoteId() const
Returns the remote id of the item.
QList< Item > List
Describes a list of items.
virtual bool doHandleResponse(qint64 tag, const Protocol::CommandPtr &response)
This method should be reimplemented in the concrete jobs in case you want to handle incoming data.
Job(QObject *parent=nullptr)
Creates a new job.
void setErrorText(const QString &errorText)
void setError(int errorCode)
Helper integration between Akonadi and Qt.
KLEO_EXPORT std::unique_ptr< GpgME::DefaultAssuanTransaction > sendCommand(std::shared_ptr< GpgME::Context > &assuanContext, const std::string &command, GpgME::Error &err)
NETWORKMANAGERQT_EXPORT QString version()
virtual qint64 size() const const override
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QObject * parent() const const
iterator insert(const T &value)
bool isEmpty() const const
void reserve(qsizetype size)
qsizetype size() const const
QString first(qsizetype n) const const
QString fromUtf8(QByteArrayView str)
bool isNull() const const