Akonadi Calendar

icalimporter.cpp
1 /**
2  This file is part of the akonadi-calendar library.
3 
4  SPDX-FileCopyrightText: 2013 Sérgio Martins <[email protected]>
5 
6  SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8 
9 #include "icalimporter.h"
10 #include "akonadicalendar_debug.h"
11 #include "icalimporter_p.h"
12 #include "utils_p.h"
13 
14 #include <AkonadiCore/AgentInstanceCreateJob>
15 #include <AkonadiCore/AgentManager>
16 #include <AkonadiCore/ServerManager>
17 
18 #include <KCalendarCore/FileStorage>
19 
20 #include <KIO/Job>
21 
22 #include <QDBusInterface>
23 #include <QTemporaryFile>
24 #include <QTimeZone>
25 
26 using namespace KCalendarCore;
27 using namespace Akonadi;
28 
29 ICalImporter::Private::Private(IncidenceChanger *changer, ICalImporter *qq)
30  : QObject(qq)
31  , q(qq)
32  , m_changer(changer)
33 {
34  if (!changer) {
35  m_changer = new IncidenceChanger(q);
36  }
37  connect(m_changer, &IncidenceChanger::createFinished, this, &ICalImporter::Private::onIncidenceCreated);
38 }
39 
40 ICalImporter::Private::~Private()
41 {
42  delete m_temporaryFile;
43 }
44 
45 void ICalImporter::Private::onIncidenceCreated(int changeId,
46  const Akonadi::Item &item,
47  Akonadi::IncidenceChanger::ResultCode resultCode,
48  const QString &errorString)
49 {
50  Q_UNUSED(item)
51 
52  if (!m_pendingRequests.contains(changeId)) {
53  return; // Not ours
54  }
55 
56  m_pendingRequests.removeAll(changeId);
57 
58  if (resultCode != IncidenceChanger::ResultCodeSuccess) {
59  m_working = false;
60  setErrorMessage(errorString);
61  m_pendingRequests.clear();
62  Q_EMIT q->importIntoExistingFinished(false, m_numIncidences);
63  } else if (m_pendingRequests.isEmpty()) {
64  m_working = false;
65  Q_EMIT q->importIntoExistingFinished(true, m_numIncidences);
66  }
67 }
68 
69 void ICalImporter::Private::setErrorMessage(const QString &message)
70 {
71  m_lastErrorMessage = message;
72  qCritical() << message;
73 }
74 
75 void ICalImporter::Private::resourceCreated(KJob *job)
76 {
77  auto createjob = qobject_cast<Akonadi::AgentInstanceCreateJob *>(job);
78 
79  Q_ASSERT(createjob);
80  m_working = false;
81  if (createjob->error()) {
82  setErrorMessage(i18n("Error creating ical resource: %1", createjob->errorString()));
83  Q_EMIT q->importIntoNewFinished(false);
84  return;
85  }
86 
87  Akonadi::AgentInstance instance = createjob->instance();
88  const QString service = Akonadi::ServerManager::agentServiceName(Akonadi::ServerManager::Resource, instance.identifier());
89 
90  QDBusInterface iface(service, QStringLiteral("/Settings"));
91  if (!iface.isValid()) {
92  setErrorMessage(i18n("Failed to obtain D-Bus interface for remote configuration."));
93  Q_EMIT q->importIntoNewFinished(false);
94  return;
95  }
96 
97  const QString path = createjob->property("path").toString();
98  Q_ASSERT(!path.isEmpty());
99 
100  iface.call(QStringLiteral("setPath"), path);
101  instance.reconfigure();
102 
103  Q_EMIT q->importIntoNewFinished(true);
104 }
105 
106 void ICalImporter::Private::remoteDownloadFinished(KIO::Job *job, const QByteArray &data)
107 {
108  const bool success = job->error() == 0;
109  m_working = false;
110  if (success) {
111  delete m_temporaryFile;
112  m_temporaryFile = new QTemporaryFile();
113  m_temporaryFile->write(data.constData(), data.count());
114  q->importIntoExistingResource(QUrl(m_temporaryFile->fileName()), m_collection);
115  } else {
116  setErrorMessage(i18n("Could not download remote file."));
117  Q_EMIT q->importIntoExistingFinished(false, 0);
118  }
119 }
120 
121 ICalImporter::ICalImporter(Akonadi::IncidenceChanger *changer, QObject *parent)
122  : QObject(parent)
123  , d(new Private(changer, this))
124 {
125 }
126 
127 ICalImporter::~ICalImporter()
128 {
129 }
130 
131 QString ICalImporter::errorMessage() const
132 {
133  return d->m_lastErrorMessage;
134 }
135 
136 bool ICalImporter::importIntoNewResource(const QString &filename)
137 {
138  d->m_lastErrorMessage.clear();
139 
140  if (d->m_working) {
141  d->setErrorMessage(i18n("An import task is already in progress."));
142  return false;
143  }
144 
145  d->m_working = true;
146 
147  Akonadi::AgentType type = Akonadi::AgentManager::self()->type(QStringLiteral("akonadi_ical_resource"));
148 
149  auto job = new Akonadi::AgentInstanceCreateJob(type, this);
150  job->setProperty("path", filename);
151  connect(job, &KJob::result, d, &Private::resourceCreated);
152  job->start();
153 
154  return true;
155 }
156 
157 bool ICalImporter::importIntoExistingResource(const QUrl &url, Akonadi::Collection collection)
158 {
159  d->m_lastErrorMessage.clear();
160 
161  if (d->m_working) {
162  d->setErrorMessage(i18n("An import task is already in progress."));
163  return false;
164  }
165 
166  if (url.isEmpty()) {
167  d->setErrorMessage(i18n("Empty filename. Will not import ical file."));
168  return false;
169  }
170 
171  if (!url.isValid()) {
172  d->setErrorMessage(i18n("Url to import is malformed."));
173  return false;
174  }
175 
176  if (url.isLocalFile()) {
177  if (!QFile::exists(url.path())) {
178  d->setErrorMessage(i18n("The specified file doesn't exist, aborting import."));
179  return false;
180  }
182  FileStorage storage(temporaryCalendar);
183  storage.setFileName(url.path());
184  bool success = storage.load();
185  if (!success) {
186  d->setErrorMessage(i18n("Failed to load ical file, check permissions."));
187  return false;
188  }
189 
190  d->m_pendingRequests.clear();
191  const Incidence::List incidences = temporaryCalendar->incidences();
192 
193  if (incidences.isEmpty()) {
194  d->setErrorMessage(i18n("The ical file to merge is empty."));
195  return false;
196  }
197 
198  if (!collection.isValid()) {
199  int dialogCode;
202  collection = CalendarUtils::selectCollection(nullptr, dialogCode /*by-ref*/, mimeTypes);
203  }
204 
205  if (!collection.isValid()) {
206  // user canceled
207  d->setErrorMessage(QString());
208  return false;
209  }
210 
211  const IncidenceChanger::DestinationPolicy policySaved = d->m_changer->destinationPolicy();
212  d->m_changer->startAtomicOperation(i18n("Merge ical file into existing calendar."));
213  d->m_changer->setDestinationPolicy(IncidenceChanger::DestinationPolicyNeverAsk);
214  for (const Incidence::Ptr &incidence : qAsConst(incidences)) {
215  Q_ASSERT(incidence);
216  if (!incidence) {
217  continue;
218  }
219  const int requestId = d->m_changer->createIncidence(incidence, collection);
220  Q_ASSERT(requestId != -1); // -1 only happens with invalid incidences
221  if (requestId != -1) {
222  d->m_pendingRequests << requestId;
223  }
224  }
225  d->m_changer->endAtomicOperation();
226 
227  d->m_changer->setDestinationPolicy(policySaved); // restore
228  d->m_numIncidences = incidences.count();
229  } else {
230  d->m_collection = collection;
232  connect(job, QOverload<KIO::Job *, const QByteArray &>::of(&KIO::TransferJob::data), d, [this](KIO::Job *job, const QByteArray &ba) {
233  d->remoteDownloadFinished(job, ba);
234  });
235  }
236 
237  d->m_working = true;
238  return true;
239 }
bool isValid() const
static QLatin1String eventMimeType()
static QLatin1String journalMimeType()
QString identifier() const
bool exists() const const
bool isEmpty() const const
void clear()
Type type(const QSqlDatabase &db)
bool isEmpty() const const
const char * constData() const const
QTimeZone systemTimeZone()
QString path(QUrl::ComponentFormattingOptions options) const const
KIOCORE_EXPORT StoredTransferJob * storedGet(const QUrl &url, LoadType reload=NoReload, JobFlags flags=DefaultFlags)
int count(char ch) const const
static QString agentServiceName(ServiceAgentType agentType, const QString &identifier)
QStringList mimeTypes(Types)
KCRASH_EXPORT void setErrorMessage(const QString &message)
static QLatin1String todoMimeType()
AgentType type(const QString &identifier) const
QString i18n(const char *text, const TYPE &arg...)
void data(KIO::Job *job, const QByteArray &data)
bool isValid() const const
bool isEmpty() const const
FreeBusyManager::Singleton.
virtual Q_SCRIPTABLE void start()=0
int count(const T &value) const const
static AgentManager * self()
bool setProperty(const char *name, const QVariant &value)
void result(KJob *job)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
T qobject_cast(QObject *object)
QObject * parent() const const
Q_EMITQ_EMIT
int error() const
bool isLocalFile() const const
void reconfigure() const
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sat Jun 19 2021 23:12:24 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.