File indexing completed on 2024-05-12 05:10:36

0001 /******************************************************************************
0002  * konsolekalendar.cpp                                                        *
0003  *                                                                            *
0004  * KonsoleKalendar is a command line interface to KDE calendars               *
0005  * SPDX-FileCopyrightText: 2002-2004 Tuukka Pasanen <illuusio@mailcity.com>   *
0006  * SPDX-FileCopyrightText: 2003-2005 Allen Winter <winter@kde.org>            *
0007  *                                                                            *
0008  * SPDX-License-Identifier: GPL-2.0-or-later WITH Qt-Commercial-exception-1.0 *
0009  *                                                                            *
0010  ******************************************************************************/
0011 /**
0012  * @file konsolekalendar.cpp
0013  * Provides the KonsoleKalendar class definition.
0014  * @author Tuukka Pasanen
0015  * @author Allen Winter
0016  */
0017 #include "konsolekalendar.h"
0018 #include "konsolekalendaradd.h"
0019 #include "konsolekalendarchange.h"
0020 #include "konsolekalendardelete.h"
0021 #include "konsolekalendarexports.h"
0022 
0023 #include "konsolekalendar_debug.h"
0024 #include <KLocalizedString>
0025 
0026 #include <Akonadi/AgentInstance>
0027 #include <Akonadi/AgentInstanceCreateJob>
0028 #include <Akonadi/AgentManager>
0029 #include <Akonadi/Collection>
0030 #include <Akonadi/CollectionFetchJob>
0031 #include <Akonadi/CollectionFetchScope>
0032 
0033 #include <QDBusInterface>
0034 #include <QDBusReply>
0035 #include <QEventLoop>
0036 #include <QFile>
0037 #include <QFileInfo>
0038 #include <QTextStream>
0039 
0040 #include <cstdio>
0041 #include <cstdlib>
0042 #include <iostream>
0043 
0044 using namespace KCalendarCore;
0045 using namespace std;
0046 
0047 KonsoleKalendar::KonsoleKalendar(KonsoleKalendarVariables *variables)
0048 {
0049     m_variables = variables;
0050 }
0051 
0052 KonsoleKalendar::~KonsoleKalendar() = default;
0053 
0054 bool KonsoleKalendar::importCalendar()
0055 {
0056     KonsoleKalendarAdd add(m_variables);
0057 
0058     qCDebug(KONSOLEKALENDAR_LOG) << "konsolecalendar.cpp::importCalendar() | importing now!";
0059     return add.addImportedCalendar();
0060 }
0061 
0062 bool KonsoleKalendar::printCalendarList()
0063 {
0064     auto job = new Akonadi::CollectionFetchJob(Akonadi::Collection::root(), Akonadi::CollectionFetchJob::Recursive);
0065     const QStringList mimeTypes = QStringList() << QStringLiteral("text/calendar") << KCalendarCore::Event::eventMimeType()
0066                                                 << KCalendarCore::Todo::todoMimeType() << KCalendarCore::Journal::journalMimeType();
0067     job->fetchScope().setContentMimeTypes(mimeTypes);
0068     QEventLoop loop;
0069     QObject::connect(job, &Akonadi::CollectionFetchJob::result, &loop, &QEventLoop::quit);
0070     job->start();
0071     loop.exec();
0072 
0073     if (job->error() != 0) {
0074         return false;
0075     }
0076 
0077     const Akonadi::Collection::List collections = job->collections();
0078 
0079     if (collections.isEmpty()) {
0080         cout << i18n("There are no calendars available.").toLocal8Bit().data() << endl;
0081     } else {
0082         cout << "--------------------------" << endl;
0083         auto mimeTypeSet = QSet<QString>(mimeTypes.begin(), mimeTypes.end()); // set changes by run method intersect
0084         for (const Akonadi::Collection &collection : collections) {
0085             const QStringList contentMimeTypes = collection.contentMimeTypes();
0086             auto collectionMimeTypeSet = QSet<QString>(contentMimeTypes.begin(), contentMimeTypes.end());
0087 
0088             if (mimeTypeSet.intersects(collectionMimeTypeSet)) {
0089                 QString colId = QString::number(collection.id()).leftJustified(6, QLatin1Char(' '));
0090                 colId += QLatin1StringView("- ");
0091 
0092                 bool readOnly = !(collection.rights() & Akonadi::Collection::CanCreateItem || collection.rights() & Akonadi::Collection::CanChangeItem
0093                                   || collection.rights() & Akonadi::Collection::CanDeleteItem);
0094 
0095                 QString readOnlyString = readOnly ? i18n("(Read only)") + QLatin1Char(' ') : QString();
0096 
0097                 cout << colId.toLocal8Bit().data() << readOnlyString.toLocal8Bit().constData() << collection.displayName().toLocal8Bit().data() << endl;
0098             }
0099         }
0100     }
0101 
0102     return true;
0103 }
0104 
0105 bool KonsoleKalendar::createAkonadiResource(const QString &icalFileUri)
0106 {
0107     Akonadi::AgentType type = Akonadi::AgentManager::self()->type(QStringLiteral("akonadi_ical_resource"));
0108     auto job = new Akonadi::AgentInstanceCreateJob(type);
0109     QEventLoop loop;
0110     QObject::connect(job, &Akonadi::CollectionFetchJob::result, &loop, &QEventLoop::quit);
0111     job->start();
0112     loop.exec();
0113     if (job->error() != 0) {
0114         return false;
0115     }
0116     auto inst = job->instance();
0117     inst.setName(QFileInfo(icalFileUri).baseName());
0118     QDBusInterface iface(QStringLiteral("org.freedesktop.Akonadi.Resource.") + inst.identifier(), QStringLiteral("/Settings"));
0119     QDBusReply<void> reply = iface.call(QStringLiteral("setDisplayName"), QFileInfo(icalFileUri).baseName());
0120     if (!reply.isValid()) {
0121         qCWarning(KONSOLEKALENDAR_LOG) << "Could not set setting 'name': " << reply.error().message();
0122         return false;
0123     }
0124     reply = iface.call(QStringLiteral("setPath"), icalFileUri);
0125     if (!reply.isValid()) {
0126         qCWarning(KONSOLEKALENDAR_LOG) << "Could not set setting 'path': " << reply.error().message();
0127         return false;
0128     }
0129     reply = iface.call(QStringLiteral("save"));
0130     if (!reply.isValid()) {
0131         qCWarning(KONSOLEKALENDAR_LOG) << "Could not save settings: " << reply.error().message();
0132         return false;
0133     }
0134     inst.reconfigure();
0135     return true;
0136 }
0137 
0138 bool KonsoleKalendar::createCalendar()
0139 {
0140     bool status = false;
0141 
0142     const QString filename = m_variables->getCalendarFile();
0143 
0144     if (m_variables->isDryRun()) {
0145         cout << i18n("Create Calendar <Dry Run>: %1", filename).toLocal8Bit().data() << endl;
0146     } else {
0147         qCDebug(KONSOLEKALENDAR_LOG) << "konsolekalendar.cpp::createCalendar() |"
0148                                      << "Creating calendar file: " << filename.toLocal8Bit().data();
0149 
0150         if (m_variables->isVerbose()) {
0151             cout << i18n("Create Calendar <Verbose>: %1", filename).toLocal8Bit().data() << endl;
0152         }
0153 
0154         status = createAkonadiResource(QStringLiteral("file://%1").arg(filename));
0155     }
0156 
0157     return status;
0158 }
0159 
0160 bool KonsoleKalendar::showInstance()
0161 {
0162     bool status = true;
0163     QFile f;
0164     QString title;
0165     Event::Ptr event;
0166     const auto timeZone = m_variables->getCalendar()->timeZone();
0167     Akonadi::CalendarBase::Ptr calendar = m_variables->getCalendar();
0168 
0169     if (m_variables->isDryRun()) {
0170         cout << qPrintable(i18n("View Events <Dry Run>:")) << endl;
0171         printSpecs();
0172     } else {
0173         qCDebug(KONSOLEKALENDAR_LOG) << "konsolekalendar.cpp::showInstance() |"
0174                                      << "open export file";
0175 
0176         if (m_variables->isExportFile()) {
0177             f.setFileName(m_variables->getExportFile());
0178             if (!f.open(QIODevice::WriteOnly)) {
0179                 status = false;
0180                 qCDebug(KONSOLEKALENDAR_LOG) << "konsolekalendar.cpp::showInstance() |"
0181                                              << "unable to open export file" << m_variables->getExportFile();
0182             }
0183         } else {
0184             f.open(stdout, QIODevice::WriteOnly);
0185         }
0186 
0187         if (status) {
0188             qCDebug(KONSOLEKALENDAR_LOG) << "konsolekalendar.cpp::showInstance() |"
0189                                          << "opened successful";
0190 
0191             if (m_variables->isVerbose()) {
0192                 cout << i18n("View Event <Verbose>:").toLocal8Bit().data() << endl;
0193                 printSpecs();
0194             }
0195 
0196             QTextStream ts(&f);
0197 
0198             if (m_variables->getAll()) {
0199                 qCDebug(KONSOLEKALENDAR_LOG) << "konsolekalendar.cpp::showInstance() |"
0200                                              << "view all events sorted list";
0201 
0202                 const Event::List sortedList = calendar->events(EventSortStartDate);
0203                 qCDebug(KONSOLEKALENDAR_LOG) << "Found" << sortedList.count() << "events";
0204                 if (!sortedList.isEmpty()) {
0205                     // The code that was here before the akonadi port was really slow with 200 events
0206                     // this is much faster:
0207                     for (const KCalendarCore::Event::Ptr &event : sortedList) {
0208                         status &= printEvent(&ts, event, event->dtStart().date());
0209                     }
0210                 }
0211             } else if (m_variables->isUID()) {
0212                 qCDebug(KONSOLEKALENDAR_LOG) << "konsolekalendar.cpp::showInstance() |"
0213                                              << "view events by uid list";
0214                 // TODO: support a list of UIDs
0215                 event = calendar->event(m_variables->getUID());
0216                 // If this UID represents a recurring Event,
0217                 // only the first day of the Event will be printed
0218                 status = printEvent(&ts, event, event->dtStart().date());
0219             } else if (m_variables->isNext()) {
0220                 qCDebug(KONSOLEKALENDAR_LOG) << "konsolekalendar.cpp::showInstance() |"
0221                                              << "Show next activity in calendar";
0222 
0223                 QDateTime datetime = m_variables->getStartDateTime();
0224                 datetime = datetime.addDays(720);
0225 
0226                 QDate dt;
0227                 for (dt = m_variables->getStartDateTime().date(); dt <= datetime.date(); dt = dt.addDays(1)) {
0228                     Event::List events = calendar->events(dt, timeZone, EventSortStartDate, SortDirectionAscending);
0229                     qCDebug(KONSOLEKALENDAR_LOG) << "2-Found" << events.count() << "events on date" << dt;
0230                     // finished here when we get the next event
0231                     if (!events.isEmpty()) {
0232                         qCDebug(KONSOLEKALENDAR_LOG) << "konsolekalendar.cpp::showInstance() |"
0233                                                      << "Got the next event";
0234                         printEvent(&ts, events.first(), dt);
0235                         return true;
0236                     }
0237                 }
0238             }
0239             f.close();
0240         }
0241     }
0242     return status;
0243 }
0244 
0245 bool KonsoleKalendar::printEventList(QTextStream *ts, Event::List *eventList, QDate date)
0246 {
0247     bool status = true;
0248 
0249     qCDebug(KONSOLEKALENDAR_LOG) << eventList->count();
0250     if (!eventList->isEmpty()) {
0251         Event::Ptr singleEvent;
0252         Event::List::ConstIterator it;
0253 
0254         for (it = eventList->constBegin(); it != eventList->constEnd() && status != false; ++it) {
0255             singleEvent = *it;
0256 
0257             status = printEvent(ts, singleEvent, date);
0258         }
0259     }
0260 
0261     return status;
0262 }
0263 
0264 bool KonsoleKalendar::printEvent(QTextStream *ts, const Event::Ptr &event, QDate dt)
0265 {
0266     bool status = false;
0267     KonsoleKalendarExports exports;
0268 
0269     if (event) {
0270         switch (m_variables->getExportType()) {
0271         case ExportTypeCSV:
0272             qCDebug(KONSOLEKALENDAR_LOG) << "konsolekalendar.cpp::printEvent() |"
0273                                          << "CSV export";
0274             status = exports.exportAsCSV(ts, event, dt);
0275             break;
0276 
0277         case ExportTypeTextShort: {
0278             qCDebug(KONSOLEKALENDAR_LOG) << "konsolekalendar.cpp::printEvent() |"
0279                                          << "TEXT-SHORT export";
0280             bool sameDay = true;
0281             if (dt.daysTo(m_saveDate)) {
0282                 sameDay = false;
0283                 m_saveDate = dt;
0284             }
0285             status = exports.exportAsTxtShort(ts, event, dt, sameDay);
0286         } break;
0287 
0288         default: // Default export-type is ExportTypeText
0289             qCDebug(KONSOLEKALENDAR_LOG) << "konsolekalendar.cpp::printEvent() |"
0290                                          << "TEXT export";
0291             status = exports.exportAsTxt(ts, event, dt);
0292             break;
0293         }
0294     }
0295     return status;
0296 }
0297 
0298 bool KonsoleKalendar::addEvent()
0299 {
0300     qCDebug(KONSOLEKALENDAR_LOG) << "konsolecalendar.cpp::addEvent() |"
0301                                  << "Create Adding";
0302     KonsoleKalendarAdd add(m_variables);
0303     qCDebug(KONSOLEKALENDAR_LOG) << "konsolecalendar.cpp::addEvent() |"
0304                                  << "Adding Event now!";
0305     return add.addEvent();
0306 }
0307 
0308 bool KonsoleKalendar::changeEvent()
0309 {
0310     qCDebug(KONSOLEKALENDAR_LOG) << "konsolecalendar.cpp::changeEvent() |"
0311                                  << "Create Changing";
0312     KonsoleKalendarChange change(m_variables);
0313     qCDebug(KONSOLEKALENDAR_LOG) << "konsolecalendar.cpp::changeEvent() |"
0314                                  << "Changing Event now!";
0315     return change.changeEvent();
0316 }
0317 
0318 bool KonsoleKalendar::deleteEvent()
0319 {
0320     qCDebug(KONSOLEKALENDAR_LOG) << "konsolecalendar.cpp::deleteEvent() |"
0321                                  << "Create Deleting";
0322     KonsoleKalendarDelete del(m_variables);
0323     qCDebug(KONSOLEKALENDAR_LOG) << "konsolecalendar.cpp::deleteEvent() |"
0324                                  << "Deleting Event now!";
0325     return del.deleteEvent();
0326 }
0327 
0328 bool KonsoleKalendar::isEvent(const QDateTime &startdate, const QDateTime &enddate, const QString &summary)
0329 {
0330     // Search for an event with specified start and end datetime stamp and summary
0331 
0332     Event::Ptr event;
0333     Event::List::ConstIterator it;
0334 
0335     bool found = false;
0336 
0337     const auto timeZone = m_variables->getCalendar()->timeZone();
0338     Event::List eventList(m_variables->getCalendar()->rawEventsForDate(startdate.date(), timeZone, EventSortStartDate, SortDirectionAscending));
0339     for (it = eventList.constBegin(); it != eventList.constEnd(); ++it) {
0340         event = *it;
0341         if (event->dtEnd().toTimeZone(timeZone) == enddate && event->summary() == summary) {
0342             found = true;
0343             break;
0344         }
0345     }
0346     return found;
0347 }
0348 
0349 void KonsoleKalendar::printSpecs()
0350 {
0351     cout << i18n("  What:  %1", m_variables->getSummary()).toLocal8Bit().data() << endl;
0352 
0353     cout << i18n("  Begin: %1", m_variables->getStartDateTime().toString(Qt::TextDate)).toLocal8Bit().data() << endl;
0354 
0355     cout << i18n("  End:   %1", m_variables->getEndDateTime().toString(Qt::TextDate)).toLocal8Bit().data() << endl;
0356 
0357     if (m_variables->getFloating() == true) {
0358         cout << i18n("  No Time Associated with Event").toLocal8Bit().data() << endl;
0359     }
0360 
0361     cout << i18n("  Desc:  %1", m_variables->getDescription()).toLocal8Bit().data() << endl;
0362 
0363     cout << i18n("  Location:  %1", m_variables->getLocation()).toLocal8Bit().data() << endl;
0364 }