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"
27ItemModifyJobPrivate::ItemModifyJobPrivate(
ItemModifyJob *parent)
32void ItemModifyJobPrivate::setClean()
34 mOperations.insert(Dirty);
37Protocol::PartMetaData ItemModifyJobPrivate::preparePart(
const QByteArray &partName)
39 ProtocolHelper::PartNamespace ns;
40 const QByteArray partLabel = ProtocolHelper::decodePartIdentifier(partName, ns);
41 if (!mParts.contains(partLabel)) {
43 return Protocol::PartMetaData();
48 const auto item = mItems.first();
49 if (mForeignParts.contains(partLabel)) {
50 mPendingData = item.d_ptr->mPayloadPath.toUtf8();
51 const auto size =
QFile(item.d_ptr->mPayloadPath).
size();
52 return Protocol::PartMetaData(partName, size, version, Protocol::PartMetaData::Foreign);
54 ItemSerializer::serialize(mItems.first(), partLabel, mPendingData, version);
55 return Protocol::PartMetaData(partName, mPendingData.size(), version);
59void ItemModifyJobPrivate::conflictResolved()
63 q->setError(KJob::NoError);
68void ItemModifyJobPrivate::conflictResolveError(
const QString &message)
72 q->setErrorText(q->errorText() + message);
76void ItemModifyJobPrivate::doUpdateItemRevision(
Akonadi::Item::Id itemId,
int oldRevision,
int newRevision)
78 auto it = std::find_if(mItems.begin(), mItems.end(), [&itemId](
const Item &item) ->
bool {
79 return item.id() == itemId;
81 if (it != mItems.end() && (*it).revision() == oldRevision) {
82 (*it).setRevision(newRevision);
86QString ItemModifyJobPrivate::jobDebuggingString()
const
89 return Protocol::debugString(fullCommand());
95void ItemModifyJobPrivate::setSilent(
bool silent)
101 :
Job(new ItemModifyJobPrivate(this), parent)
105 d->mItems.append(
item);
108 d->mOperations.
insert(ItemModifyJobPrivate::RemoteId);
109 d->mOperations.insert(ItemModifyJobPrivate::RemoteRevision);
112 d->mForeignParts = ItemSerializer::allowedForeignParts(
item);
117 :
Job(new ItemModifyJobPrivate(this), parent)
124 if (d->mItems.size() == 1) {
126 d->mOperations.
insert(ItemModifyJobPrivate::RemoteId);
127 d->mOperations.insert(ItemModifyJobPrivate::RemoteRevision);
129 d->mIgnorePayload =
true;
130 d->mRevCheck =
false;
138Protocol::ModifyItemsCommandPtr ItemModifyJobPrivate::fullCommand()
const
140 auto cmd = Protocol::ModifyItemsCommandPtr::create();
143 for (
int op : std::as_const(mOperations)) {
145 case ItemModifyJobPrivate::RemoteId:
150 case ItemModifyJobPrivate::Gid: {
151 const QString gid = GidExtractor::getGid(item);
157 case ItemModifyJobPrivate::RemoteRevision:
162 case ItemModifyJobPrivate::Dirty:
163 cmd->setDirty(
false);
168 if (item.d_ptr->mClearPayload) {
169 cmd->setInvalidateCache(
true);
172 cmd->setNotify(
true);
175 if (item.d_ptr->mFlagsOverwritten) {
176 cmd->setFlags(item.
flags());
178 const auto addedFlags = ItemChangeLog::instance()->addedFlags(item.d_ptr);
179 if (!addedFlags.isEmpty()) {
180 cmd->setAddedFlags(addedFlags);
182 const auto deletedFlags = ItemChangeLog::instance()->deletedFlags(item.d_ptr);
183 if (!deletedFlags.isEmpty()) {
184 cmd->setRemovedFlags(deletedFlags);
188 if (item.d_ptr->mTagsOverwritten) {
189 const auto tags = item.tags();
190 if (!tags.isEmpty()) {
191 cmd->setTags(ProtocolHelper::entitySetToScope(tags));
194 const auto addedTags = ItemChangeLog::instance()->addedTags(item.d_ptr);
195 if (!addedTags.isEmpty()) {
196 cmd->setAddedTags(ProtocolHelper::entitySetToScope(addedTags));
198 const auto deletedTags = ItemChangeLog::instance()->deletedTags(item.d_ptr);
199 if (!deletedTags.isEmpty()) {
200 cmd->setRemovedTags(ProtocolHelper::entitySetToScope(deletedTags));
204 if (!mParts.isEmpty()) {
207 for (
const QByteArray &part : std::as_const(mParts)) {
208 parts.
insert(ProtocolHelper::encodePartIdentifier(ProtocolHelper::PartPayload, part));
210 cmd->setParts(parts);
213 const AttributeStorage &attributeStorage = ItemChangeLog::instance()->attributeStorage(item.d_ptr);
214 const QSet<QByteArray> deletedAttributes = attributeStorage.deletedAttributes();
215 if (!deletedAttributes.
isEmpty()) {
218 for (
const QByteArray &part : deletedAttributes) {
219 removedParts.
insert(
"ATR:" + part);
221 cmd->setRemovedParts(removedParts);
223 if (attributeStorage.hasModifiedAttributes()) {
224 cmd->setAttributes(ProtocolHelper::attributesToProtocol(attributeStorage.modifiedAttributes()));
228 if (cmd->modifiedParts() == Protocol::ModifyItemsCommand::None && mParts.isEmpty() && !cmd->invalidateCache()) {
232 cmd->setItems(ProtocolHelper::entitySetToScope(mItems));
233 if (mRevCheck && item.
revision() >= 0) {
234 cmd->setOldRevision(item.
revision());
237 if (item.d_ptr->mSizeChanged) {
238 cmd->setItemSize(item.
size());
248 Protocol::ModifyItemsCommandPtr command;
250 command = d->fullCommand();
258 if (command->modifiedParts() == Protocol::ModifyItemsCommand::None) {
263 d->sendCommand(command);
270 if (!response->isResponse() && response->type() == Protocol::Command::StreamPayload) {
271 const auto &streamCmd = Protocol::cmdCast<Protocol::StreamPayloadCommand>(response);
272 auto streamResp = Protocol::StreamPayloadResponsePtr::create();
273 if (streamCmd.request() == Protocol::StreamPayloadCommand::MetaData) {
274 streamResp->setMetaData(d->preparePart(streamCmd.payloadName()));
276 if (streamCmd.destination().isEmpty()) {
277 streamResp->setData(d->mPendingData);
280 if (!ProtocolHelper::streamPayloadToFile(streamCmd.destination(), d->mPendingData,
error)) {
285 d->sendCommand(tag, streamResp);
289 if (response->isResponse() && response->type() == Protocol::Command::ModifyItems) {
290 const auto &resp = Protocol::cmdCast<Protocol::ModifyItemsResponse>(response);
291 if (resp.errorCode()) {
298 if (d->mAutomaticConflictHandlingEnabled) {
299 auto handler =
new ConflictHandler(ConflictHandler::LocalLocalConflict,
this);
300 handler->setConflictingItems(d->mItems.first(), d->mItems.first());
301 connect(handler, &ConflictHandler::conflictResolved,
this, [d]() {
302 d->conflictResolved();
304 connect(handler, &ConflictHandler::error,
this, [d](
const QString &str) {
305 d->conflictResolveError(str);
312 if (resp.modificationDateTime().isValid()) {
315 item.d_ptr->resetChangeLog();
316 }
else if (resp.id() > -1) {
317 auto it = std::find_if(d->mItems.begin(), d->mItems.end(), [&resp](
const Item &
item) ->
bool {
318 return item.id() == resp.id();
320 if (it == d->mItems.end()) {
321 qCDebug(AKONADICORE_LOG) <<
"Received STORE response for an item we did not modify: " << tag << Protocol::debugString(response);
325 const int newRev = resp.newRevision();
326 const int oldRev = (*it).revision();
327 if (newRev >= oldRev && newRev >= 0) {
328 d->itemRevisionChanged((*it).id(), oldRev, newRev);
329 (*it).setRevision(newRev);
336 for (
const Item &
item : std::as_const(d->mItems)) {
337 ChangeMediator::invalidateItem(
item);
350 if (d->mIgnorePayload == ignore) {
354 d->mIgnorePayload = ignore;
355 if (d->mIgnorePayload) {
358 Q_ASSERT(!d->mItems.first().mimeType().isEmpty());
359 d->mParts = d->mItems.first().loadedPayloadParts();
367 return d->mIgnorePayload;
374 d->mOperations.insert(ItemModifyJobPrivate::Gid);
376 d->mOperations.remove(ItemModifyJobPrivate::Gid);
383 return d->mOperations.contains(ItemModifyJobPrivate::Gid);
390 d->mRevCheck =
false;
397 d->mAutomaticConflictHandlingEnabled =
false;
403 Q_ASSERT(d->mItems.size() == 1);
405 return d->mItems.first();
414#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 payloadPath() const
Returns path to the payload file set by setPayloadPath()
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.
void setModificationTime(const QDateTime &datetime)
Sets the timestamp of the last modification of this item.
int revision() const
Returns the revision number of the item.
QString remoteId() const
Returns the remote id of the item.
QSet< QByteArray > loadedPayloadParts() const
Returns the list of loaded payload parts.
Base class for all actions in the Akonadi storage.
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.
void setErrorText(const QString &errorText)
void setError(int errorCode)
Helper integration between Akonadi and Qt.
KCOREADDONS_EXPORT unsigned int version()
virtual qint64 size() const const override
bool isEmpty() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
iterator insert(const T &value)
bool isEmpty() const const
void reserve(qsizetype size)
qsizetype size() const const
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
bool isNull() const const