2 * calendarmigrator.cpp - migrates or creates KAlarm Akonadi resources
4 * Copyright © 2011-2015 by David Jarvie <djarvie@kde.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include "calendarmigrator.h"
22 #include "akonadimodel.h"
23 #include "functions.h"
24 #include "kalarmsettings.h"
25 #include "kalarmdirsettings.h"
26 #include "mainwindow.h"
27 #include "messagebox.h"
29 #include <kalarmcal/collectionattribute.h>
30 #include <kalarmcal/compatibilityattribute.h>
31 #include <kalarmcal/version.h>
33 #include <AkonadiCore/agentinstancecreatejob.h>
34 #include <AkonadiCore/agentmanager.h>
35 #include <AkonadiCore/collectionfetchjob.h>
36 #include <AkonadiCore/collectionfetchscope.h>
37 #include <AkonadiCore/collectionmodifyjob.h>
38 #include <AkonadiCore/entitydisplayattribute.h>
39 #include <AkonadiCore/resourcesynchronizationjob.h>
41 #include <KLocalizedString>
43 #include <KConfigGroup>
46 #include <QStandardPaths>
47 #include "kalarm_debug.h"
49 using namespace Akonadi
;
50 using namespace KAlarmCal
;
54 const QString
KALARM_RESOURCE(QStringLiteral("akonadi_kalarm_resource"));
55 const QString
KALARM_DIR_RESOURCE(QStringLiteral("akonadi_kalarm_dir_resource"));
58 // Creates, or migrates from KResources, a single alarm calendar
59 class CalendarCreator
: public QObject
63 // Constructor to migrate a calendar from KResources.
64 CalendarCreator(const QString
& resourceType
, const KConfigGroup
&);
65 // Constructor to create a default Akonadi calendar.
66 CalendarCreator(CalEvent::Type
, const QString
& file
, const QString
& name
);
67 bool isValid() const { return mAlarmType
!= CalEvent::EMPTY
; }
68 CalEvent::Type
alarmType() const { return mAlarmType
; }
69 bool newCalendar() const { return mNew
; }
70 QString
resourceName() const { return mName
; }
71 Collection::Id
collectionId() const { return mCollectionId
; }
72 QString
path() const { return mPath
; }
73 QString
errorMessage() const { return mErrorMessage
; }
74 void createAgent(const QString
& agentType
, QObject
* parent
);
77 void agentCreated(KJob
*);
80 void creating(const QString
& path
);
81 void finished(CalendarCreator
*);
84 void fetchCollection();
85 void collectionFetchResult(KJob
*);
86 void resourceSynchronised(KJob
*);
87 void modifyCollectionJobDone(KJob
*);
90 void finish(bool cleanup
);
91 bool writeLocalFileConfig();
92 bool writeLocalDirectoryConfig();
93 bool writeRemoteFileConfig();
94 template <class Interface
> Interface
* writeBasicConfig();
96 enum ResourceType
{ LocalFile
, LocalDir
, RemoteFile
};
99 CalEvent::Type mAlarmType
;
100 ResourceType mResourceType
;
104 QString mErrorMessage
;
105 Collection::Id mCollectionId
;
106 int mCollectionFetchRetryCount
;
110 const bool mNew
; // true if creating default, false if converting
114 // Updates the backend calendar format of a single alarm calendar
115 class CalendarUpdater
: public QObject
119 CalendarUpdater(const Collection
& collection
, bool dirResource
,
120 bool ignoreKeepFormat
, bool newCollection
, QObject
* parent
);
122 // Return whether another instance is already updating this collection
123 bool isDuplicate() const { return mDuplicate
; }
124 // Check whether any instance is for the given collection ID
125 static bool containsCollection(Collection::Id
);
131 static QList
<CalendarUpdater
*> mInstances
;
132 Akonadi::Collection mCollection
;
134 const bool mDirResource
;
135 const bool mIgnoreKeepFormat
;
136 const bool mNewCollection
;
137 const bool mDuplicate
; // another instance is already updating this collection
141 CalendarMigrator
* CalendarMigrator::mInstance
= Q_NULLPTR
;
142 bool CalendarMigrator::mCompleted
= false;
144 CalendarMigrator::CalendarMigrator(QObject
* parent
)
146 mExistingAlarmTypes(0)
150 CalendarMigrator::~CalendarMigrator()
153 mInstance
= Q_NULLPTR
;
156 /******************************************************************************
157 * Reset to allow migration to be run again.
159 void CalendarMigrator::reset()
164 /******************************************************************************
165 * Create and return the unique CalendarMigrator instance.
167 CalendarMigrator
* CalendarMigrator::instance()
169 if (!mInstance
&& !mCompleted
)
170 mInstance
= new CalendarMigrator
;
174 /******************************************************************************
175 * Migrate old KResource calendars, or if none, create default Akonadi resources.
177 void CalendarMigrator::execute()
179 instance()->migrateOrCreate();
182 /******************************************************************************
183 * Migrate old KResource calendars, and create default Akonadi resources.
185 void CalendarMigrator::migrateOrCreate()
189 // First, check whether any Akonadi resources already exist, and if
190 // so, find their alarm types.
191 const AgentInstance::List agents
= AgentManager::self()->instances();
192 foreach (const AgentInstance
& agent
, agents
)
194 const QString type
= agent
.type().identifier();
195 if (type
== KALARM_RESOURCE
|| type
== KALARM_DIR_RESOURCE
)
197 // Fetch the resource's collection to determine its alarm types
198 CollectionFetchJob
* job
= new CollectionFetchJob(Collection::root(), CollectionFetchJob::FirstLevel
);
199 job
->fetchScope().setResource(agent
.identifier());
200 mFetchesPending
<< job
;
201 connect(job
, &KJob::result
, this, &CalendarMigrator::collectionFetchResult
);
202 // Note: Once all collections have been fetched, any missing
203 // default resources will be created.
207 if (mFetchesPending
.isEmpty())
209 // There are no Akonadi resources, so migrate any KResources alarm
210 // calendars from pre-Akonadi versions of KAlarm.
211 const QString configFile
= QStandardPaths::writableLocation(QStandardPaths::ConfigLocation
) + QStringLiteral("/kresources/alarms/stdrc");
212 const KConfig
config(configFile
, KConfig::SimpleConfig
);
214 // Fetch all the KResource identifiers which are actually in use
215 const KConfigGroup group
= config
.group("General");
216 const QStringList keys
= group
.readEntry("ResourceKeys", QStringList())
217 + group
.readEntry("PassiveResourceKeys", QStringList());
219 // Create an Akonadi resource for each KResource id
220 CalendarCreator
* creator
;
221 foreach (const QString
& id
, keys
)
223 const KConfigGroup configGroup
= config
.group(QStringLiteral("Resource_") + id
);
224 const QString resourceType
= configGroup
.readEntry("ResourceType", QString());
226 if (resourceType
== QStringLiteral("file"))
227 agentType
= KALARM_RESOURCE
;
228 else if (resourceType
== QStringLiteral("dir"))
229 agentType
= KALARM_DIR_RESOURCE
;
230 else if (resourceType
== QStringLiteral("remote"))
231 agentType
= KALARM_RESOURCE
;
233 continue; // unknown resource type - can't convert
235 creator
= new CalendarCreator(resourceType
, configGroup
);
236 if (!creator
->isValid())
240 connect(creator
, &CalendarCreator::finished
, this, &CalendarMigrator::calendarCreated
);
241 connect(creator
, &CalendarCreator::creating
, this, &CalendarMigrator::creatingCalendar
);
242 mExistingAlarmTypes
|= creator
->alarmType();
243 mCalendarsPending
<< creator
;
244 creator
->createAgent(agentType
, this);
248 // After migrating KResources, create any necessary additional default
249 // Akonadi resources.
250 createDefaultResources();
254 /******************************************************************************
255 * Called when a collection fetch job has completed.
256 * Finds which mime types are handled by the existing collection.
258 void CalendarMigrator::collectionFetchResult(KJob
* j
)
260 CollectionFetchJob
* job
= static_cast<CollectionFetchJob
*>(j
);
261 const QString id
= job
->fetchScope().resource();
263 qCCritical(KALARM_LOG
) << "CollectionFetchJob" << id
<< "error: " << j
->errorString();
266 const Collection::List collections
= job
->collections();
267 if (collections
.isEmpty())
268 qCCritical(KALARM_LOG
) << "No collections found for resource" << id
;
270 mExistingAlarmTypes
|= CalEvent::types(collections
[0].contentMimeTypes());
272 mFetchesPending
.removeAll(job
);
274 if (mFetchesPending
.isEmpty())
276 // The alarm types of all collections have been found, so now
277 // create any necessary default Akonadi resources.
278 createDefaultResources();
282 /******************************************************************************
283 * Create default Akonadi resources for any alarm types not covered by existing
284 * resources. Normally, this occurs on the first run of KAlarm, but if resources
285 * have been deleted, it could occur on later runs.
286 * If the default calendar files already exist, they will be used; otherwise
287 * they will be created.
289 void CalendarMigrator::createDefaultResources()
292 CalendarCreator
* creator
;
293 if (!(mExistingAlarmTypes
& CalEvent::ACTIVE
))
295 creator
= new CalendarCreator(CalEvent::ACTIVE
, QStringLiteral("calendar.ics"), i18nc("@info", "Active Alarms"));
296 connect(creator
, &CalendarCreator::finished
, this, &CalendarMigrator::calendarCreated
);
297 connect(creator
, &CalendarCreator::creating
, this, &CalendarMigrator::creatingCalendar
);
298 mCalendarsPending
<< creator
;
299 creator
->createAgent(KALARM_RESOURCE
, this);
301 if (!(mExistingAlarmTypes
& CalEvent::ARCHIVED
))
303 creator
= new CalendarCreator(CalEvent::ARCHIVED
, QStringLiteral("expired.ics"), i18nc("@info", "Archived Alarms"));
304 connect(creator
, &CalendarCreator::finished
, this, &CalendarMigrator::calendarCreated
);
305 connect(creator
, &CalendarCreator::creating
, this, &CalendarMigrator::creatingCalendar
);
306 mCalendarsPending
<< creator
;
307 creator
->createAgent(KALARM_RESOURCE
, this);
309 if (!(mExistingAlarmTypes
& CalEvent::TEMPLATE
))
311 creator
= new CalendarCreator(CalEvent::TEMPLATE
, QStringLiteral("template.ics"), i18nc("@info", "Alarm Templates"));
312 connect(creator
, &CalendarCreator::finished
, this, &CalendarMigrator::calendarCreated
);
313 connect(creator
, &CalendarCreator::creating
, this, &CalendarMigrator::creatingCalendar
);
314 mCalendarsPending
<< creator
;
315 creator
->createAgent(KALARM_RESOURCE
, this);
318 if (mCalendarsPending
.isEmpty())
325 /******************************************************************************
326 * Called when a calendar resource is about to be created.
327 * Emits the 'creating' signal.
329 void CalendarMigrator::creatingCalendar(const QString
& path
)
331 Q_EMIT
creating(path
, -1, false);
334 /******************************************************************************
335 * Called when creation of a migrated or new default calendar resource has
336 * completed or failed.
338 void CalendarMigrator::calendarCreated(CalendarCreator
* creator
)
340 int i
= mCalendarsPending
.indexOf(creator
);
342 return; // calendar already finished
344 Q_EMIT
creating(creator
->path(), creator
->collectionId(), true);
346 if (!creator
->errorMessage().isEmpty())
348 QString errmsg
= creator
->newCalendar()
349 ? xi18nc("@info/plain", "Failed to create default calendar <resource>%1</resource>", creator
->resourceName())
350 : xi18nc("@info/plain 'Import Alarms' is the name of a menu option",
351 "Failed to convert old configuration for calendar <resource>%1</resource>. "
352 "Please use Import Alarms to load its alarms into a new or existing calendar.", creator
->resourceName());
353 const QString locn
= i18nc("@info File path or URL", "Location: %1", creator
->path());
354 if (creator
->errorMessage().isEmpty())
355 errmsg
= xi18nc("@info", "<para>%1</para><para>%2</para>", errmsg
, locn
);
357 errmsg
= xi18nc("@info", "<para>%1</para><para>%2<nl/>(%3)</para>", errmsg
, locn
, creator
->errorMessage());
358 KAMessageBox::error(MainWindow::mainMainWindow(), errmsg
);
360 creator
->deleteLater();
362 mCalendarsPending
.removeAt(i
); // remove it from the pending list
363 if (mCalendarsPending
.isEmpty())
370 /******************************************************************************
371 * If an existing Akonadi resource calendar can be converted to the current
372 * KAlarm format, prompt the user whether to convert it, and if yes, tell the
373 * Akonadi resource to update the backend storage to the current format.
374 * The CollectionAttribute's KeepFormat property will be updated if the user
375 * chooses not to update the calendar.
377 * Note: the collection should be up to date: use AkonadiModel::refresh() before
378 * calling this function.
380 void CalendarMigrator::updateToCurrentFormat(const Collection
& collection
, bool ignoreKeepFormat
, QWidget
* parent
)
382 qCDebug(KALARM_LOG
) << collection
.id();
383 if (CalendarUpdater::containsCollection(collection
.id()))
384 return; // prevent multiple simultaneous user prompts
385 const AgentInstance agent
= AgentManager::self()->instance(collection
.resource());
386 const QString id
= agent
.type().identifier();
388 if (id
== KALARM_RESOURCE
)
390 else if (id
== KALARM_DIR_RESOURCE
)
394 qCCritical(KALARM_LOG
) << "Invalid agent type" << id
;
397 CalendarUpdater
* updater
= new CalendarUpdater(collection
, dirResource
, ignoreKeepFormat
, false, parent
);
398 QTimer::singleShot(0, updater
, &CalendarUpdater::update
);
402 QList
<CalendarUpdater
*> CalendarUpdater::mInstances
;
404 CalendarUpdater::CalendarUpdater(const Collection
& collection
, bool dirResource
,
405 bool ignoreKeepFormat
, bool newCollection
, QObject
* parent
)
406 : mCollection(collection
),
408 mDirResource(dirResource
),
409 mIgnoreKeepFormat(ignoreKeepFormat
),
410 mNewCollection(newCollection
),
411 mDuplicate(containsCollection(collection
.id()))
413 mInstances
.append(this);
416 CalendarUpdater::~CalendarUpdater()
418 mInstances
.removeAll(this);
421 bool CalendarUpdater::containsCollection(Collection::Id id
)
423 for (int i
= 0, count
= mInstances
.count(); i
< count
; ++i
)
425 if (mInstances
[i
]->mCollection
.id() == id
)
431 bool CalendarUpdater::update()
433 qCDebug(KALARM_LOG
) << mCollection
.id() << (mDirResource
? "directory" : "file");
435 if (!mDuplicate
// prevent concurrent updates
436 && mCollection
.hasAttribute
<CompatibilityAttribute
>()) // must know format to update
438 const CompatibilityAttribute
* compatAttr
= mCollection
.attribute
<CompatibilityAttribute
>();
439 const KACalendar::Compat compatibility
= compatAttr
->compatibility();
440 if ((compatibility
& ~KACalendar::Converted
)
441 // The calendar isn't in the current KAlarm format
442 && !(compatibility
& ~(KACalendar::Convertible
| KACalendar::Converted
)))
444 // The calendar format is convertible to the current KAlarm format
445 if (!mIgnoreKeepFormat
446 && mCollection
.hasAttribute
<CollectionAttribute
>()
447 && mCollection
.attribute
<CollectionAttribute
>()->keepFormat())
448 qCDebug(KALARM_LOG
) << "Not updating format (previous user choice)";
451 // The user hasn't previously said not to convert it
452 const QString versionString
= KAlarmCal::getVersionString(compatAttr
->version());
453 const QString msg
= KAlarm::conversionPrompt(mCollection
.name(), versionString
, false);
454 qCDebug(KALARM_LOG
) << "Version" << versionString
;
455 if (KAMessageBox::warningYesNo(qobject_cast
<QWidget
*>(mParent
), msg
) != KMessageBox::Yes
)
456 result
= false; // the user chose not to update the calendar
459 // Tell the resource to update the backend storage format
463 // Refetch the collection's details because anything could
464 // have happened since the prompt was first displayed.
465 if (!AkonadiModel::instance()->refresh(mCollection
))
466 errmsg
= i18nc("@info", "Invalid collection");
468 if (errmsg
.isEmpty())
470 const AgentInstance agent
= AgentManager::self()->instance(mCollection
.resource());
472 CalendarMigrator::updateStorageFormat
<OrgKdeAkonadiKAlarmDirSettingsInterface
>(agent
, errmsg
, mParent
);
474 CalendarMigrator::updateStorageFormat
<OrgKdeAkonadiKAlarmSettingsInterface
>(agent
, errmsg
, mParent
);
476 if (!errmsg
.isEmpty())
478 KAMessageBox::error(MainWindow::mainMainWindow(),
479 xi18nc("@info", "%1<nl/>(%2)",
480 xi18nc("@info/plain", "Failed to update format of calendar <resource>%1</resource>", mCollection
.name()),
486 // Record the user's choice of whether to update the calendar
487 const QModelIndex ix
= AkonadiModel::instance()->collectionIndex(mCollection
);
488 AkonadiModel::instance()->setData(ix
, !result
, AkonadiModel::KeepFormatRole
);
497 /******************************************************************************
498 * Tell an Akonadi resource to update the backend storage format to the current
500 * Reply = true if success; if false, 'errorMessage' contains the error message.
502 template <class Interface
> bool CalendarMigrator::updateStorageFormat(const AgentInstance
& agent
, QString
& errorMessage
, QObject
* parent
)
505 Interface
* iface
= getAgentInterface
<Interface
>(agent
, errorMessage
, parent
);
508 qCDebug(KALARM_LOG
) << errorMessage
;
511 iface
->setUpdateStorageFormat(true);
514 qCDebug(KALARM_LOG
) << "true";
518 /******************************************************************************
519 * Create a D-Bus interface to an Akonadi resource.
520 * Reply = interface if success
521 * = 0 if error: 'errorMessage' contains the error message.
523 template <class Interface
> Interface
* CalendarMigrator::getAgentInterface(const AgentInstance
& agent
, QString
& errorMessage
, QObject
* parent
)
525 Interface
* iface
= new Interface(QStringLiteral("org.freedesktop.Akonadi.Resource.") + agent
.identifier(),
526 QStringLiteral("/Settings"), QDBusConnection::sessionBus(), parent
);
527 if (!iface
->isValid())
529 errorMessage
= iface
->lastError().message();
530 qCDebug(KALARM_LOG
) << "D-Bus error accessing resource:" << errorMessage
;
538 /******************************************************************************
539 * Constructor to migrate a KResources calendar, using its parameters.
541 CalendarCreator::CalendarCreator(const QString
& resourceType
, const KConfigGroup
& config
)
542 : mAlarmType(CalEvent::EMPTY
),
546 // Read the resource configuration parameters from the config
547 const char* pathKey
= Q_NULLPTR
;
548 if (resourceType
== QStringLiteral("file"))
550 mResourceType
= LocalFile
;
551 pathKey
= "CalendarURL";
553 else if (resourceType
== QStringLiteral("dir"))
555 mResourceType
= LocalDir
;
556 pathKey
= "CalendarURL";
558 else if (resourceType
== QStringLiteral("remote"))
560 mResourceType
= RemoteFile
;
561 pathKey
= "DownloadUrl";
565 qCCritical(KALARM_LOG
) << "Invalid resource type:" << resourceType
;
568 mPath
= config
.readPathEntry(pathKey
, QStringLiteral(""));
569 switch (config
.readEntry("AlarmType", (int)0))
571 case 1: mAlarmType
= CalEvent::ACTIVE
; break;
572 case 2: mAlarmType
= CalEvent::ARCHIVED
; break;
573 case 4: mAlarmType
= CalEvent::TEMPLATE
; break;
575 qCCritical(KALARM_LOG
) << "Invalid alarm type for resource";
578 mName
= config
.readEntry("ResourceName", QString());
579 mColour
= config
.readEntry("Color", QColor());
580 mReadOnly
= config
.readEntry("ResourceIsReadOnly", true);
581 mEnabled
= config
.readEntry("ResourceIsActive", false);
582 mStandard
= config
.readEntry("Standard", false);
583 qCDebug(KALARM_LOG
) << "Migrating:" << mName
<< ", type=" << mAlarmType
<< ", path=" << mPath
;
586 /******************************************************************************
587 * Constructor to create a new default local file resource.
588 * This is created as enabled, read-write, and standard for its alarm type.
590 CalendarCreator::CalendarCreator(CalEvent::Type alarmType
, const QString
& file
, const QString
& name
)
591 : mAlarmType(alarmType
),
592 mResourceType(LocalFile
),
601 mPath
= QStandardPaths::writableLocation(QStandardPaths::DataLocation
) + QLatin1Char('/') + file
;
602 qCDebug(KALARM_LOG
) << "New:" << mName
<< ", type=" << mAlarmType
<< ", path=" << mPath
;
605 /******************************************************************************
606 * Create the Akonadi agent for this calendar.
608 void CalendarCreator::createAgent(const QString
& agentType
, QObject
* parent
)
610 Q_EMIT
creating(mPath
);
611 AgentInstanceCreateJob
* job
= new AgentInstanceCreateJob(agentType
, parent
);
612 connect(job
, &KJob::result
, this, &CalendarCreator::agentCreated
);
616 /******************************************************************************
617 * Called when the agent creation job for this resource has completed.
618 * Applies the calendar resource configuration to the Akonadi agent.
620 void CalendarCreator::agentCreated(KJob
* j
)
624 mErrorMessage
= j
->errorString();
625 qCCritical(KALARM_LOG
) << "AgentInstanceCreateJob error:" << mErrorMessage
;
630 // Configure the Akonadi Agent
631 qCDebug(KALARM_LOG
) << mName
;
632 AgentInstanceCreateJob
* job
= static_cast<AgentInstanceCreateJob
*>(j
);
633 mAgent
= job
->instance();
634 mAgent
.setName(mName
);
636 switch (mResourceType
)
639 ok
= writeLocalFileConfig();
642 ok
= writeLocalDirectoryConfig();
645 ok
= writeRemoteFileConfig();
648 qCCritical(KALARM_LOG
) << "Invalid resource type";
656 mAgent
.reconfigure(); // notify the agent that its configuration has been changed
658 // Wait for the resource to create its collection.
659 ResourceSynchronizationJob
* sjob
= new ResourceSynchronizationJob(mAgent
);
660 connect(sjob
, &KJob::result
, this, &CalendarCreator::resourceSynchronised
);
661 sjob
->start(); // this is required (not an Akonadi::Job)
664 /******************************************************************************
665 * Called when a resource synchronization job has completed.
666 * Fetches the collection which this agent manages.
668 void CalendarCreator::resourceSynchronised(KJob
* j
)
670 qCDebug(KALARM_LOG
) << mName
;
673 // Don't give up on error - we can still try to fetch the collection
674 qCCritical(KALARM_LOG
) << "ResourceSynchronizationJob error: " << j
->errorString();
676 mCollectionFetchRetryCount
= 0;
680 /******************************************************************************
681 * Find the collection which this agent manages.
683 void CalendarCreator::fetchCollection()
685 CollectionFetchJob
* job
= new CollectionFetchJob(Collection::root(), CollectionFetchJob::FirstLevel
);
686 job
->fetchScope().setResource(mAgent
.identifier());
687 connect(job
, &KJob::result
, this, &CalendarCreator::collectionFetchResult
);
691 bool CalendarCreator::writeLocalFileConfig()
693 OrgKdeAkonadiKAlarmSettingsInterface
* iface
= writeBasicConfig
<OrgKdeAkonadiKAlarmSettingsInterface
>();
696 iface
->setMonitorFile(true);
697 iface
->save(); // save the Agent config changes
702 bool CalendarCreator::writeLocalDirectoryConfig()
704 OrgKdeAkonadiKAlarmDirSettingsInterface
* iface
= writeBasicConfig
<OrgKdeAkonadiKAlarmDirSettingsInterface
>();
707 iface
->setMonitorFiles(true);
708 iface
->save(); // save the Agent config changes
713 bool CalendarCreator::writeRemoteFileConfig()
715 OrgKdeAkonadiKAlarmSettingsInterface
* iface
= writeBasicConfig
<OrgKdeAkonadiKAlarmSettingsInterface
>();
718 iface
->setMonitorFile(true);
719 iface
->save(); // save the Agent config changes
724 template <class Interface
> Interface
* CalendarCreator::writeBasicConfig()
726 Interface
* iface
= CalendarMigrator::getAgentInterface
<Interface
>(mAgent
, mErrorMessage
, this);
729 iface
->setReadOnly(mReadOnly
);
730 iface
->setDisplayName(mName
);
731 iface
->setPath(mPath
);
732 iface
->setAlarmTypes(CalEvent::mimeTypes(mAlarmType
));
733 iface
->setUpdateStorageFormat(false);
738 /******************************************************************************
739 * Called when a collection fetch job has completed.
740 * Obtains the collection handled by the agent, and configures it.
742 void CalendarCreator::collectionFetchResult(KJob
* j
)
744 qCDebug(KALARM_LOG
) << mName
;
747 mErrorMessage
= j
->errorString();
748 qCCritical(KALARM_LOG
) << "CollectionFetchJob error: " << mErrorMessage
;
752 CollectionFetchJob
* job
= static_cast<CollectionFetchJob
*>(j
);
753 const Collection::List collections
= job
->collections();
754 if (collections
.isEmpty())
756 if (++mCollectionFetchRetryCount
>= 10)
758 mErrorMessage
= i18nc("@info", "New configuration timed out");
759 qCCritical(KALARM_LOG
) << "Timeout fetching collection for resource";
763 // Need to wait a bit longer until the resource has initialised and
764 // created its collection. Retry after 200ms.
765 qCDebug(KALARM_LOG
) << "Retrying";
766 QTimer::singleShot(200, this, &CalendarCreator::fetchCollection
);
769 if (collections
.count() > 1)
771 mErrorMessage
= i18nc("@info", "New configuration was corrupt");
772 qCCritical(KALARM_LOG
) << "Wrong number of collections for this resource:" << collections
.count();
777 // Set Akonadi Collection attributes
778 Collection collection
= collections
[0];
779 mCollectionId
= collection
.id();
780 collection
.setContentMimeTypes(CalEvent::mimeTypes(mAlarmType
));
781 EntityDisplayAttribute
* dattr
= collection
.attribute
<EntityDisplayAttribute
>(Collection::AddIfMissing
);
782 dattr
->setIconName(QStringLiteral("kalarm"));
783 CollectionAttribute
* attr
= collection
.attribute
<CollectionAttribute
>(Collection::AddIfMissing
);
784 attr
->setEnabled(mEnabled
? mAlarmType
: CalEvent::EMPTY
);
786 attr
->setStandard(mAlarmType
);
787 if (mColour
.isValid())
788 attr
->setBackgroundColor(mColour
);
790 // Update the calendar to the current KAlarm format if necessary,
791 // and if the user agrees.
792 bool dirResource
= false;
793 switch (mResourceType
)
802 Q_ASSERT(0); // Invalid resource type
806 bool duplicate
= false;
809 CalendarUpdater
* updater
= new CalendarUpdater(collection
, dirResource
, false, true, this);
810 duplicate
= updater
->isDuplicate();
811 keep
= !updater
->update(); // note that 'updater' will auto-delete when finished
815 // Record the user's choice of whether to update the calendar
816 attr
->setKeepFormat(keep
);
819 // Update the collection's CollectionAttribute value in the Akonadi database.
820 // Note that we can't supply 'collection' to CollectionModifyJob since
821 // that also contains the CompatibilityAttribute value, which is read-only
822 // for applications. So create a new Collection instance and only set a
823 // value for CollectionAttribute.
824 Collection
c(collection
.id());
825 CollectionAttribute
* att
= c
.attribute
<CollectionAttribute
>(Collection::AddIfMissing
);
827 CollectionModifyJob
* cmjob
= new CollectionModifyJob(c
, this);
828 connect(cmjob
, &KJob::result
, this, &CalendarCreator::modifyCollectionJobDone
);
831 /******************************************************************************
832 * Called when a collection modification job has completed.
833 * Checks for any error.
835 void CalendarCreator::modifyCollectionJobDone(KJob
* j
)
837 Collection collection
= static_cast<CollectionModifyJob
*>(j
)->collection();
840 mErrorMessage
= j
->errorString();
841 qCCritical(KALARM_LOG
) << "CollectionFetchJob error: " << mErrorMessage
;
846 qCDebug(KALARM_LOG
) << "Completed:" << mName
;
851 /******************************************************************************
852 * Emit the finished() signal. If 'cleanup' is true, delete the newly created
853 * but incomplete Agent.
855 void CalendarCreator::finish(bool cleanup
)
860 AgentManager::self()->removeInstance(mAgent
);
862 Q_EMIT
finished(this);
866 #include "calendarmigrator.moc"