• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepim API Reference
  • KDE Home
  • Contact Us
 

kalarm

  • sources
  • kde-4.12
  • kdepim
  • kalarm
calendarmigrator.cpp
Go to the documentation of this file.
1 /*
2  * calendarmigrator.cpp - migrates or creates KAlarm Akonadi resources
3  * Program: kalarm
4  * Copyright © 2011-2014 by David Jarvie <djarvie@kde.org>
5  *
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.
10  *
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.
15  *
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.
19  */
20 
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"
28 
29 #include <kalarmcal/collectionattribute.h>
30 #include <kalarmcal/compatibilityattribute.h>
31 #include <kalarmcal/version.h>
32 
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>
40 
41 #include <klocale.h>
42 #include <kconfiggroup.h>
43 #include <kstandarddirs.h>
44 #include <kdebug.h>
45 
46 #include <QTimer>
47 
48 using namespace Akonadi;
49 using namespace KAlarmCal;
50 
51 
52 // Creates, or migrates from KResources, a single alarm calendar
53 class CalendarCreator : public QObject
54 {
55  Q_OBJECT
56  public:
57  // Constructor to migrate a calendar from KResources.
58  CalendarCreator(const QString& resourceType, const KConfigGroup&);
59  // Constructor to create a default Akonadi calendar.
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);
69 
70  public slots:
71  void agentCreated(KJob*);
72 
73  signals:
74  void creating(const QString& path);
75  void finished(CalendarCreator*);
76 
77  private slots:
78  void fetchCollection();
79  void collectionFetchResult(KJob*);
80  void resourceSynchronised(KJob*);
81  void modifyCollectionJobDone(KJob*);
82 
83  private:
84  void finish(bool cleanup);
85  bool writeLocalFileConfig();
86  bool writeLocalDirectoryConfig();
87  bool writeRemoteFileConfig();
88  template <class Interface> Interface* writeBasicConfig();
89 
90  enum ResourceType { LocalFile, LocalDir, RemoteFile };
91 
92  AgentInstance mAgent;
93  CalEvent::Type mAlarmType;
94  ResourceType mResourceType;
95  QString mPath;
96  QString mName;
97  QColor mColour;
98  QString mErrorMessage;
99  Collection::Id mCollectionId;
100  int mCollectionFetchRetryCount;
101  bool mReadOnly;
102  bool mEnabled;
103  bool mStandard;
104  const bool mNew; // true if creating default, false if converting
105  bool mFinished;
106 };
107 
108 // Updates the backend calendar format of a single alarm calendar
109 class CalendarUpdater : public QObject
110 {
111  Q_OBJECT
112  public:
113  CalendarUpdater(const Collection& collection, bool dirResource,
114  bool ignoreKeepFormat, bool newCollection, QObject* parent);
115  ~CalendarUpdater();
116  // Return whether another instance is already updating this collection
117  bool isDuplicate() const { return mDuplicate; }
118  // Check whether any instance is for the given collection ID
119  static bool containsCollection(Collection::Id);
120 
121  public slots:
122  bool update();
123 
124  private:
125  static QList<CalendarUpdater*> mInstances;
126  Akonadi::Collection mCollection;
127  QObject* mParent;
128  const bool mDirResource;
129  const bool mIgnoreKeepFormat;
130  const bool mNewCollection;
131  const bool mDuplicate; // another instance is already updating this collection
132 };
133 
134 
135 CalendarMigrator* CalendarMigrator::mInstance = 0;
136 bool CalendarMigrator::mCompleted = false;
137 
138 CalendarMigrator::CalendarMigrator(QObject* parent)
139  : QObject(parent),
140  mExistingAlarmTypes(0)
141 {
142 }
143 
144 CalendarMigrator::~CalendarMigrator()
145 {
146  kDebug();
147  mInstance = 0;
148 }
149 
150 /******************************************************************************
151 * Create and return the unique CalendarMigrator instance.
152 */
153 CalendarMigrator* CalendarMigrator::instance()
154 {
155  if (!mInstance && !mCompleted)
156  mInstance = new CalendarMigrator;
157  return mInstance;
158 }
159 
160 /******************************************************************************
161 * Migrate old KResource calendars, or if none, create default Akonadi resources.
162 */
163 void CalendarMigrator::execute()
164 {
165  instance()->migrateOrCreate();
166 }
167 
168 /******************************************************************************
169 * Migrate old KResource calendars, and create default Akonadi resources.
170 */
171 void CalendarMigrator::migrateOrCreate()
172 {
173  kDebug();
174 
175  // First, check whether any Akonadi resources already exist, and if
176  // so, find their alarm types.
177  AgentInstance::List agents = AgentManager::self()->instances();
178  foreach (const AgentInstance& agent, agents)
179  {
180  QString type = agent.type().identifier();
181  if (type == QLatin1String("akonadi_kalarm_resource")
182  || type == QLatin1String("akonadi_kalarm_dir_resource"))
183  {
184  // Fetch the resource's collection to determine its alarm types
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*)));
189  // Note: Once all collections have been fetched, any missing
190  // default resources will be created.
191  }
192  }
193 
194  if (mFetchesPending.isEmpty())
195  {
196  // There are no Akonadi resources, so migrate any KResources alarm
197  // calendars from pre-Akonadi versions of KAlarm.
198  const QString configFile = KStandardDirs::locateLocal("config", QLatin1String("kresources/alarms/stdrc"));
199  KConfig config(configFile, KConfig::SimpleConfig);
200 
201  // Fetch all the KResource identifiers which are actually in use
202  KConfigGroup group = config.group("General");
203  QStringList keys = group.readEntry("ResourceKeys", QStringList())
204  + group.readEntry("PassiveResourceKeys", QStringList());
205 
206  // Create an Akonadi resource for each KResource id
207  CalendarCreator* creator;
208  foreach (const QString& id, keys)
209  {
210  KConfigGroup configGroup = config.group(QLatin1String("Resource_") + id);
211  QString resourceType = configGroup.readEntry("ResourceType", QString());
212  QString agentType;
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");
219  else
220  continue; // unknown resource type - can't convert
221 
222  creator = new CalendarCreator(resourceType, configGroup);
223  if (!creator->isValid())
224  delete creator;
225  else
226  {
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);
232  }
233  }
234 
235  // After migrating KResources, create any necessary additional default
236  // Akonadi resources.
237  createDefaultResources();
238  }
239 }
240 
241 /******************************************************************************
242 * Called when a collection fetch job has completed.
243 * Finds which mime types are handled by the existing collection.
244 */
245 void CalendarMigrator::collectionFetchResult(KJob* j)
246 {
247  CollectionFetchJob* job = static_cast<CollectionFetchJob*>(j);
248  QString id = job->fetchScope().resource();
249  if (j->error())
250  kError() << "CollectionFetchJob" << id << "error: " << j->errorString();
251  else
252  {
253  Collection::List collections = job->collections();
254  if (collections.isEmpty())
255  kError() << "No collections found for resource" << id;
256  else
257  mExistingAlarmTypes |= CalEvent::types(collections[0].contentMimeTypes());
258  }
259  mFetchesPending.removeAll(job);
260 
261  if (mFetchesPending.isEmpty())
262  {
263  // The alarm types of all collections have been found, so now
264  // create any necessary default Akonadi resources.
265  createDefaultResources();
266  }
267 }
268 
269 /******************************************************************************
270 * Create default Akonadi resources for any alarm types not covered by existing
271 * resources. Normally, this occurs on the first run of KAlarm, but if resources
272 * have been deleted, it could occur on later runs.
273 * If the default calendar files already exist, they will be used; otherwise
274 * they will be created.
275 */
276 void CalendarMigrator::createDefaultResources()
277 {
278  kDebug();
279  CalendarCreator* creator;
280  if (!(mExistingAlarmTypes & CalEvent::ACTIVE))
281  {
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);
287  }
288  if (!(mExistingAlarmTypes & CalEvent::ARCHIVED))
289  {
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);
295  }
296  if (!(mExistingAlarmTypes & CalEvent::TEMPLATE))
297  {
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);
303  }
304 
305  if (mCalendarsPending.isEmpty())
306  {
307  mCompleted = true;
308  deleteLater();
309  }
310 }
311 
312 /******************************************************************************
313 * Called when a calendar resource is about to be created.
314 * Emits the 'creating' signal.
315 */
316 void CalendarMigrator::creatingCalendar(const QString& path)
317 {
318  emit creating(path, -1, false);
319 }
320 
321 /******************************************************************************
322 * Called when creation of a migrated or new default calendar resource has
323 * completed or failed.
324 */
325 void CalendarMigrator::calendarCreated(CalendarCreator* creator)
326 {
327  int i = mCalendarsPending.indexOf(creator);
328  if (i < 0)
329  return; // calendar already finished
330 
331  emit creating(creator->path(), creator->collectionId(), true);
332 
333  if (!creator->errorMessage().isEmpty())
334  {
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);
343  else
344  errmsg = i18nc("@info", "<para>%1</para><para>%2<nl/>(%3)</para>", errmsg, locn, creator->errorMessage());
345  KAMessageBox::error(MainWindow::mainMainWindow(), errmsg);
346  }
347  creator->deleteLater();
348 
349  mCalendarsPending.removeAt(i); // remove it from the pending list
350  if (mCalendarsPending.isEmpty())
351  {
352  mCompleted = true;
353  deleteLater();
354  }
355 }
356 
357 /******************************************************************************
358 * If an existing Akonadi resource calendar can be converted to the current
359 * KAlarm format, prompt the user whether to convert it, and if yes, tell the
360 * Akonadi resource to update the backend storage to the current format.
361 * The CollectionAttribute's KeepFormat property will be updated if the user
362 * chooses not to update the calendar.
363 *
364 * Note: the collection should be up to date: use AkonadiModel::refresh() before
365 * calling this function.
366 */
367 void CalendarMigrator::updateToCurrentFormat(const Collection& collection, bool ignoreKeepFormat, QWidget* parent)
368 {
369  kDebug() << collection.id();
370  if (CalendarUpdater::containsCollection(collection.id()))
371  return; // prevent multiple simultaneous user prompts
372  AgentInstance agent = AgentManager::self()->instance(collection.resource());
373  const QString id = agent.type().identifier();
374  bool dirResource;
375  if (id == QLatin1String("akonadi_kalarm_resource"))
376  dirResource = false;
377  else if (id == QLatin1String("akonadi_kalarm_dir_resource"))
378  dirResource = true;
379  else
380  {
381  kError() << "Invalid agent type" << id;
382  return;
383  }
384  CalendarUpdater* updater = new CalendarUpdater(collection, dirResource, ignoreKeepFormat, false, parent);
385  QTimer::singleShot(0, updater, SLOT(update()));
386 }
387 
388 
389 QList<CalendarUpdater*> CalendarUpdater::mInstances;
390 
391 CalendarUpdater::CalendarUpdater(const Collection& collection, bool dirResource,
392  bool ignoreKeepFormat, bool newCollection, QObject* parent)
393  : mCollection(collection),
394  mParent(parent),
395  mDirResource(dirResource),
396  mIgnoreKeepFormat(ignoreKeepFormat),
397  mNewCollection(newCollection),
398  mDuplicate(containsCollection(collection.id()))
399 {
400  mInstances.append(this);
401 }
402 
403 CalendarUpdater::~CalendarUpdater()
404 {
405  mInstances.removeAll(this);
406 }
407 
408 bool CalendarUpdater::containsCollection(Collection::Id id)
409 {
410  for (int i = 0, count = mInstances.count(); i < count; ++i)
411  {
412  if (mInstances[i]->mCollection.id() == id)
413  return true;
414  }
415  return false;
416 }
417 
418 bool CalendarUpdater::update()
419 {
420  kDebug() << mCollection.id() << (mDirResource ? "directory" : "file");
421  bool result = true;
422  if (!mDuplicate // prevent concurrent updates
423  && mCollection.hasAttribute<CompatibilityAttribute>()) // must know format to update
424  {
425  const CompatibilityAttribute* compatAttr = mCollection.attribute<CompatibilityAttribute>();
426  KACalendar::Compat compatibility = compatAttr->compatibility();
427  if ((compatibility & ~KACalendar::Converted)
428  // The calendar isn't in the current KAlarm format
429  && !(compatibility & ~(KACalendar::Convertible | KACalendar::Converted)))
430  {
431  // The calendar format is convertible to the current KAlarm format
432  if (!mIgnoreKeepFormat
433  && mCollection.hasAttribute<CollectionAttribute>()
434  && mCollection.attribute<CollectionAttribute>()->keepFormat())
435  kDebug() << "Not updating format (previous user choice)";
436  else
437  {
438  // The user hasn't previously said not to convert it
439  QString versionString = KAlarmCal::getVersionString(compatAttr->version());
440  QString msg = KAlarm::conversionPrompt(mCollection.name(), versionString, false);
441  kDebug() << "Version" << versionString;
442  if (KAMessageBox::warningYesNo(qobject_cast<QWidget*>(mParent), msg) != KMessageBox::Yes)
443  result = false; // the user chose not to update the calendar
444  else
445  {
446  // Tell the resource to update the backend storage format
447  QString errmsg;
448  if (!mNewCollection)
449  {
450  // Refetch the collection's details because anything could
451  // have happened since the prompt was first displayed.
452  if (!AkonadiModel::instance()->refresh(mCollection))
453  errmsg = i18nc("@info/plain", "Invalid collection");
454  }
455  if (errmsg.isEmpty())
456  {
457  AgentInstance agent = AgentManager::self()->instance(mCollection.resource());
458  if (mDirResource)
459  CalendarMigrator::updateStorageFormat<OrgKdeAkonadiKAlarmDirSettingsInterface>(agent, errmsg, mParent);
460  else
461  CalendarMigrator::updateStorageFormat<OrgKdeAkonadiKAlarmSettingsInterface>(agent, errmsg, mParent);
462  }
463  if (!errmsg.isEmpty())
464  {
465  KAMessageBox::error(MainWindow::mainMainWindow(),
466  i18nc("@info", "%1<nl/>(%2)",
467  i18nc("@info/plain", "Failed to update format of calendar <resource>%1</resource>", mCollection.name()),
468  errmsg));
469  }
470  }
471  if (!mNewCollection)
472  {
473  // Record the user's choice of whether to update the calendar
474  QModelIndex ix = AkonadiModel::instance()->collectionIndex(mCollection);
475  AkonadiModel::instance()->setData(ix, !result, AkonadiModel::KeepFormatRole);
476  }
477  }
478  }
479  }
480  deleteLater();
481  return result;
482 }
483 
484 /******************************************************************************
485 * Tell an Akonadi resource to update the backend storage format to the current
486 * KAlarm format.
487 * Reply = true if success; if false, 'errorMessage' contains the error message.
488 */
489 template <class Interface> bool CalendarMigrator::updateStorageFormat(const AgentInstance& agent, QString& errorMessage, QObject* parent)
490 {
491  kDebug();
492  Interface* iface = getAgentInterface<Interface>(agent, errorMessage, parent);
493  if (!iface)
494  {
495  kDebug() << errorMessage;
496  return false;
497  }
498  iface->setUpdateStorageFormat(true);
499  iface->writeConfig();
500  delete iface;
501  kDebug() << "true";
502  return true;
503 }
504 
505 /******************************************************************************
506 * Create a D-Bus interface to an Akonadi resource.
507 * Reply = interface if success
508 * = 0 if error: 'errorMessage' contains the error message.
509 */
510 template <class Interface> Interface* CalendarMigrator::getAgentInterface(const AgentInstance& agent, QString& errorMessage, QObject* parent)
511 {
512  Interface* iface = new Interface(QLatin1String("org.freedesktop.Akonadi.Resource.") + agent.identifier(),
513  QLatin1String("/Settings"), QDBusConnection::sessionBus(), parent);
514  if (!iface->isValid())
515  {
516  errorMessage = iface->lastError().message();
517  kDebug() << "D-Bus error accessing resource:" << errorMessage;
518  delete iface;
519  return 0;
520  }
521  return iface;
522 }
523 
524 
525 /******************************************************************************
526 * Constructor to migrate a KResources calendar, using its parameters.
527 */
528 CalendarCreator::CalendarCreator(const QString& resourceType, const KConfigGroup& config)
529  : mAlarmType(CalEvent::EMPTY),
530  mNew(false),
531  mFinished(false)
532 {
533  // Read the resource configuration parameters from the config
534  const char* pathKey = 0;
535  if (resourceType == QLatin1String("file"))
536  {
537  mResourceType = LocalFile;
538  pathKey = "CalendarURL";
539  }
540  else if (resourceType == QLatin1String("dir"))
541  {
542  mResourceType = LocalDir;
543  pathKey = "CalendarURL";
544  }
545  else if (resourceType == QLatin1String("remote"))
546  {
547  mResourceType = RemoteFile;
548  pathKey = "DownloadUrl";
549  }
550  else
551  {
552  kError() << "Invalid resource type:" << resourceType;
553  return;
554  }
555  mPath = config.readPathEntry(pathKey, QLatin1String(""));
556  switch (config.readEntry("AlarmType", (int)0))
557  {
558  case 1: mAlarmType = CalEvent::ACTIVE; break;
559  case 2: mAlarmType = CalEvent::ARCHIVED; break;
560  case 4: mAlarmType = CalEvent::TEMPLATE; break;
561  default:
562  kError() << "Invalid alarm type for resource";
563  return;
564  }
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;
571 }
572 
573 /******************************************************************************
574 * Constructor to create a new default local file resource.
575 * This is created as enabled, read-write, and standard for its alarm type.
576 */
577 CalendarCreator::CalendarCreator(CalEvent::Type alarmType, const QString& file, const QString& name)
578  : mAlarmType(alarmType),
579  mResourceType(LocalFile),
580  mName(name),
581  mColour(),
582  mReadOnly(false),
583  mEnabled(true),
584  mStandard(true),
585  mNew(true),
586  mFinished(false)
587 {
588  mPath = KStandardDirs::locateLocal("appdata", file);
589  kDebug() << "New:" << mName << ", type=" << mAlarmType << ", path=" << mPath;
590 }
591 
592 /******************************************************************************
593 * Create the Akonadi agent for this calendar.
594 */
595 void CalendarCreator::createAgent(const QString& agentType, QObject* parent)
596 {
597  emit creating(mPath);
598  AgentInstanceCreateJob* job = new AgentInstanceCreateJob(agentType, parent);
599  connect(job, SIGNAL(result(KJob*)), SLOT(agentCreated(KJob*)));
600  job->start();
601 }
602 
603 /******************************************************************************
604 * Called when the agent creation job for this resource has completed.
605 * Applies the calendar resource configuration to the Akonadi agent.
606 */
607 void CalendarCreator::agentCreated(KJob* j)
608 {
609  if (j->error())
610  {
611  mErrorMessage = j->errorString();
612  kError() << "AgentInstanceCreateJob error:" << mErrorMessage;
613  finish(false);
614  return;
615  }
616 
617  // Configure the Akonadi Agent
618  kDebug() << mName;
619  AgentInstanceCreateJob* job = static_cast<AgentInstanceCreateJob*>(j);
620  mAgent = job->instance();
621  mAgent.setName(mName);
622  bool ok = false;
623  switch (mResourceType)
624  {
625  case LocalFile:
626  ok = writeLocalFileConfig();
627  break;
628  case LocalDir:
629  ok = writeLocalDirectoryConfig();
630  break;
631  case RemoteFile:
632  ok = writeRemoteFileConfig();
633  break;
634  default:
635  kError() << "Invalid resource type";
636  break;
637  }
638  if (!ok)
639  {
640  finish(true);
641  return;
642  }
643  mAgent.reconfigure(); // notify the agent that its configuration has been changed
644 
645  // Wait for the resource to create its collection.
646  ResourceSynchronizationJob* sjob = new ResourceSynchronizationJob(mAgent);
647  connect(sjob, SIGNAL(result(KJob*)), SLOT(resourceSynchronised(KJob*)));
648  sjob->start(); // this is required (not an Akonadi::Job)
649 }
650 
651 /******************************************************************************
652 * Called when a resource synchronisation job has completed.
653 * Fetches the collection which this agent manages.
654 */
655 void CalendarCreator::resourceSynchronised(KJob* j)
656 {
657  kDebug() << mName;
658  if (j->error())
659  {
660  // Don't give up on error - we can still try to fetch the collection
661  kError() << "ResourceSynchronizationJob error: " << j->errorString();
662  }
663  mCollectionFetchRetryCount = 0;
664  fetchCollection();
665 }
666 
667 /******************************************************************************
668 * Find the collection which this agent manages.
669 */
670 void CalendarCreator::fetchCollection()
671 {
672  CollectionFetchJob* job = new CollectionFetchJob(Collection::root(), CollectionFetchJob::FirstLevel);
673  job->fetchScope().setResource(mAgent.identifier());
674  connect(job, SIGNAL(result(KJob*)), SLOT(collectionFetchResult(KJob*)));
675  job->start();
676 }
677 
678 bool CalendarCreator::writeLocalFileConfig()
679 {
680  OrgKdeAkonadiKAlarmSettingsInterface* iface = writeBasicConfig<OrgKdeAkonadiKAlarmSettingsInterface>();
681  if (!iface)
682  return false;
683  iface->setMonitorFile(true);
684  iface->writeConfig(); // save the Agent config changes
685  delete iface;
686  return true;
687 }
688 
689 bool CalendarCreator::writeLocalDirectoryConfig()
690 {
691  OrgKdeAkonadiKAlarmDirSettingsInterface* iface = writeBasicConfig<OrgKdeAkonadiKAlarmDirSettingsInterface>();
692  if (!iface)
693  return false;
694  iface->setMonitorFiles(true);
695  iface->writeConfig(); // save the Agent config changes
696  delete iface;
697  return true;
698 }
699 
700 bool CalendarCreator::writeRemoteFileConfig()
701 {
702  OrgKdeAkonadiKAlarmSettingsInterface* iface = writeBasicConfig<OrgKdeAkonadiKAlarmSettingsInterface>();
703  if (!iface)
704  return false;
705  iface->setMonitorFile(true);
706  iface->writeConfig(); // save the Agent config changes
707  delete iface;
708  return true;
709 }
710 
711 template <class Interface> Interface* CalendarCreator::writeBasicConfig()
712 {
713  Interface* iface = CalendarMigrator::getAgentInterface<Interface>(mAgent, mErrorMessage, this);
714  if (iface)
715  {
716  iface->setReadOnly(mReadOnly);
717  iface->setDisplayName(mName);
718  iface->setPath(mPath);
719  iface->setAlarmTypes(CalEvent::mimeTypes(mAlarmType));
720  iface->setUpdateStorageFormat(false);
721  }
722  return iface;
723 }
724 
725 /******************************************************************************
726 * Called when a collection fetch job has completed.
727 * Obtains the collection handled by the agent, and configures it.
728 */
729 void CalendarCreator::collectionFetchResult(KJob* j)
730 {
731  kDebug() << mName;
732  if (j->error())
733  {
734  mErrorMessage = j->errorString();
735  kError() << "CollectionFetchJob error: " << mErrorMessage;
736  finish(true);
737  return;
738  }
739  CollectionFetchJob* job = static_cast<CollectionFetchJob*>(j);
740  Collection::List collections = job->collections();
741  if (collections.isEmpty())
742  {
743  if (++mCollectionFetchRetryCount >= 10)
744  {
745  mErrorMessage = i18nc("@info/plain", "New configuration timed out");
746  kError() << "Timeout fetching collection for resource";
747  finish(true);
748  return;
749  }
750  // Need to wait a bit longer until the resource has initialised and
751  // created its collection. Retry after 200ms.
752  kDebug() << "Retrying";
753  QTimer::singleShot(200, this, SLOT(fetchCollection()));
754  return;
755  }
756  if (collections.count() > 1)
757  {
758  mErrorMessage = i18nc("@info/plain", "New configuration was corrupt");
759  kError() << "Wrong number of collections for this resource:" << collections.count();
760  finish(true);
761  return;
762  }
763 
764  // Set Akonadi Collection attributes
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);
772  if (mStandard)
773  attr->setStandard(mAlarmType);
774  if (mColour.isValid())
775  attr->setBackgroundColor(mColour);
776 
777  // Update the calendar to the current KAlarm format if necessary,
778  // and if the user agrees.
779  bool dirResource = false;
780  switch (mResourceType)
781  {
782  case LocalFile:
783  case RemoteFile:
784  break;
785  case LocalDir:
786  dirResource = true;
787  break;
788  default:
789  Q_ASSERT(0); // Invalid resource type
790  break;
791  }
792  bool keep = false;
793  bool duplicate = false;
794  if (!mReadOnly)
795  {
796  CalendarUpdater* updater = new CalendarUpdater(collection, dirResource, false, true, this);
797  duplicate = updater->isDuplicate();
798  keep = !updater->update(); // note that 'updater' will auto-delete when finished
799  }
800  if (!duplicate)
801  {
802  // Record the user's choice of whether to update the calendar
803  attr->setKeepFormat(keep);
804  }
805 
806  // Update the collection's CollectionAttribute value in the Akonadi database.
807  // Note that we can't supply 'collection' to CollectionModifyJob since
808  // that also contains the CompatibilityAttribute value, which is read-only
809  // for applications. So create a new Collection instance and only set a
810  // value for CollectionAttribute.
811  Collection c(collection.id());
812  CollectionAttribute* att = c.attribute<CollectionAttribute>(Entity::AddIfMissing);
813  *att = *attr;
814  CollectionModifyJob* cmjob = new CollectionModifyJob(c, this);
815  connect(cmjob, SIGNAL(result(KJob*)), this, SLOT(modifyCollectionJobDone(KJob*)));
816 }
817 
818 /******************************************************************************
819 * Called when a collection modification job has completed.
820 * Checks for any error.
821 */
822 void CalendarCreator::modifyCollectionJobDone(KJob* j)
823 {
824  Collection collection = static_cast<CollectionModifyJob*>(j)->collection();
825  if (j->error())
826  {
827  mErrorMessage = j->errorString();
828  kError() << "CollectionFetchJob error: " << mErrorMessage;
829  finish(true);
830  }
831  else
832  {
833  kDebug() << "Completed:" << mName;
834  finish(false);
835  }
836 }
837 
838 /******************************************************************************
839 * Emit the finished() signal. If 'cleanup' is true, delete the newly created
840 * but incomplete Agent.
841 */
842 void CalendarCreator::finish(bool cleanup)
843 {
844  if (!mFinished)
845  {
846  if (cleanup)
847  AgentManager::self()->removeInstance(mAgent);
848  mFinished = true;
849  emit finished(this);
850  }
851 }
852 
853 #include "calendarmigrator.moc"
854 #include "moc_calendarmigrator.cpp"
855 
856 // vim: et sw=4:
calendarmigrator.h
CalendarMigrator::creating
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 ...
QWidget
CalendarMigrator::updateToCurrentFormat
static void updateToCurrentFormat(const Akonadi::Collection &, bool ignoreKeepFormat, QWidget *parent)
Definition: calendarmigrator.cpp:367
KAMessageBox::error
static void error(QWidget *parent, const QString &text, const QString &caption=QString(), Options options=Options(Notify|WindowModal))
AkonadiModel::collectionIndex
QModelIndex collectionIndex(Akonadi::Collection::Id id) const
Definition: akonadimodel.h:119
QObject
akonadimodel.h
CalendarMigrator::getAgentInterface
static Interface * getAgentInterface(const Akonadi::AgentInstance &, QString &errorMessage, QObject *parent)
AkonadiModel::instance
static AkonadiModel * instance()
Definition: akonadimodel.cpp:83
mainwindow.h
main application window
CalendarMigrator::instance
static CalendarMigrator * instance()
Definition: calendarmigrator.cpp:153
messagebox.h
CalendarMigrator::~CalendarMigrator
~CalendarMigrator()
Definition: calendarmigrator.cpp:144
CalendarMigrator::CalendarUpdater
friend class CalendarUpdater
Definition: calendarmigrator.h:80
KAMessageBox::warningYesNo
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))
functions.h
miscellaneous functions
CalendarMigrator::execute
static void execute()
Definition: calendarmigrator.cpp:163
MainWindow::mainMainWindow
static MainWindow * mainMainWindow()
Definition: mainwindow.cpp:288
KJob
AkonadiModel::KeepFormatRole
Definition: akonadimodel.h:65
CalendarMigrator
Class to migrate KResources alarm calendars from pre-Akonadi versions of KAlarm, and to create defaul...
Definition: calendarmigrator.h:45
QList< CalendarUpdater * >
AkonadiModel::setData
virtual bool setData(const QModelIndex &, const QVariant &value, int role)
Definition: akonadimodel.cpp:496
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:59:10 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

kalarm

Skip menu "kalarm"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members

kdepim API Reference

Skip menu "kdepim API Reference"
  • akonadi_next
  • akregator
  • blogilo
  • calendarsupport
  • console
  •   kabcclient
  •   konsolekalendar
  • kaddressbook
  • kalarm
  •   lib
  • kdgantt2
  • kjots
  • kleopatra
  • kmail
  • knode
  • knotes
  • kontact
  • korgac
  • korganizer
  • ktimetracker
  • libkdepim
  • libkleo
  • libkpgp
  • mailcommon
  • messagelist
  • messageviewer

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal