24 #include "kalarmsettings.h"
25 #include "kalarmdirsettings.h"
29 #include <kalarmcal/collectionattribute.h>
30 #include <kalarmcal/compatibilityattribute.h>
31 #include <kalarmcal/version.h>
33 #include <akonadi/agentinstancecreatejob.h>
34 #include <akonadi/agentmanager.h>
35 #include <akonadi/collectionfetchjob.h>
36 #include <akonadi/collectionfetchscope.h>
37 #include <akonadi/collectionmodifyjob.h>
38 #include <akonadi/entitydisplayattribute.h>
39 #include <akonadi/resourcesynchronizationjob.h>
42 #include <kconfiggroup.h>
43 #include <kstandarddirs.h>
48 using namespace Akonadi;
49 using namespace KAlarmCal;
53 class CalendarCreator :
public QObject
58 CalendarCreator(
const QString& resourceType,
const KConfigGroup&);
60 CalendarCreator(CalEvent::Type,
const QString& file,
const QString& name);
61 bool isValid()
const {
return mAlarmType != CalEvent::EMPTY; }
62 CalEvent::Type alarmType()
const {
return mAlarmType; }
63 bool newCalendar()
const {
return mNew; }
64 QString resourceName()
const {
return mName; }
65 Collection::Id collectionId()
const {
return mCollectionId; }
66 QString path()
const {
return mPath; }
67 QString errorMessage()
const {
return mErrorMessage; }
68 void createAgent(
const QString& agentType,
QObject* parent);
71 void agentCreated(
KJob*);
74 void creating(
const QString& path);
75 void finished(CalendarCreator*);
78 void fetchCollection();
79 void collectionFetchResult(
KJob*);
80 void resourceSynchronised(
KJob*);
81 void modifyCollectionJobDone(
KJob*);
84 void finish(
bool cleanup);
85 bool writeLocalFileConfig();
86 bool writeLocalDirectoryConfig();
87 bool writeRemoteFileConfig();
88 template <
class Interface> Interface* writeBasicConfig();
90 enum ResourceType { LocalFile, LocalDir, RemoteFile };
93 CalEvent::Type mAlarmType;
94 ResourceType mResourceType;
98 QString mErrorMessage;
99 Collection::Id mCollectionId;
100 int mCollectionFetchRetryCount;
109 class CalendarUpdater :
public QObject
113 CalendarUpdater(
const Collection& collection,
bool dirResource,
114 bool ignoreKeepFormat,
bool newCollection,
QObject* parent);
117 bool isDuplicate()
const {
return mDuplicate; }
119 static bool containsCollection(Collection::Id);
126 Akonadi::Collection mCollection;
128 const bool mDirResource;
129 const bool mIgnoreKeepFormat;
130 const bool mNewCollection;
131 const bool mDuplicate;
136 bool CalendarMigrator::mCompleted =
false;
138 CalendarMigrator::CalendarMigrator(
QObject* parent)
140 mExistingAlarmTypes(0)
155 if (!mInstance && !mCompleted)
171 void CalendarMigrator::migrateOrCreate()
177 AgentInstance::List agents = AgentManager::self()->instances();
178 foreach (
const AgentInstance& agent, agents)
180 QString type = agent.type().identifier();
181 if (type == QLatin1String(
"akonadi_kalarm_resource")
182 || type == QLatin1String(
"akonadi_kalarm_dir_resource"))
185 CollectionFetchJob* job =
new CollectionFetchJob(Collection::root(), CollectionFetchJob::FirstLevel);
186 job->fetchScope().setResource(agent.identifier());
187 mFetchesPending << job;
188 connect(job, SIGNAL(result(
KJob*)), SLOT(collectionFetchResult(
KJob*)));
194 if (mFetchesPending.isEmpty())
198 const QString configFile = KStandardDirs::locateLocal(
"config", QLatin1String(
"kresources/alarms/stdrc"));
199 KConfig config(configFile, KConfig::SimpleConfig);
202 KConfigGroup group = config.group(
"General");
203 QStringList keys = group.readEntry(
"ResourceKeys", QStringList())
204 + group.readEntry(
"PassiveResourceKeys", QStringList());
207 CalendarCreator* creator;
208 foreach (
const QString&
id, keys)
210 KConfigGroup configGroup = config.group(QLatin1String(
"Resource_") +
id);
211 QString resourceType = configGroup.readEntry(
"ResourceType", QString());
213 if (resourceType == QLatin1String(
"file"))
214 agentType = QLatin1String(
"akonadi_kalarm_resource");
215 else if (resourceType == QLatin1String(
"dir"))
216 agentType = QLatin1String(
"akonadi_kalarm_dir_resource");
217 else if (resourceType == QLatin1String(
"remote"))
218 agentType = QLatin1String(
"akonadi_kalarm_resource");
222 creator =
new CalendarCreator(resourceType, configGroup);
223 if (!creator->isValid())
227 connect(creator, SIGNAL(finished(CalendarCreator*)), SLOT(calendarCreated(CalendarCreator*)));
228 connect(creator, SIGNAL(
creating(QString)), SLOT(creatingCalendar(QString)));
229 mExistingAlarmTypes |= creator->alarmType();
230 mCalendarsPending << creator;
231 creator->createAgent(agentType,
this);
237 createDefaultResources();
245 void CalendarMigrator::collectionFetchResult(
KJob* j)
247 CollectionFetchJob* job =
static_cast<CollectionFetchJob*
>(j);
248 QString
id = job->fetchScope().resource();
250 kError() <<
"CollectionFetchJob" <<
id <<
"error: " << j->errorString();
253 Collection::List collections = job->collections();
254 if (collections.isEmpty())
255 kError() <<
"No collections found for resource" << id;
257 mExistingAlarmTypes |= CalEvent::types(collections[0].contentMimeTypes());
259 mFetchesPending.removeAll(job);
261 if (mFetchesPending.isEmpty())
265 createDefaultResources();
276 void CalendarMigrator::createDefaultResources()
279 CalendarCreator* creator;
280 if (!(mExistingAlarmTypes & CalEvent::ACTIVE))
282 creator =
new CalendarCreator(CalEvent::ACTIVE, QLatin1String(
"calendar.ics"), i18nc(
"@info/plain",
"Active Alarms"));
283 connect(creator, SIGNAL(finished(CalendarCreator*)), SLOT(calendarCreated(CalendarCreator*)));
284 connect(creator, SIGNAL(
creating(QString)), SLOT(creatingCalendar(QString)));
285 mCalendarsPending << creator;
286 creator->createAgent(QLatin1String(
"akonadi_kalarm_resource"),
this);
288 if (!(mExistingAlarmTypes & CalEvent::ARCHIVED))
290 creator =
new CalendarCreator(CalEvent::ARCHIVED, QLatin1String(
"expired.ics"), i18nc(
"@info/plain",
"Archived Alarms"));
291 connect(creator, SIGNAL(finished(CalendarCreator*)), SLOT(calendarCreated(CalendarCreator*)));
292 connect(creator, SIGNAL(
creating(QString)), SLOT(creatingCalendar(QString)));
293 mCalendarsPending << creator;
294 creator->createAgent(QLatin1String(
"akonadi_kalarm_resource"),
this);
296 if (!(mExistingAlarmTypes & CalEvent::TEMPLATE))
298 creator =
new CalendarCreator(CalEvent::TEMPLATE, QLatin1String(
"template.ics"), i18nc(
"@info/plain",
"Alarm Templates"));
299 connect(creator, SIGNAL(finished(CalendarCreator*)), SLOT(calendarCreated(CalendarCreator*)));
300 connect(creator, SIGNAL(
creating(QString)), SLOT(creatingCalendar(QString)));
301 mCalendarsPending << creator;
302 creator->createAgent(QLatin1String(
"akonadi_kalarm_resource"),
this);
305 if (mCalendarsPending.isEmpty())
316 void CalendarMigrator::creatingCalendar(
const QString& path)
325 void CalendarMigrator::calendarCreated(CalendarCreator* creator)
327 int i = mCalendarsPending.indexOf(creator);
331 emit
creating(creator->path(), creator->collectionId(),
true);
333 if (!creator->errorMessage().isEmpty())
335 QString errmsg = creator->newCalendar()
336 ? i18nc(
"@info/plain",
"Failed to create default calendar <resource>%1</resource>", creator->resourceName())
337 : i18nc(
"@info/plain 'Import Alarms' is the name of a menu option",
338 "Failed to convert old configuration for calendar <resource>%1</resource>. "
339 "Please use Import Alarms to load its alarms into a new or existing calendar.", creator->resourceName());
340 QString locn = i18nc(
"@info/plain File path or URL",
"Location: %1", creator->path());
341 if (creator->errorMessage().isEmpty())
342 errmsg = i18nc(
"@info",
"<para>%1</para><para>%2</para>", errmsg, locn);
344 errmsg = i18nc(
"@info",
"<para>%1</para><para>%2<nl/>(%3)</para>", errmsg, locn, creator->errorMessage());
347 creator->deleteLater();
349 mCalendarsPending.removeAt(i);
350 if (mCalendarsPending.isEmpty())
369 kDebug() << collection.id();
370 if (CalendarUpdater::containsCollection(collection.id()))
372 AgentInstance agent = AgentManager::self()->instance(collection.resource());
373 const QString
id = agent.type().identifier();
375 if (
id == QLatin1String(
"akonadi_kalarm_resource"))
377 else if (
id == QLatin1String(
"akonadi_kalarm_dir_resource"))
381 kError() <<
"Invalid agent type" << id;
385 QTimer::singleShot(0, updater, SLOT(update()));
391 CalendarUpdater::CalendarUpdater(
const Collection& collection,
bool dirResource,
392 bool ignoreKeepFormat,
bool newCollection,
QObject* parent)
393 : mCollection(collection),
395 mDirResource(dirResource),
396 mIgnoreKeepFormat(ignoreKeepFormat),
397 mNewCollection(newCollection),
398 mDuplicate(containsCollection(collection.id()))
400 mInstances.append(
this);
403 CalendarUpdater::~CalendarUpdater()
405 mInstances.removeAll(
this);
408 bool CalendarUpdater::containsCollection(Collection::Id
id)
410 for (
int i = 0, count = mInstances.count(); i < count; ++i)
412 if (mInstances[i]->mCollection.id() == id)
418 bool CalendarUpdater::update()
420 kDebug() << mCollection.id() << (mDirResource ?
"directory" :
"file");
423 && mCollection.hasAttribute<CompatibilityAttribute>())
425 const CompatibilityAttribute* compatAttr = mCollection.attribute<CompatibilityAttribute>();
426 KACalendar::Compat compatibility = compatAttr->compatibility();
427 if ((compatibility & ~KACalendar::Converted)
429 && !(compatibility & ~(KACalendar::Convertible | KACalendar::Converted)))
432 if (!mIgnoreKeepFormat
433 && mCollection.hasAttribute<CollectionAttribute>()
434 && mCollection.attribute<CollectionAttribute>()->keepFormat())
435 kDebug() <<
"Not updating format (previous user choice)";
439 QString versionString = KAlarmCal::getVersionString(compatAttr->version());
440 QString msg = KAlarm::conversionPrompt(mCollection.name(), versionString,
false);
441 kDebug() <<
"Version" << versionString;
453 errmsg = i18nc(
"@info/plain",
"Invalid collection");
455 if (errmsg.isEmpty())
457 AgentInstance agent = AgentManager::self()->instance(mCollection.resource());
459 CalendarMigrator::updateStorageFormat<OrgKdeAkonadiKAlarmDirSettingsInterface>(agent, errmsg, mParent);
461 CalendarMigrator::updateStorageFormat<OrgKdeAkonadiKAlarmSettingsInterface>(agent, errmsg, mParent);
463 if (!errmsg.isEmpty())
466 i18nc(
"@info",
"%1<nl/>(%2)",
467 i18nc(
"@info/plain",
"Failed to update format of calendar <resource>%1</resource>", mCollection.name()),
489 template <
class Interface>
bool CalendarMigrator::updateStorageFormat(
const AgentInstance& agent, QString& errorMessage,
QObject* parent)
492 Interface* iface = getAgentInterface<Interface>(agent, errorMessage, parent);
495 kDebug() << errorMessage;
498 iface->setUpdateStorageFormat(
true);
499 iface->writeConfig();
512 Interface* iface =
new Interface(QLatin1String(
"org.freedesktop.Akonadi.Resource.") + agent.identifier(),
513 QLatin1String(
"/Settings"), QDBusConnection::sessionBus(), parent);
514 if (!iface->isValid())
516 errorMessage = iface->lastError().message();
517 kDebug() <<
"D-Bus error accessing resource:" << errorMessage;
528 CalendarCreator::CalendarCreator(
const QString& resourceType,
const KConfigGroup& config)
529 : mAlarmType(CalEvent::EMPTY),
534 const char* pathKey = 0;
535 if (resourceType == QLatin1String(
"file"))
537 mResourceType = LocalFile;
538 pathKey =
"CalendarURL";
540 else if (resourceType == QLatin1String(
"dir"))
542 mResourceType = LocalDir;
543 pathKey =
"CalendarURL";
545 else if (resourceType == QLatin1String(
"remote"))
547 mResourceType = RemoteFile;
548 pathKey =
"DownloadUrl";
552 kError() <<
"Invalid resource type:" << resourceType;
555 mPath = config.readPathEntry(pathKey, QLatin1String(
""));
556 switch (config.readEntry(
"AlarmType", (
int)0))
558 case 1: mAlarmType = CalEvent::ACTIVE;
break;
559 case 2: mAlarmType = CalEvent::ARCHIVED;
break;
560 case 4: mAlarmType = CalEvent::TEMPLATE;
break;
562 kError() <<
"Invalid alarm type for resource";
565 mName = config.readEntry(
"ResourceName", QString());
566 mColour = config.readEntry(
"Color", QColor());
567 mReadOnly = config.readEntry(
"ResourceIsReadOnly",
true);
568 mEnabled = config.readEntry(
"ResourceIsActive",
false);
569 mStandard = config.readEntry(
"Standard",
false);
570 kDebug() <<
"Migrating:" << mName <<
", type=" << mAlarmType <<
", path=" << mPath;
577 CalendarCreator::CalendarCreator(CalEvent::Type alarmType,
const QString& file,
const QString& name)
578 : mAlarmType(alarmType),
579 mResourceType(LocalFile),
588 mPath = KStandardDirs::locateLocal(
"appdata", file);
589 kDebug() <<
"New:" << mName <<
", type=" << mAlarmType <<
", path=" << mPath;
595 void CalendarCreator::createAgent(
const QString& agentType,
QObject* parent)
597 emit creating(mPath);
598 AgentInstanceCreateJob* job =
new AgentInstanceCreateJob(agentType, parent);
599 connect(job, SIGNAL(result(
KJob*)), SLOT(agentCreated(
KJob*)));
607 void CalendarCreator::agentCreated(
KJob* j)
611 mErrorMessage = j->errorString();
612 kError() <<
"AgentInstanceCreateJob error:" << mErrorMessage;
619 AgentInstanceCreateJob* job =
static_cast<AgentInstanceCreateJob*
>(j);
620 mAgent = job->instance();
621 mAgent.setName(mName);
623 switch (mResourceType)
626 ok = writeLocalFileConfig();
629 ok = writeLocalDirectoryConfig();
632 ok = writeRemoteFileConfig();
635 kError() <<
"Invalid resource type";
643 mAgent.reconfigure();
646 ResourceSynchronizationJob* sjob =
new ResourceSynchronizationJob(mAgent);
647 connect(sjob, SIGNAL(result(
KJob*)), SLOT(resourceSynchronised(
KJob*)));
655 void CalendarCreator::resourceSynchronised(
KJob* j)
661 kError() <<
"ResourceSynchronizationJob error: " << j->errorString();
663 mCollectionFetchRetryCount = 0;
670 void CalendarCreator::fetchCollection()
672 CollectionFetchJob* job =
new CollectionFetchJob(Collection::root(), CollectionFetchJob::FirstLevel);
673 job->fetchScope().setResource(mAgent.identifier());
674 connect(job, SIGNAL(result(
KJob*)), SLOT(collectionFetchResult(
KJob*)));
678 bool CalendarCreator::writeLocalFileConfig()
680 OrgKdeAkonadiKAlarmSettingsInterface* iface = writeBasicConfig<OrgKdeAkonadiKAlarmSettingsInterface>();
683 iface->setMonitorFile(
true);
684 iface->writeConfig();
689 bool CalendarCreator::writeLocalDirectoryConfig()
691 OrgKdeAkonadiKAlarmDirSettingsInterface* iface = writeBasicConfig<OrgKdeAkonadiKAlarmDirSettingsInterface>();
694 iface->setMonitorFiles(
true);
695 iface->writeConfig();
700 bool CalendarCreator::writeRemoteFileConfig()
702 OrgKdeAkonadiKAlarmSettingsInterface* iface = writeBasicConfig<OrgKdeAkonadiKAlarmSettingsInterface>();
705 iface->setMonitorFile(
true);
706 iface->writeConfig();
711 template <
class Interface> Interface* CalendarCreator::writeBasicConfig()
713 Interface* iface = CalendarMigrator::getAgentInterface<Interface>(mAgent, mErrorMessage,
this);
716 iface->setReadOnly(mReadOnly);
717 iface->setDisplayName(mName);
718 iface->setPath(mPath);
719 iface->setAlarmTypes(CalEvent::mimeTypes(mAlarmType));
720 iface->setUpdateStorageFormat(
false);
729 void CalendarCreator::collectionFetchResult(
KJob* j)
734 mErrorMessage = j->errorString();
735 kError() <<
"CollectionFetchJob error: " << mErrorMessage;
739 CollectionFetchJob* job =
static_cast<CollectionFetchJob*
>(j);
740 Collection::List collections = job->collections();
741 if (collections.isEmpty())
743 if (++mCollectionFetchRetryCount >= 10)
745 mErrorMessage = i18nc(
"@info/plain",
"New configuration timed out");
746 kError() <<
"Timeout fetching collection for resource";
752 kDebug() <<
"Retrying";
753 QTimer::singleShot(200,
this, SLOT(fetchCollection()));
756 if (collections.count() > 1)
758 mErrorMessage = i18nc(
"@info/plain",
"New configuration was corrupt");
759 kError() <<
"Wrong number of collections for this resource:" << collections.count();
765 Collection collection = collections[0];
766 mCollectionId = collection.id();
767 collection.setContentMimeTypes(CalEvent::mimeTypes(mAlarmType));
768 EntityDisplayAttribute* dattr = collection.attribute<EntityDisplayAttribute>(Collection::AddIfMissing);
769 dattr->setIconName(QLatin1String(
"kalarm"));
770 CollectionAttribute* attr = collection.attribute<CollectionAttribute>(Entity::AddIfMissing);
771 attr->setEnabled(mEnabled ? mAlarmType : CalEvent::EMPTY);
773 attr->setStandard(mAlarmType);
774 if (mColour.isValid())
775 attr->setBackgroundColor(mColour);
779 bool dirResource =
false;
780 switch (mResourceType)
793 bool duplicate =
false;
796 CalendarUpdater* updater =
new CalendarUpdater(collection, dirResource,
false,
true,
this);
797 duplicate = updater->isDuplicate();
798 keep = !updater->update();
803 attr->setKeepFormat(keep);
811 Collection c(collection.id());
812 CollectionAttribute* att = c.attribute<CollectionAttribute>(Entity::AddIfMissing);
814 CollectionModifyJob* cmjob =
new CollectionModifyJob(c,
this);
815 connect(cmjob, SIGNAL(result(
KJob*)),
this, SLOT(modifyCollectionJobDone(
KJob*)));
822 void CalendarCreator::modifyCollectionJobDone(
KJob* j)
824 Collection collection =
static_cast<CollectionModifyJob*
>(j)->collection();
827 mErrorMessage = j->errorString();
828 kError() <<
"CollectionFetchJob error: " << mErrorMessage;
833 kDebug() <<
"Completed:" << mName;
842 void CalendarCreator::finish(
bool cleanup)
847 AgentManager::self()->removeInstance(mAgent);
853 #include "calendarmigrator.moc"
854 #include "moc_calendarmigrator.cpp"
void creating(const QString &path, Akonadi::Collection::Id id, bool finished)
Signal emitted when a resource is about to be created, and when creation has completed (successfully ...
static void updateToCurrentFormat(const Akonadi::Collection &, bool ignoreKeepFormat, QWidget *parent)
static void error(QWidget *parent, const QString &text, const QString &caption=QString(), Options options=Options(Notify|WindowModal))
QModelIndex collectionIndex(Akonadi::Collection::Id id) const
static Interface * getAgentInterface(const Akonadi::AgentInstance &, QString &errorMessage, QObject *parent)
static AkonadiModel * instance()
static CalendarMigrator * instance()
friend class CalendarUpdater
static int warningYesNo(QWidget *parent, const QString &text, const QString &caption=QString(), const KGuiItem &buttonYes=KStandardGuiItem::yes(), const KGuiItem &buttonNo=KStandardGuiItem::no(), const QString &dontAskAgainName=QString(), Options options=Options(Notify|Dangerous|WindowModal))
static MainWindow * mainMainWindow()
Class to migrate KResources alarm calendars from pre-Akonadi versions of KAlarm, and to create defaul...
virtual bool setData(const QModelIndex &, const QVariant &value, int role)