1 /*
2 * kalarmresourcecommon.cpp - common functions for KAlarm Akonadi resources
3 * Program: kalarm
4 * SPDX-FileCopyrightText: 2009-2019 David Jarvie <djarvie@kde.org>
5 *
6 * SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8
9 #include "kalarmresourcecommon.h"
10
11 #include <KAlarmCal/CompatibilityAttribute>
12 #include <KAlarmCal/EventAttribute>
13
14 #include <Akonadi/AttributeFactory>
15 #include <Akonadi/CollectionModifyJob>
16
17 #include <KCalendarCore/FileStorage>
18 #include <KCalendarCore/MemoryCalendar>
19
20 #include <KLocalizedString>
21
22 #include <QDebug>
23 #include <QTime>
24
25 using namespace Akonadi;
26 using namespace KCalendarCore;
27 using namespace KAlarmCal;
28
29 class Private : public QObject
30 {
31 Q_OBJECT
32 public:
Private(QObject * parent)33 Private(QObject *parent)
34 : QObject(parent)
35 {
36 }
37
38 static Private *mInstance;
39
40 private Q_SLOTS:
41 void modifyCollectionJobDone(KJob *);
42 };
43 Private *Private::mInstance = nullptr;
44
45 namespace KAlarmResourceCommon
46 {
47 /******************************************************************************
48 * Perform common initialisation for KAlarm resources.
49 */
initialise(QObject * parent)50 void initialise(QObject *parent)
51 {
52 // Create an object which can receive signals.
53 if (!Private::mInstance) {
54 Private::mInstance = new Private(parent);
55 }
56
57 // Set a default start-of-day time for date-only alarms.
58 KAEvent::setStartOfDay(QTime(0, 0, 0));
59
60 AttributeFactory::registerAttribute<CompatibilityAttribute>();
61 AttributeFactory::registerAttribute<EventAttribute>();
62 }
63
64 /******************************************************************************
65 * Find the compatibility of an existing calendar file, and convert it in
66 * memory to the current KAlarm format (if possible).
67 */
getCompatibility(const FileStorage::Ptr & fileStorage,int & version)68 KACalendar::Compat getCompatibility(const FileStorage::Ptr &fileStorage, int &version)
69 {
70 QString versionString;
71 version = KACalendar::updateVersion(fileStorage, versionString);
72 switch (version) {
73 case KACalendar::IncompatibleFormat:
74 return KACalendar::Incompatible; // calendar is not in KAlarm format, or is in a future format
75 case KACalendar::CurrentFormat:
76 return KACalendar::Current; // calendar is in the current format
77 default:
78 return KACalendar::Convertible; // calendar is in an out of date format
79 }
80 }
81
82 /******************************************************************************
83 * Set an event into a new item's payload and return the new item.
84 * The caller should signal its retrieval by calling itemRetrieved(newitem).
85 * NOTE: the caller must set the event's compatibility beforehand.
86 */
retrieveItem(const Akonadi::Item & item,KAEvent & event)87 Item retrieveItem(const Akonadi::Item &item, KAEvent &event)
88 {
89 const QString mime = CalEvent::mimeType(event.category());
90 if (item.hasAttribute<EventAttribute>()) {
91 event.setCommandError(item.attribute<EventAttribute>()->commandError());
92 }
93
94 Item newItem = item;
95 newItem.setMimeType(mime);
96 newItem.setPayload<KAEvent>(event);
97 return newItem;
98 }
99
100 /******************************************************************************
101 * Called when an item has been changed to validate it.
102 * Reply = the KAEvent for the item
103 * = invalid if error, in which case errorMsg contains the error message
104 * (which will be empty if the KAEvent is simply invalid).
105 */
checkItemChanged(const Akonadi::Item & item,QString & errorMsg)106 KAEvent checkItemChanged(const Akonadi::Item &item, QString &errorMsg)
107 {
108 KAEvent event;
109 if (item.hasPayload<KAEvent>()) {
110 event = item.payload<KAEvent>();
111 }
112 if (event.isValid()) {
113 if (item.remoteId() != event.id()) {
114 qWarning() << "Item ID" << item.remoteId() << "differs from payload ID" << event.id();
115 errorMsg = i18nc("@info", "Item ID %1 differs from payload ID %2.", item.remoteId(), event.id());
116 return KAEvent();
117 }
118 }
119
120 errorMsg.clear();
121 return event;
122 }
123
124 /******************************************************************************
125 * Set a collection's compatibility attribute.
126 * Note that because this parameter is set asynchronously by the resource, it
127 * can't be stored in the same attribute as other collection parameters which
128 * are written by the application. This avoids the resource and application
129 * overwriting each other's changes if they attempt simultaneous updates.
130 */
setCollectionCompatibility(const Collection & collection,KACalendar::Compat compatibility,int version)131 void setCollectionCompatibility(const Collection &collection, KACalendar::Compat compatibility, int version)
132 {
133 qDebug() << "KAlarmResourceCommon::setCollectionCompatibility:" << collection.id() << "->" << compatibility << version;
134 // Update the CompatibilityAttribute value only.
135 // Note that we can't supply 'collection' to CollectionModifyJob since
136 // that may also contain the CollectionAttribute value, which is read-only
137 // for the resource. So create a new Collection instance and only set a
138 // value for CompatibilityAttribute.
139 Collection col(collection.id());
140 if (!collection.isValid()) {
141 col.setParentCollection(collection.parentCollection());
142 col.setRemoteId(collection.remoteId());
143 }
144 auto attr = col.attribute<CompatibilityAttribute>(Collection::AddIfMissing);
145 attr->setCompatibility(compatibility);
146 attr->setVersion(version);
147 Q_ASSERT(Private::mInstance);
148 auto job = new CollectionModifyJob(col, Private::mInstance->parent());
149 Private::mInstance->connect(job, SIGNAL(result(KJob *)), SLOT(modifyCollectionJobDone(KJob *)));
150 }
151
152 /******************************************************************************
153 * Return an error message common to more than one resource.
154 */
errorMessage(ErrorCode code,const QString & param)155 QString errorMessage(ErrorCode code, const QString ¶m)
156 {
157 switch (code) {
158 case UidNotFound:
159 return i18nc("@info", "Event with uid '%1' not found.", param);
160 case NotCurrentFormat:
161 return i18nc("@info", "Calendar is not in current KAlarm format.");
162 case EventNotCurrentFormat:
163 return i18nc("@info", "Event with uid '%1' is not in current KAlarm format.", param);
164 case EventNoAlarms:
165 return i18nc("@info", "Event with uid '%1' contains no usable alarms.", param);
166 case EventReadOnly:
167 return i18nc("@info", "Event with uid '%1' is read only", param);
168 case CalendarAdd:
169 return i18nc("@info", "Failed to add event with uid '%1' to calendar", param);
170 }
171 return QString();
172 }
173 } // namespace KAlarmResourceCommon
174
175 /******************************************************************************
176 * Called when a collection modification job has completed, to report any error.
177 */
modifyCollectionJobDone(KJob * j)178 void Private::modifyCollectionJobDone(KJob *j)
179 {
180 qDebug() << "KAlarmResourceCommon::modifyCollectionJobDone";
181 if (j->error()) {
182 Collection collection = static_cast<CollectionModifyJob *>(j)->collection();
183 qCritical() << "Error: modifyCollectionJobDone: collection" << collection.id() << ":" << j->errorString();
184 }
185 }
186
187 #include "kalarmresourcecommon.moc"
188