File indexing completed on 2024-11-24 04:44:16

0001 /*
0002     This file is part of the kolab resource - the implementation of the
0003     Kolab storage format. See www.kolab.org for documentation on this.
0004 
0005     SPDX-FileCopyrightText: 2004 Bo Thorsen <bo@sonofthor.dk>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include "task.h"
0011 #include "pimkolab_debug.h"
0012 
0013 using namespace KolabV2;
0014 
0015 KCalendarCore::Todo::Ptr Task::fromXml(const QDomDocument &xmlDoc, const QString &tz)
0016 {
0017     Task task(tz);
0018     task.loadXML(xmlDoc);
0019     KCalendarCore::Todo::Ptr todo(new KCalendarCore::Todo());
0020     task.saveTo(todo);
0021     return todo;
0022 }
0023 
0024 QString Task::taskToXML(const KCalendarCore::Todo::Ptr &todo, const QString &tz)
0025 {
0026     Task task(tz, todo);
0027     return task.saveXML();
0028 }
0029 
0030 Task::Task(const QString &tz, const KCalendarCore::Todo::Ptr &task)
0031     : Incidence(tz, task)
0032     , mPercentCompleted(0)
0033     , mStatus(KCalendarCore::Incidence::StatusNone)
0034 {
0035     if (task) {
0036         setFields(task);
0037     }
0038 }
0039 
0040 Task::~Task() = default;
0041 
0042 void Task::setPercentCompleted(int percent)
0043 {
0044     mPercentCompleted = percent;
0045 }
0046 
0047 int Task::percentCompleted() const
0048 {
0049     return mPercentCompleted;
0050 }
0051 
0052 void Task::setStatus(KCalendarCore::Incidence::Status status)
0053 {
0054     mStatus = status;
0055 }
0056 
0057 KCalendarCore::Incidence::Status Task::status() const
0058 {
0059     return mStatus;
0060 }
0061 
0062 void Task::setParent(const QString &parentUid)
0063 {
0064     mParent = parentUid;
0065 }
0066 
0067 QString Task::parent() const
0068 {
0069     return mParent;
0070 }
0071 
0072 void Task::setDueDate(const QDateTime &date)
0073 {
0074     mDueDate = date;
0075     mHasDueDate = true;
0076 }
0077 
0078 void Task::setDueDate(const QDate &date)
0079 {
0080     mDueDate = QDateTime(date, QTime());
0081     mHasDueDate = true;
0082     mFloatingStatus = AllDay;
0083 }
0084 
0085 void Task::setDueDate(const QString &date)
0086 {
0087     if (date.length() > 10) {
0088         // This is a date + time
0089         setDueDate(stringToDateTime(date));
0090     } else {
0091         // This is only a date
0092         setDueDate(stringToDate(date));
0093     }
0094 }
0095 
0096 QDateTime Task::dueDate() const
0097 {
0098     return mDueDate;
0099 }
0100 
0101 void Task::setHasStartDate(bool v)
0102 {
0103     mHasStartDate = v;
0104 }
0105 
0106 bool Task::hasStartDate() const
0107 {
0108     return mHasStartDate;
0109 }
0110 
0111 bool Task::hasDueDate() const
0112 {
0113     return mHasDueDate;
0114 }
0115 
0116 void Task::setCompletedDate(const QDateTime &date)
0117 {
0118     mCompletedDate = date;
0119     mHasCompletedDate = true;
0120 }
0121 
0122 QDateTime Task::completedDate() const
0123 {
0124     return mCompletedDate;
0125 }
0126 
0127 bool Task::hasCompletedDate() const
0128 {
0129     return mHasCompletedDate;
0130 }
0131 
0132 bool Task::loadAttribute(QDomElement &element)
0133 {
0134     QString tagName = element.tagName();
0135 
0136     if (tagName == QLatin1StringView("completed")) {
0137         bool ok;
0138         int percent = element.text().toInt(&ok);
0139         if (!ok || percent < 0 || percent > 100) {
0140             percent = 0;
0141         }
0142         setPercentCompleted(percent);
0143     } else if (tagName == QLatin1StringView("status")) {
0144         if (element.text() == QLatin1StringView("in-progress")) {
0145             setStatus(KCalendarCore::Incidence::StatusInProcess);
0146         } else if (element.text() == QLatin1StringView("completed")) {
0147             setStatus(KCalendarCore::Incidence::StatusCompleted);
0148         } else if (element.text() == QLatin1StringView("waiting-on-someone-else")) {
0149             setStatus(KCalendarCore::Incidence::StatusNeedsAction);
0150         } else if (element.text() == QLatin1StringView("deferred")) {
0151             // Guessing a status here
0152             setStatus(KCalendarCore::Incidence::StatusCanceled);
0153         } else {
0154             // Default
0155             setStatus(KCalendarCore::Incidence::StatusNone);
0156         }
0157     } else if (tagName == QLatin1StringView("due-date")) {
0158         setDueDate(element.text());
0159     } else if (tagName == QLatin1StringView("parent")) {
0160         setParent(element.text());
0161     } else if (tagName == QLatin1StringView("x-completed-date")) {
0162         setCompletedDate(stringToDateTime(element.text()));
0163     } else if (tagName == QLatin1StringView("start-date")) {
0164         setHasStartDate(true);
0165         setStartDate(element.text());
0166     } else {
0167         return Incidence::loadAttribute(element);
0168     }
0169 
0170     // We handled this
0171     return true;
0172 }
0173 
0174 bool Task::saveAttributes(QDomElement &element) const
0175 {
0176     // Save the base class elements
0177     Incidence::saveAttributes(element);
0178 
0179     writeString(element, QStringLiteral("completed"), QString::number(percentCompleted()));
0180 
0181     switch (status()) {
0182     case KCalendarCore::Incidence::StatusInProcess:
0183         writeString(element, QStringLiteral("status"), QStringLiteral("in-progress"));
0184         break;
0185     case KCalendarCore::Incidence::StatusCompleted:
0186         writeString(element, QStringLiteral("status"), QStringLiteral("completed"));
0187         break;
0188     case KCalendarCore::Incidence::StatusNeedsAction:
0189         writeString(element, QStringLiteral("status"), QStringLiteral("waiting-on-someone-else"));
0190         break;
0191     case KCalendarCore::Incidence::StatusCanceled:
0192         writeString(element, QStringLiteral("status"), QStringLiteral("deferred"));
0193         break;
0194     case KCalendarCore::Incidence::StatusNone:
0195         writeString(element, QStringLiteral("status"), QStringLiteral("not-started"));
0196         break;
0197     case KCalendarCore::Incidence::StatusTentative:
0198     case KCalendarCore::Incidence::StatusConfirmed:
0199     case KCalendarCore::Incidence::StatusDraft:
0200     case KCalendarCore::Incidence::StatusFinal:
0201     case KCalendarCore::Incidence::StatusX:
0202         // All of these are saved as StatusNone.
0203         writeString(element, QStringLiteral("status"), QStringLiteral("not-started"));
0204         break;
0205     }
0206 
0207     if (hasDueDate()) {
0208         if (mFloatingStatus == HasTime) {
0209             writeString(element, QStringLiteral("due-date"), dateTimeToString(dueDate()));
0210         } else {
0211             writeString(element, QStringLiteral("due-date"), dateToString(dueDate().date()));
0212         }
0213     }
0214 
0215     if (!parent().isNull()) {
0216         writeString(element, QStringLiteral("parent"), parent());
0217     }
0218 
0219     if (hasCompletedDate() && percentCompleted() == 100) {
0220         writeString(element, QStringLiteral("x-completed-date"), dateTimeToString(completedDate()));
0221     }
0222 
0223     return true;
0224 }
0225 
0226 bool Task::loadXML(const QDomDocument &document)
0227 {
0228     QDomElement top = document.documentElement();
0229 
0230     if (top.tagName() != QLatin1StringView("task")) {
0231         qCWarning(PIMKOLAB_LOG) << QStringLiteral("XML error: Top tag was %1 instead of the expected task").arg(top.tagName());
0232         return false;
0233     }
0234     setHasStartDate(false); // todo's don't necessarily have one
0235 
0236     for (QDomNode n = top.firstChild(); !n.isNull(); n = n.nextSibling()) {
0237         if (n.isComment()) {
0238             continue;
0239         }
0240         if (n.isElement()) {
0241             QDomElement e = n.toElement();
0242             if (!loadAttribute(e)) {
0243                 // TODO: Unhandled tag - save for later storage
0244                 qCDebug(PIMKOLAB_LOG) << "Warning: Unhandled tag" << e.tagName();
0245             }
0246         } else {
0247             qCDebug(PIMKOLAB_LOG) << "Node is not a comment or an element???";
0248         }
0249     }
0250 
0251     return true;
0252 }
0253 
0254 QString Task::saveXML() const
0255 {
0256     QDomDocument document = domTree();
0257     QDomElement element = document.createElement(QStringLiteral("task"));
0258     element.setAttribute(QStringLiteral("version"), QStringLiteral("1.0"));
0259     saveAttributes(element);
0260     if (!hasStartDate() && startDate().isValid()) {
0261         // events and journals always have a start date, but tasks don't.
0262         // Remove the entry done by the inherited save above, because we
0263         // don't have one.
0264         QDomNodeList l = element.elementsByTagName(QStringLiteral("start-date"));
0265         Q_ASSERT(l.count() == 1);
0266         element.removeChild(l.item(0));
0267     }
0268     document.appendChild(element);
0269     return document.toString();
0270 }
0271 
0272 void Task::setFields(const KCalendarCore::Todo::Ptr &task)
0273 {
0274     Incidence::setFields(task);
0275 
0276     setPercentCompleted(task->percentComplete());
0277     setStatus(task->status());
0278     setHasStartDate(task->hasStartDate());
0279 
0280     if (task->hasDueDate()) {
0281         if (task->allDay()) {
0282             // This is a floating task. Don't timezone move this one
0283             mFloatingStatus = AllDay;
0284             setDueDate(task->dtDue().date());
0285         } else {
0286             mFloatingStatus = HasTime;
0287             setDueDate(localToUTC(task->dtDue()));
0288         }
0289     } else {
0290         mHasDueDate = false;
0291     }
0292 
0293     if (!task->relatedTo().isEmpty()) {
0294         setParent(task->relatedTo());
0295     } else {
0296         setParent(QString());
0297     }
0298 
0299     if (task->hasCompletedDate() && task->percentComplete() == 100) {
0300         setCompletedDate(localToUTC(task->completed()));
0301     } else {
0302         mHasCompletedDate = false;
0303     }
0304 }
0305 
0306 void Task::saveTo(const KCalendarCore::Todo::Ptr &task)
0307 {
0308     Incidence::saveTo(task);
0309 
0310     task->setPercentComplete(percentCompleted());
0311     task->setStatus(status());
0312     // PORT KF6 task->setHasStartDate( hasStartDate() );
0313     // PORT KF6 task->setHasDueDate( hasDueDate() );
0314     if (hasDueDate()) {
0315         task->setDtDue(utcToLocal(dueDate()));
0316     }
0317 
0318     if (!parent().isEmpty()) {
0319         task->setRelatedTo(parent());
0320     }
0321 
0322     if (hasCompletedDate() && task->percentComplete() == 100) {
0323         task->setCompleted(utcToLocal(mCompletedDate));
0324     }
0325 }