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 }