diff --git a/src/plugins/filters/icalendar/export/CMakeLists.txt b/src/plugins/filters/icalendar/export/CMakeLists.txt index 6eb34925..6232e786 100644 --- a/src/plugins/filters/icalendar/export/CMakeLists.txt +++ b/src/plugins/filters/icalendar/export/CMakeLists.txt @@ -1,26 +1,31 @@ include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${PLAN_SOURCE_DIR} ${PLANKERNEL_INCLUDES} + ${PLANUI_INCLUDES} ${PLANMAIN_INCLUDES} ) -set(icalendarexport_PART_SRCS +set(icalendarexport_SRCS icalendarexport.cpp + ICalExportDialog.cpp ) +ki18n_wrap_ui(icalendarexport_SRCS + ICalExportDialog.ui +) -add_library(planicalexport MODULE ${icalendarexport_PART_SRCS}) +add_library(planicalexport MODULE ${icalendarexport_SRCS}) #calligraplan_filter_desktop_to_json(planicalexport plan_icalendar_export.desktop) if(${KF5_VERSION} VERSION_LESS "5.16.0") kcoreaddons_desktop_to_json(planicalexport plan_icalendar_export.desktop) else() kcoreaddons_desktop_to_json(planicalexport plan_icalendar_export.desktop SERVICE_TYPES ${PLAN_SOURCE_DIR}/servicetypes/calligraplan_filter.desktop ) endif() target_link_libraries(planicalexport planprivate plankernel planmain) install(TARGETS planicalexport DESTINATION ${PLUGIN_INSTALL_DIR}/calligraplan/formatfilters) diff --git a/src/plugins/filters/icalendar/export/ICalExportDialog.cpp b/src/plugins/filters/icalendar/export/ICalExportDialog.cpp new file mode 100644 index 00000000..9669541d --- /dev/null +++ b/src/plugins/filters/icalendar/export/ICalExportDialog.cpp @@ -0,0 +1,81 @@ +/* This file is part of the KDE project + * Copyright (C) 2019 Dag Andersen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "ICalExportDialog.h" + +#include "kptproject.h" +#include "kptschedule.h" + +using namespace KPlato; + +QModelIndex findManager(const ScheduleItemModel *model, const QModelIndex &parent = QModelIndex()) +{ + for (int i = 0; i < model->rowCount(parent); ++i) { + QModelIndex idx = model->index(i, 0, parent); + ScheduleManager *sm = model->manager(idx); + if (sm) { + if (sm->isBaselined()) { + return idx; + } + } + if (model->hasChildren(idx)) { + QModelIndex cidx = findManager(model, idx); + if (cidx.isValid()) { + return cidx; + } + } + } + if (!parent.isValid() && model->rowCount() > 0) { + return model->index(0, 0); + } + return QModelIndex(); +} + +ICalExportDialog::ICalExportDialog(Project &project, QWidget *parent) + : KoDialog(parent) + , m_project(project) +{ + m_ui.setupUi(this); + setMainWidget(m_ui.ui_mainWidget); + setButtons(KoDialog::Ok | KoDialog::Cancel); + m_ui.ui_scheduleView->setProject(&project); + m_ui.ui_scheduleView->setColumnsHidden(QList() << 1 << -1); + m_ui.ui_scheduleView->expandAll(); + m_ui.ui_scheduleView->selectionModel()->select(findManager(m_ui.ui_scheduleView->model()), QItemSelectionModel::ClearAndSelect); +} + +bool ICalExportDialog::includeProject() const +{ + return m_ui.ui_project->isChecked(); +} + +bool ICalExportDialog::includeSummarytasks() const +{ + return m_ui.ui_summarytasks->isChecked(); +} + +long ICalExportDialog::scheduleId() const +{ + ScheduleManager *m = m_ui.ui_scheduleView->currentManager(); + if (!m) { + return ANYSCHEDULED; + } + return m->scheduleId(); +} + diff --git a/src/plugins/filters/icalendar/export/ICalExportDialog.h b/src/plugins/filters/icalendar/export/ICalExportDialog.h new file mode 100644 index 00000000..447b9a5d --- /dev/null +++ b/src/plugins/filters/icalendar/export/ICalExportDialog.h @@ -0,0 +1,50 @@ +/* This file is part of the KDE project + * Copyright (C) 2019 Dag Andersen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef ICALEXPORTDIALOG_H +#define ICALEXPORTDIALOG_H + +#include + +#include + +namespace KPlato +{ + +class Project; + +class ICalExportDialog : public KoDialog +{ + Q_OBJECT +public: + ICalExportDialog(Project &project, QWidget *parent=0); + + bool includeProject() const; + bool includeSummarytasks() const; + long scheduleId() const; + +private: + Project &m_project; + Ui::ICalExportDialog m_ui; + +}; + +} //KPlato namespace + +#endif diff --git a/src/plugins/filters/icalendar/export/ICalExportDialog.ui b/src/plugins/filters/icalendar/export/ICalExportDialog.ui new file mode 100644 index 00000000..b0999594 --- /dev/null +++ b/src/plugins/filters/icalendar/export/ICalExportDialog.ui @@ -0,0 +1,79 @@ + + + Dag Andersen <danders@get2net.dk> + KPlato::ICalExportDialog + + + + 0 + 0 + 333 + 260 + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + Include Project + + + true + + + + + + + Include Summarytasks + + + true + + + + + + + + + + + + + + + + + KPlato::ScheduleTreeView + QTreeView +
kptscheduleeditor.h
+
+
+ + +
diff --git a/src/plugins/filters/icalendar/export/icalendarexport.cpp b/src/plugins/filters/icalendar/export/icalendarexport.cpp index 7062774d..f8a32da1 100644 --- a/src/plugins/filters/icalendar/export/icalendarexport.cpp +++ b/src/plugins/filters/icalendar/export/icalendarexport.cpp @@ -1,400 +1,402 @@ /* This file is part of the KDE project * Copyright (C) 2009, 2011, 2012 Dag Andersen * Copyright (C) 2019 Dag Andersen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // clazy:excludeall=qstring-arg #include "icalendarexport.h" +#include "ICalExportDialog.h" #include "config.h" #include #include #include #include #include #include "kptdebug.h" #if 0 #include #include #include #include #endif #include #include #include #include #include #include #include #include #include using namespace KPlato; K_PLUGIN_FACTORY_WITH_JSON(ICalendarExportFactory, "plan_icalendar_export.json", registerPlugin();) #ifdef HAVE_QDATETIME_KCALCORE #define KQDT QDateTime #else #define KQDT KDateTime #endif ICalendarExport::ICalendarExport(QObject* parent, const QVariantList &) : KoFilter(parent) { } KoFilter::ConversionStatus ICalendarExport::convert(const QByteArray& from, const QByteArray& to) { debugPlan << from << to; if ( ( from != "application/x-vnd.kde.plan" ) || ( to != "text/calendar" ) ) { return KoFilter::NotImplemented; } bool batch = false; if ( m_chain->manager() ) { batch = m_chain->manager()->getBatchMode(); } if ( batch ) { //TODO debugPlan << "batch"; return KoFilter::UsageError; } debugPlan<<"online:"<inputDocument(); MainDocument *doc = dynamic_cast( m_chain->inputDocument() ); if (doc == 0) { errorPlan << "Cannot open Plan document"; return KoFilter::InternalError; } if (m_chain->outputFile().isEmpty()) { errorPlan << "Output filename is empty"; return KoFilter::InternalError; } QFile file(m_chain->outputFile()); if (! file.open(QIODevice::WriteOnly)) { errorPlan << "Failed to open output file:" << file.fileName(); return KoFilter::StorageCreationError; } - + QApplication::restoreOverrideCursor(); + ICalExportDialog dlg(doc->getProject()); + if (dlg.exec() != QDialog::Accepted) { + QApplication::setOverrideCursor(Qt::WaitCursor); + return KoFilter::UserCancelled; + } + QApplication::setOverrideCursor( Qt::WaitCursor ); + m_scheduleId = dlg.scheduleId(); + m_includeProject = dlg.includeProject(); + m_includeSummarytasks = dlg.includeSummarytasks(); KoFilter::ConversionStatus status = convert(doc->getProject(), file); file.close(); //debugPlan << "Finished with status:"< lst = project.allScheduleManagers(); - foreach(const ScheduleManager *m, lst) { - if (! baselined) { - sid = lst.last()->scheduleId(); - //debugPlan<<"last:"<isBaselined()) { - sid = m->scheduleId(); - //debugPlan<<"baselined:"<resources()) { if (r->type() == Resource::Type_Work) { s += QString("ATTENDEE;CN=") + r->name() + "\r\n\t"; s += QString(";RSVP=FALSE;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;") + "\r\n\t"; s += QString("CUTYPE=INDIVIDUAL;") + "\r\n\t"; s += QString("X-UID=") + r->id() + ':' + r->email() + "\r\n"; } } } else { const QList lst = static_cast(node).requestedResources(); foreach(const Resource *r, lst) { if (r->type() == Resource::Type_Work) { s += QString("ATTENDEE;CN=") + r->name() + "\r\n\t"; s += QString(";RSVP=FALSE;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;") + "\r\n\t"; s += QString("CUTYPE=INDIVIDUAL;") + "\r\n\t"; s += QString("X-UID=") + r->id().right(10) + ':' + r->email() + "\r\n"; } } } return s; } QString doAttachment(const Documents &docs) { QString s; foreach(const Document *doc, docs.documents()) { s += QString("ATTACH:") + doc->url().url() + "\r\n"; } return s; } void escape(QString &txt) { txt.replace('\\', "\\\\"); txt.replace('\n', "\\n"); txt.replace(',', "\\,"); txt.replace(':', "\\:"); txt.replace(';', "\\;"); } QString ICalendarExport::doDescription(const QString &description) { QTextEdit te; te.setHtml(description); QString txt = te.toPlainText().trimmed(); QString s; if (!txt.isEmpty()) { s = QString("DESCRIPTION") + QString::number(m_descriptions.count()) + ':' + "\r\n"; escape(txt); m_descriptions << txt; txt = description; txt.remove('\n'); txt.remove('\r'); // in case... escape(txt); s += QString("X-ALT-DESC;FMTTYPE=text/html:") + txt + "\r\n"; } return s; } QString ICalendarExport::createTodo(const Node &node, long sid) { QString s; s += QString("BEGIN:VTODO") + "\r\n"; QString txt = node.name(); escape(txt); s += QString("SUMMARY:") + txt + "\r\n"; s += doDescription(node.description()); s += QString("UID:") + node.id() + "\r\n"; s += QString("DTSTAMP:") + dtToString(QDateTime::currentDateTime()) + "\r\n"; s += QString("CREATED:") + dtToString(QDateTime::currentDateTime()) + "\r\n"; s += QString("LAST-MODIFIED:") + dtToString(QDateTime::currentDateTime()) + "\r\n"; s += QString("CATEGORIES:Plan") + "\r\n"; DateTime dt = node.startTime(sid); if (dt.isValid()) { s += QString("DTSTART:") + dtToString(dt) + "\r\n"; } dt = node.endTime(sid); if (dt.isValid()) { s += QString("DUE:") + dtToString(dt) + "\r\n"; } if (node.parentNode()) { - s += QString("RELATED-TO:") + node.parentNode()->id() + "\r\n"; + if (m_includeSummarytasks && node.parentNode()->type() == Node::Type_Summarytask) { + s += QString("RELATED-TO:") + node.parentNode()->id() + "\r\n"; + } else if (m_includeProject) { + s += QString("RELATED-TO:") + node.projectNode()->id() + "\r\n"; + } } if (node.type() == Node::Type_Task) { s += QString("PERCENT-COMPLETE:") + QString::number(static_cast(node).completion().percentFinished()) + "\r\n"; s += doAttendees(node, sid); } else if (node.type() == Node::Type_Milestone) { s += QString("PERCENT-COMPLETE:") + QString::number(static_cast(node).completion().percentFinished()) + "\r\n"; } else if (node.type() == Node::Type_Project) { if (!node.leader().isEmpty()) { s += QString("ORGANIZER:") + node.leader() + "\r\n"; } } s += doAttachment(node.documents()); s += QString("END:VTODO") + "\r\n"; return s; } QString ICalendarExport::doNode(const Node *node, long sid) { - QString s = createTodo(*node, sid); + QString s; + bool create = true; + if (node->type() == Node::Type_Project) { + create = m_includeProject; + } else if (node->type() == Node::Type_Summarytask) { + create = m_includeSummarytasks; + } + if (create) { + s = createTodo(*node, sid); + } for (int i = 0; i < node->numChildren(); ++i) { s += doNode(node->childNode(i), sid); } return s; } void foldData(QString &data) { int count = 0; // bytecount for (int i = 0; i < data.length() - 6; ++i) { if (data.at(i) == '\r' && data.at(i+1) == '\n') { count = 0; ++i; // skip past LF continue; } if (count >= 70) { data.insert(i, "\r\n\t"); count = 0; i += 2; // skip past CRLFTAB continue; } // we count bytes, so need to know bytesize of character count += QByteArray::fromStdString(QString(data.at(i)).toStdString()).size(); } // remove any empty lines (not allowed) while (data.contains("\r\n\r\n")) { data.replace("\r\n\r\n", "\r\n"); } } KoFilter::ConversionStatus ICalendarExport::convert(const Project &project, QFile &file) { - long sid = scheduleId(project); + long sid = m_scheduleId; QString data = beginCalendar(); data += doNode(&project, sid); data += endCalendar(); foldData(data); for (int i = 0; i < m_descriptions.count(); ++i) { QString rs = QString("DESCRIPTION") + QString::number(i) + ':'; QString s = QString("DESCRIPTION:") + m_descriptions.at(i); foldData(s); data.replace(rs, s); } qint64 n = file.write(data.toUtf8()); if (n < 0) { return KoFilter::InternalError; } return KoFilter::OK; } #if 0 KoFilter::ConversionStatus ICalendarExport::convert(const Project &project, QFile &file) { KCalCore::Calendar::Ptr cal(new KCalCore::MemoryCalendar("UTC")); //TODO: schedule selection dialog long id = ANYSCHEDULED; bool baselined = project.isBaselined(id); QList lst = project.allScheduleManagers(); foreach(const ScheduleManager *m, lst) { if (! baselined) { id = lst.last()->scheduleId(); //debugPlan<<"last:"<isBaselined()) { id = m->scheduleId(); //debugPlan<<"baselined:"<setUid( node->id() ); todo->setSummary(node->name()); todo->setDescription(node->description()); todo->setCategories(QLatin1String("Plan")); if (! node->projectNode()->leader().isEmpty()) { todo->setOrganizer(node->projectNode()->leader()); } if ( node->type() != Node::Type_Project && ! node->leader().isEmpty()) { #if KCALCORE_HAVE_NO_PERSION_PTR KCalCore::Person p = KCalCore::Person::fromFullName(node->leader()); KCalCore::Attendee::Ptr a(new KCalCore::Attendee(p.name(), p.email())); #else KCalCore::Person::Ptr p = KCalCore::Person::fromFullName(node->leader()); KCalCore::Attendee::Ptr a(new KCalCore::Attendee(p->name(), p->email())); #endif a->setRole(KCalCore::Attendee::NonParticipant); todo->addAttendee(a); } DateTime st = node->startTime(id); DateTime et = node->endTime(id); if (st.isValid()) { todo->setDtStart( KQDT( st ) ); } if (et.isValid()) { todo->setDtDue( KQDT( et ) ); } if (node->type() == Node::Type_Task) { const Task *task = qobject_cast(const_cast(node)); Schedule *s = task->schedule(id); if (id < 0 || s == 0) { // Not scheduled, use requests const QList lst = task->requestedResources(); foreach(const Resource *r, lst) { if (r->type() == Resource::Type_Work) { todo->addAttendee(KCalCore::Attendee::Ptr(new KCalCore::Attendee(r->name(), r->email()))); } } } else { foreach(const Resource *r, s->resources()) { if (r->type() == Resource::Type_Work) { todo->addAttendee(KCalCore::Attendee::Ptr(new KCalCore::Attendee(r->name(), r->email()))); } } } } else if (node->type() == Node::Type_Milestone) { const Task *task = qobject_cast(const_cast(node)); todo->setDtStart(KQDT()); todo->setPercentComplete(task->completion().percentFinished()); } foreach(const Document *doc, node->documents().documents()) { todo->addAttachment(KCalCore::Attachment::Ptr(new KCalCore::Attachment(doc->url().url()))); } if (! parent.isNull()) { todo->setRelatedTo(parent->uid(), KCalCore::Incidence::RelTypeParent); } cal->addTodo(todo); foreach(const Node *n, node->childNodeIterator()) { createTodos(cal, n, id, todo); } } #endif #include "icalendarexport.moc" diff --git a/src/plugins/filters/icalendar/export/icalendarexport.h b/src/plugins/filters/icalendar/export/icalendarexport.h index 06b4c95b..c3f27e35 100644 --- a/src/plugins/filters/icalendar/export/icalendarexport.h +++ b/src/plugins/filters/icalendar/export/icalendarexport.h @@ -1,69 +1,72 @@ /* This file is part of the KDE project * Copyright (C) 2009 Dag Andersen * Copyright (C) 2019 Dag Andersen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef ICALENDAREXPORT_H #define ICALENDAREXPORT_H #include #if 0 #include #endif #include #include class QFile; class QByteArray; namespace KPlato { class Project; class Node; } class ICalendarExport : public KoFilter { Q_OBJECT public: ICalendarExport(QObject* parent, const QVariantList &); virtual ~ICalendarExport() {} virtual KoFilter::ConversionStatus convert(const QByteArray& from, const QByteArray& to); protected: KoFilter::ConversionStatus convert(const KPlato::Project &project, QFile &file); #if 0 void createTodos(KCalCore::Calendar::Ptr cal, const KPlato::Node *node, long id, KCalCore::Todo::Ptr parent = KCalCore::Todo::Ptr()); #endif private: QString doNode(const KPlato::Node *node, long sid); QString createTodo(const KPlato::Node &node, long sid); QString doDescription(const QString &description); private: + long m_scheduleId; + bool m_includeProject; + bool m_includeSummarytasks; QList m_descriptions; }; #endif // ICALENDAREXPORT_H