1 /******************************************************************************
2 * konsolekalendar.cpp *
3 * *
4 * KonsoleKalendar is a command line interface to KDE calendars *
5 * SPDX-FileCopyrightText: 2002-2004 Tuukka Pasanen <illuusio@mailcity.com> *
6 * SPDX-FileCopyrightText: 2003-2005 Allen Winter <winter@kde.org> *
7 * *
8 * SPDX-License-Identifier: GPL-2.0-or-later WITH Qt-Commercial-exception-1.0 *
9 * *
10 ******************************************************************************/
11 /**
12 * @file konsolekalendar.cpp
13 * Provides the KonsoleKalendar class definition.
14 * @author Tuukka Pasanen
15 * @author Allen Winter
16 */
17 #include "konsolekalendar.h"
18 #include "konsolekalendaradd.h"
19 #include "konsolekalendarchange.h"
20 #include "konsolekalendardelete.h"
21 #include "konsolekalendarexports.h"
22
23 #include "konsolekalendar_debug.h"
24 #include <KLocalizedString>
25
26 #include <Akonadi/AgentInstance>
27 #include <Akonadi/AgentInstanceCreateJob>
28 #include <Akonadi/AgentManager>
29 #include <Akonadi/Collection>
30 #include <Akonadi/CollectionFetchJob>
31 #include <Akonadi/CollectionFetchScope>
32 #include <KCalUtils/HtmlExport>
33 #include <kcalutils/htmlexportsettings.h>
34
35 #include <QDBusInterface>
36 #include <QDBusReply>
37 #include <QEventLoop>
38 #include <QFile>
39 #include <QFileInfo>
40 #include <QTextStream>
41
42 #include <iostream>
43 #include <stdio.h>
44 #include <stdlib.h>
45
46 using namespace KCalendarCore;
47 using namespace std;
48
KonsoleKalendar(KonsoleKalendarVariables * variables)49 KonsoleKalendar::KonsoleKalendar(KonsoleKalendarVariables *variables)
50 {
51 m_variables = variables;
52 }
53
~KonsoleKalendar()54 KonsoleKalendar::~KonsoleKalendar()
55 {
56 }
57
importCalendar()58 bool KonsoleKalendar::importCalendar()
59 {
60 KonsoleKalendarAdd add(m_variables);
61
62 qCDebug(KONSOLEKALENDAR_LOG) << "konsolecalendar.cpp::importCalendar() | importing now!";
63 return add.addImportedCalendar();
64 }
65
printCalendarList()66 bool KonsoleKalendar::printCalendarList()
67 {
68 auto job = new Akonadi::CollectionFetchJob(Akonadi::Collection::root(), Akonadi::CollectionFetchJob::Recursive);
69 const QStringList mimeTypes = QStringList() << QStringLiteral("text/calendar") << KCalendarCore::Event::eventMimeType()
70 << KCalendarCore::Todo::todoMimeType() << KCalendarCore::Journal::journalMimeType();
71 job->fetchScope().setContentMimeTypes(mimeTypes);
72 QEventLoop loop;
73 QObject::connect(job, &Akonadi::CollectionFetchJob::result, &loop, &QEventLoop::quit);
74 job->start();
75 loop.exec();
76
77 if (job->error() != 0) {
78 return false;
79 }
80
81 const Akonadi::Collection::List collections = job->collections();
82
83 if (collections.isEmpty()) {
84 cout << i18n("There are no calendars available.").toLocal8Bit().data() << endl;
85 } else {
86 cout << "--------------------------" << endl;
87 auto mimeTypeSet = QSet<QString>(mimeTypes.begin(), mimeTypes.end()); // set changes by run method intersect
88 for (const Akonadi::Collection &collection : collections) {
89 const QStringList contentMimeTypes = collection.contentMimeTypes();
90 auto collectionMimeTypeSet = QSet<QString>(contentMimeTypes.begin(), contentMimeTypes.end());
91
92 if (mimeTypeSet.intersects(collectionMimeTypeSet)) {
93 QString colId = QString::number(collection.id()).leftJustified(6, QLatin1Char(' '));
94 colId += QLatin1String("- ");
95
96 bool readOnly = !(collection.rights() & Akonadi::Collection::CanCreateItem || collection.rights() & Akonadi::Collection::CanChangeItem
97 || collection.rights() & Akonadi::Collection::CanDeleteItem);
98
99 QString readOnlyString = readOnly ? i18n("(Read only)") + QLatin1Char(' ') : QString();
100
101 cout << colId.toLocal8Bit().data() << readOnlyString.toLocal8Bit().constData() << collection.displayName().toLocal8Bit().data() << endl;
102 }
103 }
104 }
105
106 return true;
107 }
108
createAkonadiResource(const QString & icalFileUri)109 bool KonsoleKalendar::createAkonadiResource(const QString &icalFileUri)
110 {
111 Akonadi::AgentType type = Akonadi::AgentManager::self()->type(QStringLiteral("akonadi_ical_resource"));
112 auto job = new Akonadi::AgentInstanceCreateJob(type);
113 QEventLoop loop;
114 QObject::connect(job, &Akonadi::CollectionFetchJob::result, &loop, &QEventLoop::quit);
115 job->start();
116 loop.exec();
117 if (job->error() != 0) {
118 return false;
119 }
120 auto inst = job->instance();
121 inst.setName(QFileInfo(icalFileUri).baseName());
122 QDBusInterface iface(QStringLiteral("org.freedesktop.Akonadi.Resource.") + inst.identifier(), QStringLiteral("/Settings"));
123 QDBusReply<void> reply = iface.call(QStringLiteral("setDisplayName"), QFileInfo(icalFileUri).baseName());
124 if (!reply.isValid()) {
125 qCWarning(KONSOLEKALENDAR_LOG) << "Could not set setting 'name': " << reply.error().message();
126 return false;
127 }
128 reply = iface.call(QStringLiteral("setPath"), icalFileUri);
129 if (!reply.isValid()) {
130 qCWarning(KONSOLEKALENDAR_LOG) << "Could not set setting 'path': " << reply.error().message();
131 return false;
132 }
133 reply = iface.call(QStringLiteral("save"));
134 if (!reply.isValid()) {
135 qCWarning(KONSOLEKALENDAR_LOG) << "Could not save settings: " << reply.error().message();
136 return false;
137 }
138 inst.reconfigure();
139 return true;
140 }
141
createCalendar()142 bool KonsoleKalendar::createCalendar()
143 {
144 bool status = false;
145
146 const QString filename = m_variables->getCalendarFile();
147
148 if (m_variables->isDryRun()) {
149 cout << i18n("Create Calendar <Dry Run>: %1", filename).toLocal8Bit().data() << endl;
150 } else {
151 qCDebug(KONSOLEKALENDAR_LOG) << "konsolekalendar.cpp::createCalendar() |"
152 << "Creating calendar file: " << filename.toLocal8Bit().data();
153
154 if (m_variables->isVerbose()) {
155 cout << i18n("Create Calendar <Verbose>: %1", filename).toLocal8Bit().data() << endl;
156 }
157
158 status = createAkonadiResource(QStringLiteral("file://%1").arg(filename));
159 }
160
161 return status;
162 }
163
showInstance()164 bool KonsoleKalendar::showInstance()
165 {
166 bool status = true;
167 QFile f;
168 QString title;
169 Event::Ptr event;
170 const auto timeZone = m_variables->getCalendar()->timeZone();
171 Akonadi::CalendarBase::Ptr calendar = m_variables->getCalendar();
172
173 if (m_variables->isDryRun()) {
174 cout << qPrintable(i18n("View Events <Dry Run>:")) << endl;
175 printSpecs();
176 } else {
177 qCDebug(KONSOLEKALENDAR_LOG) << "konsolekalendar.cpp::showInstance() |"
178 << "open export file";
179
180 if (m_variables->isExportFile()) {
181 f.setFileName(m_variables->getExportFile());
182 if (!f.open(QIODevice::WriteOnly)) {
183 status = false;
184 qCDebug(KONSOLEKALENDAR_LOG) << "konsolekalendar.cpp::showInstance() |"
185 << "unable to open export file" << m_variables->getExportFile();
186 }
187 } else {
188 f.open(stdout, QIODevice::WriteOnly);
189 }
190
191 if (status) {
192 qCDebug(KONSOLEKALENDAR_LOG) << "konsolekalendar.cpp::showInstance() |"
193 << "opened successful";
194
195 if (m_variables->isVerbose()) {
196 cout << i18n("View Event <Verbose>:").toLocal8Bit().data() << endl;
197 printSpecs();
198 }
199
200 QTextStream ts(&f);
201
202 if (m_variables->getExportType() != ExportTypeHTML && m_variables->getExportType() != ExportTypeMonthHTML) {
203 if (m_variables->getAll()) {
204 qCDebug(KONSOLEKALENDAR_LOG) << "konsolekalendar.cpp::showInstance() |"
205 << "view all events sorted list";
206
207 const Event::List sortedList = calendar->events(EventSortStartDate);
208 qCDebug(KONSOLEKALENDAR_LOG) << "Found" << sortedList.count() << "events";
209 if (!sortedList.isEmpty()) {
210 // The code that was here before the akonadi port was really slow with 200 events
211 // this is much faster:
212 for (const KCalendarCore::Event::Ptr &event : sortedList) {
213 status &= printEvent(&ts, event, event->dtStart().date());
214 }
215 }
216 } else if (m_variables->isUID()) {
217 qCDebug(KONSOLEKALENDAR_LOG) << "konsolekalendar.cpp::showInstance() |"
218 << "view events by uid list";
219 // TODO: support a list of UIDs
220 event = calendar->event(m_variables->getUID());
221 // If this UID represents a recurring Event,
222 // only the first day of the Event will be printed
223 status = printEvent(&ts, event, event->dtStart().date());
224 } else if (m_variables->isNext()) {
225 qCDebug(KONSOLEKALENDAR_LOG) << "konsolekalendar.cpp::showInstance() |"
226 << "Show next activity in calendar";
227
228 QDateTime datetime = m_variables->getStartDateTime();
229 datetime = datetime.addDays(720);
230
231 QDate dt;
232 for (dt = m_variables->getStartDateTime().date(); dt <= datetime.date(); dt = dt.addDays(1)) {
233 Event::List events = calendar->events(dt, timeZone, EventSortStartDate, SortDirectionAscending);
234 qCDebug(KONSOLEKALENDAR_LOG) << "2-Found" << events.count() << "events on date" << dt;
235 // finished here when we get the next event
236 if (!events.isEmpty()) {
237 qCDebug(KONSOLEKALENDAR_LOG) << "konsolekalendar.cpp::showInstance() |"
238 << "Got the next event";
239 printEvent(&ts, events.first(), dt);
240 return true;
241 }
242 }
243 } else {
244 qCDebug(KONSOLEKALENDAR_LOG) << "konsolekalendar.cpp::showInstance() |"
245 << "view raw events within date range list";
246
247 QDate dt;
248 for (dt = m_variables->getStartDateTime().date(); dt <= m_variables->getEndDateTime().date() && status != false; dt = dt.addDays(1)) {
249 Event::List events = calendar->events(dt, timeZone, EventSortStartDate, SortDirectionAscending);
250 qCDebug(KONSOLEKALENDAR_LOG) << "3-Found" << events.count() << "events on date: " << dt;
251 status = printEventList(&ts, &events, dt);
252 }
253 }
254 } else {
255 QDate firstdate;
256 QDate lastdate;
257 if (m_variables->getAll()) {
258 qCDebug(KONSOLEKALENDAR_LOG) << "konsolekalendar.cpp::showInstance() |"
259 << "HTML view all events sorted list";
260 // sort the events for this date by start date
261 // in order to determine the date range.
262 auto events = new Event::List(calendar->rawEvents(EventSortStartDate, SortDirectionAscending));
263 firstdate = events->first()->dtStart().date();
264 lastdate = events->last()->dtStart().date();
265 } else if (m_variables->isUID()) {
266 // TODO
267 qCDebug(KONSOLEKALENDAR_LOG) << "konsolekalendar.cpp::showInstance() |"
268 << "HTML view events by uid list";
269 cout << i18n("Sorry, export to HTML by UID is not supported yet").toLocal8Bit().data() << endl;
270 return false;
271 } else {
272 qCDebug(KONSOLEKALENDAR_LOG) << "konsolekalendar.cpp::showInstance() |"
273 << "HTML view raw events within date range list";
274 firstdate = m_variables->getStartDateTime().date();
275 lastdate = m_variables->getEndDateTime().date();
276 }
277
278 KCalUtils::HTMLExportSettings htmlSettings(QStringLiteral("Konsolekalendar"));
279
280 // TODO: get progname and url from the values set in main
281 htmlSettings.setCreditName(QStringLiteral("KonsoleKalendar"));
282 htmlSettings.setCreditURL(QStringLiteral("https://userbase.kde.org/KonsoleKalendar"));
283
284 htmlSettings.setExcludePrivate(true);
285 htmlSettings.setExcludeConfidential(true);
286
287 htmlSettings.setEventView(false);
288 htmlSettings.setMonthView(false);
289 if (m_variables->getExportType() == ExportTypeMonthHTML) {
290 title = i18n("Events:");
291 htmlSettings.setMonthView(true);
292 } else {
293 if (firstdate == lastdate) {
294 title = i18n("Events: %1", firstdate.toString(Qt::TextDate));
295 } else {
296 title = i18n("Events: %1 - %2", firstdate.toString(Qt::TextDate), lastdate.toString(Qt::TextDate));
297 }
298 htmlSettings.setEventView(true);
299 }
300 htmlSettings.setEventTitle(title);
301 htmlSettings.setEventAttendees(true);
302 // Not supporting Todos yet
303 // title = "To-Do List for " + firstdate.toString(Qt::TextDate);
304 // if ( firstdate != lastdate ) {
305 // title += " - " + lastdate.toString(Qt::TextDate);
306 // }
307 htmlSettings.setTodoListTitle(title);
308 htmlSettings.setTodoView(false);
309 // htmlSettings.setTaskCategories( false );
310 // htmlSettings.setTaskAttendees( false );
311 // htmlSettings.setTaskDueDate( true );
312
313 htmlSettings.setDateStart(QDateTime(firstdate.startOfDay()));
314 htmlSettings.setDateEnd(QDateTime(lastdate.startOfDay()));
315
316 auto exp = new KCalUtils::HtmlExport(calendar.data(), &htmlSettings);
317 status = exp->save(&ts);
318 delete exp;
319 }
320 f.close();
321 }
322 }
323 return status;
324 }
325
printEventList(QTextStream * ts,Event::List * eventList,QDate date)326 bool KonsoleKalendar::printEventList(QTextStream *ts, Event::List *eventList, QDate date)
327 {
328 bool status = true;
329
330 qCDebug(KONSOLEKALENDAR_LOG) << eventList->count();
331 if (!eventList->isEmpty()) {
332 Event::Ptr singleEvent;
333 Event::List::ConstIterator it;
334
335 for (it = eventList->constBegin(); it != eventList->constEnd() && status != false; ++it) {
336 singleEvent = *it;
337
338 status = printEvent(ts, singleEvent, date);
339 }
340 }
341
342 return status;
343 }
344
printEvent(QTextStream * ts,const Event::Ptr & event,QDate dt)345 bool KonsoleKalendar::printEvent(QTextStream *ts, const Event::Ptr &event, QDate dt)
346 {
347 bool status = false;
348 KonsoleKalendarExports exports;
349
350 if (event) {
351 switch (m_variables->getExportType()) {
352 case ExportTypeCSV:
353 qCDebug(KONSOLEKALENDAR_LOG) << "konsolekalendar.cpp::printEvent() |"
354 << "CSV export";
355 status = exports.exportAsCSV(ts, event, dt);
356 break;
357
358 case ExportTypeTextShort: {
359 qCDebug(KONSOLEKALENDAR_LOG) << "konsolekalendar.cpp::printEvent() |"
360 << "TEXT-SHORT export";
361 bool sameDay = true;
362 if (dt.daysTo(m_saveDate)) {
363 sameDay = false;
364 m_saveDate = dt;
365 }
366 status = exports.exportAsTxtShort(ts, event, dt, sameDay);
367 } break;
368
369 case ExportTypeHTML:
370 // this is handled separately for now
371 break;
372
373 default: // Default export-type is ExportTypeText
374 qCDebug(KONSOLEKALENDAR_LOG) << "konsolekalendar.cpp::printEvent() |"
375 << "TEXT export";
376 status = exports.exportAsTxt(ts, event, dt);
377 break;
378 }
379 }
380 return status;
381 }
382
addEvent()383 bool KonsoleKalendar::addEvent()
384 {
385 qCDebug(KONSOLEKALENDAR_LOG) << "konsolecalendar.cpp::addEvent() |"
386 << "Create Adding";
387 KonsoleKalendarAdd add(m_variables);
388 qCDebug(KONSOLEKALENDAR_LOG) << "konsolecalendar.cpp::addEvent() |"
389 << "Adding Event now!";
390 return add.addEvent();
391 }
392
changeEvent()393 bool KonsoleKalendar::changeEvent()
394 {
395 qCDebug(KONSOLEKALENDAR_LOG) << "konsolecalendar.cpp::changeEvent() |"
396 << "Create Changing";
397 KonsoleKalendarChange change(m_variables);
398 qCDebug(KONSOLEKALENDAR_LOG) << "konsolecalendar.cpp::changeEvent() |"
399 << "Changing Event now!";
400 return change.changeEvent();
401 }
402
deleteEvent()403 bool KonsoleKalendar::deleteEvent()
404 {
405 qCDebug(KONSOLEKALENDAR_LOG) << "konsolecalendar.cpp::deleteEvent() |"
406 << "Create Deleting";
407 KonsoleKalendarDelete del(m_variables);
408 qCDebug(KONSOLEKALENDAR_LOG) << "konsolecalendar.cpp::deleteEvent() |"
409 << "Deleting Event now!";
410 return del.deleteEvent();
411 }
412
isEvent(const QDateTime & startdate,const QDateTime & enddate,const QString & summary)413 bool KonsoleKalendar::isEvent(const QDateTime &startdate, const QDateTime &enddate, const QString &summary)
414 {
415 // Search for an event with specified start and end datetime stamp and summary
416
417 Event::Ptr event;
418 Event::List::ConstIterator it;
419
420 bool found = false;
421
422 const auto timeZone = m_variables->getCalendar()->timeZone();
423 Event::List eventList(m_variables->getCalendar()->rawEventsForDate(startdate.date(), timeZone, EventSortStartDate, SortDirectionAscending));
424 for (it = eventList.constBegin(); it != eventList.constEnd(); ++it) {
425 event = *it;
426 if (event->dtEnd().toTimeZone(timeZone) == enddate && event->summary() == summary) {
427 found = true;
428 break;
429 }
430 }
431 return found;
432 }
433
printSpecs()434 void KonsoleKalendar::printSpecs()
435 {
436 cout << i18n(" What: %1", m_variables->getSummary()).toLocal8Bit().data() << endl;
437
438 cout << i18n(" Begin: %1", m_variables->getStartDateTime().toString(Qt::TextDate)).toLocal8Bit().data() << endl;
439
440 cout << i18n(" End: %1", m_variables->getEndDateTime().toString(Qt::TextDate)).toLocal8Bit().data() << endl;
441
442 if (m_variables->getFloating() == true) {
443 cout << i18n(" No Time Associated with Event").toLocal8Bit().data() << endl;
444 }
445
446 cout << i18n(" Desc: %1", m_variables->getDescription()).toLocal8Bit().data() << endl;
447
448 cout << i18n(" Location: %1", m_variables->getLocation()).toLocal8Bit().data() << endl;
449 }
450