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

kalarm

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

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