Commit 71f86256 authored by David Jarvie's avatar David Jarvie
Browse files

Bug 290050: Ensure that Akonadi resources exist for all alarm types

If no Akonadi resource exists for any alarm type (active, archived,
template) on KAlarm startup, create a default alarm calendar
resource. This ensures that if by error a calendar type is missing,
the user can still operate KAlarm without needing to manuallly
create calendars.

BUG:290050
parent aa35e535
/*
* akonadimodel.cpp - KAlarm calendar file access using Akonadi
* Program: kalarm
* Copyright © 2007-2011 by David Jarvie <djarvie@kde.org>
* Copyright © 2007-2012 by David Jarvie <djarvie@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -136,26 +136,10 @@ AkonadiModel::AkonadiModel(ChangeRecorder* monitor, QObject* parent)
connect(this, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int)));
connect(monitor, SIGNAL(itemChanged(Akonadi::Item,QSet<QByteArray>)), SLOT(slotMonitoredItemChanged(Akonadi::Item,QSet<QByteArray>)));
// Check whether there are any KAlarm resources configured
bool found = false;
AgentInstance::List agents = AgentManager::self()->instances();
foreach (const AgentInstance& agent, agents)
{
QString type = agent.type().identifier();
if (type == QLatin1String("akonadi_kalarm_resource")
|| type == QLatin1String("akonadi_kalarm_dir_resource"))
{
found = true;
break;
}
}
if (!found)
{
// There are no KAlarm Akonadi resources.
// Migrate any KResources alarm calendars from pre-Akonadi versions of KAlarm,
// or create default calendars.
CalendarMigrator::execute();
}
// If necessary, migrate any KResources alarm calendars from pre-Akonadi
// versions of KAlarm, or create default Akonadi calendar resources if any
// are missing.
CalendarMigrator::execute();
}
/******************************************************************************
......
/*
* calendarmigrator.cpp - migrates or creates KAlarm Akonadi resources
* Program: kalarm
* Copyright © 2011 by David Jarvie <djarvie@kde.org>
* Copyright © 2011-2012 by David Jarvie <djarvie@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -54,14 +54,17 @@ class CalendarCreator : public QObject
{
Q_OBJECT
public:
// Constructor to migrate a calendar from KResources.
CalendarCreator(const QString& resourceType, const KConfigGroup&);
// Constructor to create a default Akonadi calendar.
CalendarCreator(CalEvent::Type, const QString& file, const QString& name);
bool isValid() const { return mAlarmType != CalEvent::EMPTY; }
bool newCalendar() const { return mNew; }
QString resourceName() const { return mName; }
QString path() const { return mPath; }
QString errorMessage() const { return mErrorMessage; }
void createAgent(const QString& agentType, QObject* parent);
bool isValid() const { return mAlarmType != CalEvent::EMPTY; }
CalEvent::Type alarmType() const { return mAlarmType; }
bool newCalendar() const { return mNew; }
QString resourceName() const { return mName; }
QString path() const { return mPath; }
QString errorMessage() const { return mErrorMessage; }
void createAgent(const QString& agentType, QObject* parent);
public slots:
void agentCreated(KJob*);
......@@ -126,7 +129,8 @@ class CalendarUpdater : public QObject
CalendarMigrator* CalendarMigrator::mInstance = 0;
CalendarMigrator::CalendarMigrator(QObject* parent)
: QObject(parent)
: QObject(parent),
mExistingAlarmTypes(0)
{
}
......@@ -154,69 +158,135 @@ void CalendarMigrator::execute()
}
/******************************************************************************
* Migrate old KResource calendars, or if none, create default Akonadi resources.
* Migrate old KResource calendars, and create default Akonadi resources.
*/
void CalendarMigrator::migrateOrCreate()
{
kDebug();
CalendarCreator* creator;
// First migrate any KResources alarm calendars from pre-Akonadi versions of KAlarm.
bool haveResources = false;
const QString configFile = KStandardDirs::locateLocal("config", QLatin1String("kresources/alarms/stdrc"));
KConfig config(configFile, KConfig::SimpleConfig);
// First, check whether any Akonadi resources already exist, and if
// so, find their alarm types.
AgentInstance::List agents = AgentManager::self()->instances();
foreach (const AgentInstance& agent, agents)
{
QString type = agent.type().identifier();
if (type == QLatin1String("akonadi_kalarm_resource")
|| type == QLatin1String("akonadi_kalarm_dir_resource"))
{
// Fetch the resource's collection to determine its alarm types
CollectionFetchJob* job = new CollectionFetchJob(Collection::root(), CollectionFetchJob::FirstLevel);
job->fetchScope().setResource(agent.identifier());
mFetchesPending << job;
connect(job, SIGNAL(result(KJob*)), SLOT(collectionFetchResult(KJob*)));
// Note: Once all collections have been fetched, any missing
// default resources will be created.
}
}
// Fetch all the resource identifiers which are actually in use
KConfigGroup group = config.group("General");
QStringList keys = group.readEntry("ResourceKeys", QStringList())
+ group.readEntry("PassiveResourceKeys", QStringList());
if (mFetchesPending.isEmpty())
{
// There are no Akonadi resources, so migrate any KResources alarm
// calendars from pre-Akonadi versions of KAlarm.
const QString configFile = KStandardDirs::locateLocal("config", QLatin1String("kresources/alarms/stdrc"));
KConfig config(configFile, KConfig::SimpleConfig);
// Fetch all the KResource identifiers which are actually in use
KConfigGroup group = config.group("General");
QStringList keys = group.readEntry("ResourceKeys", QStringList())
+ group.readEntry("PassiveResourceKeys", QStringList());
// Create an Akonadi resource for each KResource id
CalendarCreator* creator;
foreach (const QString& id, keys)
{
KConfigGroup configGroup = config.group(QLatin1String("Resource_") + id);
QString resourceType = configGroup.readEntry("ResourceType", QString());
QString agentType;
if (resourceType == QLatin1String("file"))
agentType = QLatin1String("akonadi_kalarm_resource");
else if (resourceType == QLatin1String("dir"))
agentType = QLatin1String("akonadi_kalarm_dir_resource");
else if (resourceType == QLatin1String("remote"))
agentType = QLatin1String("akonadi_kalarm_resource");
else
continue; // unknown resource type - can't convert
// Create an Akonadi resource for each resource id
foreach (const QString& id, keys)
creator = new CalendarCreator(resourceType, configGroup);
if (!creator->isValid())
delete creator;
else
{
connect(creator, SIGNAL(finished(CalendarCreator*)), SLOT(calendarCreated(CalendarCreator*)));
connect(creator, SIGNAL(creating(QString)), SLOT(creatingCalendar(QString)));
mExistingAlarmTypes |= creator->alarmType();
mCalendarsPending << creator;
creator->createAgent(agentType, this);
}
}
// After migrating KResources, create any necessary additional default
// Akonadi resources.
createDefaultResources();
}
}
/******************************************************************************
* Called when a collection fetch job has completed.
* Finds which mime types are handled by the existing collection.
*/
void CalendarMigrator::collectionFetchResult(KJob* j)
{
CollectionFetchJob* job = static_cast<CollectionFetchJob*>(j);
QString id = job->fetchScope().resource();
if (j->error())
kError() << "CollectionFetchJob" << id << "error: " << j->errorString();
else
{
KConfigGroup configGroup = config.group(QLatin1String("Resource_") + id);
QString resourceType = configGroup.readEntry("ResourceType", QString());
QString agentType;
if (resourceType == QLatin1String("file"))
agentType = QLatin1String("akonadi_kalarm_resource");
else if (resourceType == QLatin1String("dir"))
agentType = QLatin1String("akonadi_kalarm_dir_resource");
else if (resourceType == QLatin1String("remote"))
agentType = QLatin1String("akonadi_kalarm_resource");
Collection::List collections = job->collections();
if (collections.isEmpty())
kError() << "No collections found for resource" << id;
else
continue; // unknown resource type - can't convert
mExistingAlarmTypes |= CalEvent::types(collections[0].contentMimeTypes());
}
mFetchesPending.removeAll(job);
haveResources = true;
creator = new CalendarCreator(resourceType, configGroup);
if (!creator->isValid())
delete creator;
else
{
connect(creator, SIGNAL(finished(CalendarCreator*)), SLOT(calendarCreated(CalendarCreator*)));
connect(creator, SIGNAL(creating(QString)), SLOT(creatingCalendar(QString)));
mCalendarsPending << creator;
creator->createAgent(agentType, this);
}
if (mFetchesPending.isEmpty())
{
// The alarm types of all collections have been found, so now
// create any necessary default Akonadi resources.
createDefaultResources();
}
}
if (!haveResources)
/******************************************************************************
* Create default Akonadi resources for any alarm types not covered by existing
* resources. Normally, this occurs on the first run of KAlarm, but if resources
* have been deleted, it could occur on later runs.
* If the default calendar files already exist, they will be used; otherwise
* they will be created.
*/
void CalendarMigrator::createDefaultResources()
{
kDebug();
CalendarCreator* creator;
if (!(mExistingAlarmTypes & CalEvent::ACTIVE))
{
// There were no KResources calendars to migrate, so create default ones.
// Normally this occurs on first installation of KAlarm.
// If the default files already exist, they will be used; otherwise they
// will be created.
creator = new CalendarCreator(CalEvent::ACTIVE, QLatin1String("calendar.ics"), i18nc("@info/plain", "Active Alarms"));
connect(creator, SIGNAL(finished(CalendarCreator*)), SLOT(calendarCreated(CalendarCreator*)));
connect(creator, SIGNAL(creating(QString)), SLOT(creatingCalendar(QString)));
mCalendarsPending << creator;
creator->createAgent(QLatin1String("akonadi_kalarm_resource"), this);
}
if (!(mExistingAlarmTypes & CalEvent::ARCHIVED))
{
creator = new CalendarCreator(CalEvent::ARCHIVED, QLatin1String("expired.ics"), i18nc("@info/plain", "Archived Alarms"));
connect(creator, SIGNAL(finished(CalendarCreator*)), SLOT(calendarCreated(CalendarCreator*)));
connect(creator, SIGNAL(creating(QString)), SLOT(creatingCalendar(QString)));
mCalendarsPending << creator;
creator->createAgent(QLatin1String("akonadi_kalarm_resource"), this);
}
if (!(mExistingAlarmTypes & CalEvent::TEMPLATE))
{
creator = new CalendarCreator(CalEvent::TEMPLATE, QLatin1String("template.ics"), i18nc("@info/plain", "Alarm Templates"));
connect(creator, SIGNAL(finished(CalendarCreator*)), SLOT(calendarCreated(CalendarCreator*)));
connect(creator, SIGNAL(creating(QString)), SLOT(creatingCalendar(QString)));
......
/*
* calendarmigrator.h - migrates or creates KAlarm Akonadi resources
* Program: kalarm
* Copyright © 2011 by David Jarvie <djarvie@kde.org>
* Copyright © 2011-2012 by David Jarvie <djarvie@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -31,10 +31,17 @@
class KConfigGroup;
class KJob;
namespace KRES { class Resource; }
namespace Akonadi { class CollectionFetchJob; }
class CalendarCreator;
class CalendarUpdater;
using namespace KAlarmCal;
/**
* Class to migrate KResources alarm calendars from pre-Akonadi versions of
* KAlarm, and to create default calendar resources if none exist.
*/
class CalendarMigrator : public QObject
{
Q_OBJECT
......@@ -52,16 +59,20 @@ class CalendarMigrator : public QObject
void creating(const QString& path, bool finished);
private slots:
void collectionFetchResult(KJob*);
void creatingCalendar(const QString& path);
void calendarCreated(CalendarCreator*);
private:
CalendarMigrator(QObject* parent = 0);
void migrateOrCreate();
void createDefaultResources();
template <class Interface> static bool updateStorageFormat(const Akonadi::AgentInstance&, QString& errorMessage, QObject* parent);
static CalendarMigrator* mInstance;
QList<CalendarCreator*> mCalendarsPending; // pending calendar migration or creation jobs
QList<Akonadi::CollectionFetchJob*> mFetchesPending; // pending collection fetch jobs for existing resources
CalEvent::Types mExistingAlarmTypes; // alarm types provided by existing Akonadi resources
friend class CalendarUpdater;
};
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment