1 /* This file is part of the KDE project
2  Copyright (C) 2010 Dag Andersen <danders@get2net.dk>
3 
4  This library is free software; you can redistribute it and/or
5  modify it under the terms of the GNU Library General Public
6  License as published by the Free Software Foundation; either
7  version 2 of the License, or (at your option) any later version.
8 
9  This library is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  Library General Public License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB.  If not, write to
16  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  Boston, MA 02110-1301, USA.
18 */
19 
20 // clazy:excludeall=qstring-arg
21 #include "KPlatoXmlLoaderBase.h"
22 
23 #include "kptlocale.h"
24 #include "kptxmlloaderobject.h"
25 #include "kptnode.h"
26 #include "kptproject.h"
27 #include "kpttask.h"
28 #include "kptcalendar.h"
29 #include "kptschedule.h"
30 #include "kptrelation.h"
31 #include "kptresource.h"
32 #include "kptaccount.h"
33 #include "kptappointment.h"
34 
35 #include <KoXmlReader.h>
36 
37 #include <KLocalizedString>
38 
39 #include <QTimeZone>
40 
41 
42 using namespace KPlato;
43 
KPlatoXmlLoaderBase()44 KPlatoXmlLoaderBase::KPlatoXmlLoaderBase()
45 {
46 }
47 
load(Project * project,const KoXmlElement & element,XMLLoaderObject & status)48 bool KPlatoXmlLoaderBase::load(Project *project, const KoXmlElement &element, XMLLoaderObject &status)
49 {
50     debugPlanXml<<"project";
51     // load locale first
52     KoXmlNode n = element.firstChild();
53     for (; ! n.isNull(); n = n.nextSibling()) {
54         if (! n.isElement()) {
55             continue;
56         }
57         KoXmlElement e = n.toElement();
58         if (e.tagName() == "locale") {
59             Locale *l = project->locale();
60             l->setCurrencySymbol(e.attribute("currency-symbol", l->currencySymbol()));
61             //NOTE: decimalplaces missing
62         }
63     }
64     QList<Calendar*> cals;
65     QString s;
66     bool ok = false;
67     project->setName(element.attribute("name"));
68     project->removeId(project->id());
69     project->setId(element.attribute("id"));
70     project->registerNodeId(project);
71     project->setLeader(element.attribute("leader"));
72     project->setDescription(element.attribute("description"));
73     QTimeZone tz(element.attribute("timezone").toLatin1());
74 
75     if (tz.isValid()) {
76         project->setTimeZone(tz);
77     } else warnPlanXml<<"No timezone specified, using default (local)";
78     status.setProjectTimeZone(project->timeZone());
79 
80     // Allow for both numeric and text
81     s = element.attribute("scheduling", "0");
82     project->setConstraint((Node::ConstraintType) (s.toInt(&ok)));
83     if (! ok)
84         project->setConstraint(s);
85     if (project->constraint() != Node::MustStartOn && project->constraint() != Node::MustFinishOn) {
86         errorPlanXml << "Illegal constraint: " << project->constraintToString();
87         project->setConstraint(Node::MustStartOn);
88     }
89     s = element.attribute("start-time");
90     if (! s.isEmpty()) {
91         project->setConstraintStartTime(DateTime::fromString(s, project->timeZone()));
92     }
93     s = element.attribute("end-time");
94     if (! s.isEmpty()) {
95         project->setConstraintEndTime(DateTime::fromString(s, project->timeZone()));
96     }
97     // Load the project children
98     // Do calendars first, they only reference other calendars
99     //debugPlanXml<<"Calendars--->";
100     n = element.firstChild();
101     for (; ! n.isNull(); n = n.nextSibling()) {
102         if (! n.isElement()) {
103             continue;
104         }
105         KoXmlElement e = n.toElement();
106         if (e.tagName() == "calendar") {
107             // Load the calendar.
108             // Referenced by resources
109             Calendar * child = new Calendar();
110             child->setProject(project);
111             if (load(child, e, status)) {
112                 cals.append(child); // temporary, reorder later
113             } else {
114                 // TODO: Complain about this
115                 errorPlanXml << "Failed to load calendar";
116                 delete child;
117             }
118         } else if (e.tagName() == "standard-worktime") {
119             // Load standard worktime
120             StandardWorktime * child = new StandardWorktime();
121             if (load(child, e, status)) {
122                 project->setStandardWorktime(child);
123             } else {
124                 errorPlanXml << "Failed to load standard worktime";
125                 delete child;
126             }
127         }
128     }
129     // calendars references calendars in arbitrary saved order
130     bool added = false;
131     do {
132         added = false;
133         QList<Calendar*> lst;
134         while (!cals.isEmpty()) {
135             Calendar *c = cals.takeFirst();
136             if (c->parentId().isEmpty()) {
137                 project->addCalendar(c, status.baseCalendar()); // handle pre 0.6 version
138                 added = true;
139                 //debugPlanXml<<"added to project:"<<c->name();
140             } else {
141                 Calendar *par = project->calendar(c->parentId());
142                 if (par) {
143                     project->addCalendar(c, par);
144                     added = true;
145                     //debugPlanXml<<"added:"<<c->name()<<" to parent:"<<par->name();
146                 } else {
147                     lst.append(c); // treat later
148                     //debugPlanXml<<"treat later:"<<c->name();
149                 }
150             }
151         }
152         cals = lst;
153     } while (added);
154     if (! cals.isEmpty()) {
155         errorPlanXml<<"All calendars not saved!";
156     }
157     //debugPlanXml<<"Calendars<---";
158     // Resource groups and resources, can reference calendars
159     n = element.firstChild();
160     for (; ! n.isNull(); n = n.nextSibling()) {
161         if (! n.isElement()) {
162             continue;
163         }
164         KoXmlElement e = n.toElement();
165         if (e.tagName() == "resource-group") {
166             // Load the resources
167             // References calendars
168             ResourceGroup * child = new ResourceGroup();
169             if (load(child, e, status)) {
170                 project->addResourceGroup(child);
171             } else {
172                 // TODO: Complain about this
173                 delete child;
174             }
175         }
176     }
177     // The main stuff
178     n = element.firstChild();
179     for (; ! n.isNull(); n = n.nextSibling()) {
180         if (! n.isElement()) {
181             continue;
182         }
183         KoXmlElement e = n.toElement();
184         if (e.tagName() == "project") {
185             //debugPlanXml<<"Sub project--->";
186 /*                // Load the subproject
187             Project * child = new Project(this);
188             if (child->load(e)) {
189                 if (!addTask(child, this)) {
190                     delete child; // TODO: Complain about this
191                 }
192             } else {
193                 // TODO: Complain about this
194                 delete child;
195             }*/
196         } else if (e.tagName() == "task") {
197             //debugPlanXml<<"Task--->";
198             // Load the task (and resourcerequests).
199             // Depends on resources already loaded
200             Task *child = new Task(project);
201             if (load(child, e, status)) {
202                 if (! project->addTask(child, project)) {
203                     delete child; // TODO: Complain about this
204                 }
205             } else {
206                 // TODO: Complain about this
207                 delete child;
208             }
209         }
210     }
211     // These go last
212     n = element.firstChild();
213     for (; ! n.isNull(); n = n.nextSibling()) {
214         debugPlanXml<<n.isElement();
215         if (! n.isElement()) {
216             continue;
217         }
218         KoXmlElement e = n.toElement();
219         if (e.tagName() == "accounts") {
220             //debugPlanXml<<"Accounts--->";
221             // Load accounts
222             // References tasks
223             if (! load(project->accounts(), e, status)) {
224                 errorPlanXml << "Failed to load accounts";
225             }
226         } else if (e.tagName() == "relation") {
227             //debugPlanXml<<"Relation--->";
228             // Load the relation
229             // References tasks
230             Relation * child = new Relation();
231             if (! load(child, e, status)) {
232                 // TODO: Complain about this
233                 errorPlanXml << "Failed to load relation";
234                 delete child;
235             }
236             //debugPlanXml<<"Relation<---";
237         } else if (e.tagName() == "schedules") {
238             debugPlanXml<<"Project schedules & task appointments--->";
239             // References tasks and resources
240             KoXmlNode sn = e.firstChild();
241             for (; ! sn.isNull(); sn = sn.nextSibling()) {
242                 if (! sn.isElement()) {
243                     continue;
244                 }
245                 KoXmlElement el = sn.toElement();
246                 //debugPlanXml<<el.tagName()<<" Version="<<status.version();
247                 ScheduleManager *sm = 0;
248                 bool add = false;
249                 if (status.version() <= "0.5") {
250                     if (el.tagName() == "schedule") {
251                         sm = project->findScheduleManagerByName(el.attribute("name"));
252                         if (sm == 0) {
253                             sm = new ScheduleManager(*project, el.attribute("name"));
254                             add = true;
255                         }
256                     }
257                 } else if (el.tagName() == "plan") {
258                     sm = new ScheduleManager(*project);
259                     add = true;
260                 }
261                 if (sm) {
262                     if (load(sm, el, status)) {
263                         if (add)
264                             project->addScheduleManager(sm);
265                     } else {
266                         errorPlanXml << "Failed to load schedule manager";
267                         delete sm;
268                     }
269                 } else {
270                     debugPlanXml<<"No schedule manager ?!";
271                 }
272             }
273             debugPlanXml<<"Project schedules & task appointments<---";
274         } else if (e.tagName() == "resource-teams") {
275             //debugPlanXml<<"Resource teams--->";
276             // References other resources
277             KoXmlNode tn = e.firstChild();
278             for (; ! tn.isNull(); tn = tn.nextSibling()) {
279                 if (! tn.isElement()) {
280                     continue;
281                 }
282                 KoXmlElement el = tn.toElement();
283                 if (el.tagName() == "team") {
284                     Resource *r = project->findResource(el.attribute("team-id"));
285                     Resource *tm = project->findResource(el.attribute("member-id"));
286                     if (r == 0 || tm == 0) {
287                         errorPlanXml<<"resource-teams: cannot find resources";
288                         continue;
289                     }
290                     if (r == tm) {
291                         errorPlanXml<<"resource-teams: a team cannot be a member of itself";
292                         continue;
293                     }
294                     r->addTeamMemberId(tm->id());
295                 } else {
296                     errorPlanXml<<"resource-teams: unhandled tag"<<el.tagName();
297                 }
298             }
299             //debugPlanXml<<"Resource teams<---";
300         } else if (e.tagName() == "wbs-definition") {
301             load(project->wbsDefinition(), e, status);
302         } else if (e.tagName() == "locale") {
303             // handled earlier
304         } else if (e.tagName() == "resource-group") {
305             // handled earlier
306         } else if (e.tagName() == "calendar") {
307             // handled earlier
308         } else if (e.tagName() == "standard-worktime") {
309             // handled earlier
310         } else if (e.tagName() == "project") {
311             // handled earlier
312         } else if (e.tagName() == "task") {
313             // handled earlier
314         } else {
315             warnPlanXml<<"Unhandled tag:"<<e.tagName();
316         }
317     }
318     // set schedule parent
319     foreach (Schedule *s, project->schedules()) {
320         project->setParentSchedule(s);
321     }
322 
323     debugPlanXml<<"Project loaded:"<<project<<project->name()<<project->allNodes();
324     return true;
325 }
326 
load(Task * task,const KoXmlElement & element,XMLLoaderObject & status)327 bool KPlatoXmlLoaderBase::load(Task *task, const KoXmlElement &element, XMLLoaderObject &status)
328 {
329     debugPlanXml<<"task";
330     QString s;
331     bool ok = false;
332     task->setId(element.attribute("id"));
333 
334     task->setName(element.attribute("name"));
335     task->setLeader(element.attribute("leader"));
336     task->setDescription(element.attribute("description"));
337     //debugPlanXml<<m_name<<": id="<<m_id;
338 
339     // Allow for both numeric and text
340     QString constraint = element.attribute("scheduling","0");
341     task->setConstraint((Node::ConstraintType)constraint.toInt(&ok));
342     if (! ok) {
343         task->setConstraint(constraint);
344     }
345     s = element.attribute("constraint-starttime");
346     if (! s.isEmpty()) {
347         task->setConstraintStartTime(DateTime::fromString(s, status.projectTimeZone()));
348     }
349     s = element.attribute("constraint-endtime");
350     if (! s.isEmpty()) {
351         task->setConstraintEndTime(DateTime::fromString(s, status.projectTimeZone()));
352     }
353     task->setStartupCost(element.attribute("startup-cost", "0.0").toDouble());
354     task->setShutdownCost(element.attribute("shutdown-cost", "0.0").toDouble());
355 
356     // Load the task children
357     KoXmlNode n = element.firstChild();
358     for (; ! n.isNull(); n = n.nextSibling()) {
359         if (! n.isElement()) {
360             continue;
361         }
362         KoXmlElement e = n.toElement();
363         if (e.tagName() == "project") {
364             // Load the subproject
365 /*                Project *child = new Project(this, status);
366             if (child->load(e)) {
367                 if (!project.addSubTask(child, this)) {
368                     delete child;  // TODO: Complain about this
369                 }
370             } else {
371                 // TODO: Complain about this
372                 delete child;
373             }*/
374         } else if (e.tagName() == "task") {
375             // Load the task
376             Task *child = new Task(task);
377             if (load(child, e, status)) {
378                 if (! status.project().addSubTask(child, task)) {
379                     delete child;  // TODO: Complain about this
380                 }
381             } else {
382                 // TODO: Complain about this
383                 delete child;
384             }
385         } else if (e.tagName() == "resource") {
386             // tasks don't have resources
387         } else if (e.tagName() == "estimate" || (/*status.version() < "0.6" &&*/ e.tagName() == "effort")) {
388             //  Load the estimate
389             load(task->estimate(), e, status);
390         } else if (e.tagName() == "resourcegroup-request") {
391             // Load the resource request
392             // Handle multiple requests to same group gracefully (Not really allowed)
393             ResourceGroupRequest *r = task->requests().findGroupRequestById(e.attribute("group-id"));
394             if (r) {
395                 warnPlanXml<<"Multiple requests to same group, loading into existing group";
396                 if (! load(r, e, status)) {
397                     errorPlanXml<<"Failed to load resource request";
398                 }
399             } else {
400                 r = new ResourceGroupRequest();
401                 if (load(r, e, status)) {
402                     task->addRequest(r);
403                 } else {
404                     errorPlanXml<<"Failed to load resource request";
405                     delete r;
406                 }
407             }
408         } else if (e.tagName() == "workpackage") {
409             load(task->workPackage(), e, status);
410         } else if (e.tagName() == "progress") {
411             load(task->completion(), e, status);
412         } else if (e.tagName() == "schedules") {
413             KoXmlNode n = e.firstChild();
414             for (; ! n.isNull(); n = n.nextSibling()) {
415                 if (! n.isElement()) {
416                     continue;
417                 }
418                 KoXmlElement el = n.toElement();
419                 if (el.tagName() == "schedule") {
420                     NodeSchedule *sch = new NodeSchedule();
421                     if (loadNodeSchedule(sch, el, status)) {
422                         sch->setNode(task);
423                         task->addSchedule(sch);
424                     } else {
425                         errorPlanXml<<"Failed to load schedule";
426                         delete sch;
427                     }
428                 }
429             }
430         } else if (e.tagName() == "documents") {
431             load(task->documents(), e, status);
432         } else if (e.tagName() == "workpackage-log") {
433             KoXmlNode n = e.firstChild();
434             for (; ! n.isNull(); n = n.nextSibling()) {
435                 if (! n.isElement()) {
436                     continue;
437                 }
438                 KoXmlElement el = n.toElement();
439                 if (el.tagName() == "workpackage") {
440                     WorkPackage *wp = new WorkPackage(task);
441                     if (loadWpLog(wp, el, status)) {
442                         task->addWorkPackage(wp);
443                     } else {
444                         errorPlanXml<<"Failed to load logged workpackage";
445                         delete wp;
446                     }
447                 }
448             }
449         }
450     }
451     //debugPlanXml<<m_name<<" loaded";
452     return true;
453 }
454 
load(Calendar * calendar,const KoXmlElement & element,XMLLoaderObject & status)455 bool KPlatoXmlLoaderBase::load(Calendar *calendar, const KoXmlElement &element, XMLLoaderObject &status)
456 {
457     debugPlanXml<<"calendar"<<element.text();
458     //bool ok;
459     calendar->setId(element.attribute("id"));
460     calendar->setParentId(element.attribute("parent"));
461     calendar->setName(element.attribute("name",""));
462     QTimeZone tz(element.attribute("timezone").toLatin1());
463     if (tz.isValid()) {
464         calendar->setTimeZone(tz);
465     } else warnPlanXml<<"No timezone specified, use default (local)";
466     bool dc = (bool)element.attribute("default","0").toInt();
467     if (dc) {
468         status.project().setDefaultCalendar(calendar);
469     }
470     KoXmlNode n = element.firstChild();
471     for (; ! n.isNull(); n = n.nextSibling()) {
472         if (! n.isElement()) {
473             continue;
474         }
475         KoXmlElement e = n.toElement();
476         if (e.tagName() == "weekday") {
477             if (! load(calendar->weekdays(), e, status)) {
478                 return false;
479             }
480         }
481         if (e.tagName() == "day") {
482             CalendarDay *day = new CalendarDay();
483             if (load(day, e, status)) {
484                 if (! day->date().isValid()) {
485                     delete day;
486                     errorPlanXml<<calendar->name()<<": Failed to load calendarDay - Invalid date";
487                 } else {
488                     CalendarDay *d = calendar->findDay(day->date());
489                     if (d) {
490                         // already exists, keep the new
491                         delete calendar->takeDay(d);
492                         warnPlanXml<<calendar->name()<<" Load calendarDay - Date already exists";
493                     }
494                     calendar->addDay(day);
495                 }
496             } else {
497                 delete day;
498                 errorPlanXml<<"Failed to load calendarDay";
499                 return true; // don't throw away the whole calendar
500             }
501         }
502     }
503     return true;
504 }
505 
load(CalendarDay * day,const KoXmlElement & element,XMLLoaderObject & status)506 bool KPlatoXmlLoaderBase::load(CalendarDay *day, const KoXmlElement &element, XMLLoaderObject &status)
507 {
508     debugPlanXml<<"day";
509     bool ok=false;
510     day->setState(QString(element.attribute("state", "-1")).toInt(&ok));
511     if (day->state() < 0) {
512         return false;
513     }
514     //debugPlanXml<<" state="<<m_state;
515     QString s = element.attribute("date");
516     if (! s.isEmpty()) {
517         day->setDate(QDate::fromString(s, Qt::ISODate));
518         if (! day->date().isValid()) {
519             day->setDate(QDate::fromString(s));
520         }
521     }
522     day->clearIntervals();
523     KoXmlNode n = element.firstChild();
524     for (; ! n.isNull(); n = n.nextSibling()) {
525         if (! n.isElement()) {
526             continue;
527         }
528         KoXmlElement e = n.toElement();
529         if (e.tagName() == "interval") {
530             //debugPlanXml<<"Interval start="<<e.attribute("start")<<" end="<<e.attribute("end");
531             QString st = e.attribute("start");
532             if (st.isEmpty()) {
533                 errorPlanXml<<"Empty interval";
534                 continue;
535             }
536             QTime start = QTime::fromString(st);
537             int length = 0;
538             if (status.version() <= "0.6.1") {
539                 QString en = e.attribute("end");
540                 if (en.isEmpty()) {
541                     errorPlanXml<<"Invalid interval end";
542                     continue;
543                 }
544                 QTime end = QTime::fromString(en);
545                 length = start.msecsTo(end);
546             } else {
547                 length = e.attribute("length", "0").toInt();
548             }
549             if (length <= 0) {
550                 errorPlanXml<<"Invalid interval length";
551                 continue;
552             }
553             day->addInterval(new TimeInterval(start, length));
554         }
555     }
556     return true;
557 }
558 
load(CalendarWeekdays * weekdays,const KoXmlElement & element,XMLLoaderObject & status)559 bool KPlatoXmlLoaderBase::load(CalendarWeekdays *weekdays, const KoXmlElement& element, XMLLoaderObject& status)
560 {
561     debugPlanXml<<"weekdays";
562     bool ok;
563     int dayNo = QString(element.attribute("day","-1")).toInt(&ok);
564     if (dayNo < 0 || dayNo > 6) {
565         errorPlanXml<<"Illegal weekday: "<<dayNo;
566         return true; // we continue anyway
567     }
568     CalendarDay *day = weekdays->weekday(dayNo + 1);
569     if (day == 0) {
570         errorPlanXml<<"No weekday: "<<dayNo;
571         return false;
572     }
573     if (! load(day, element, status)) {
574         day->setState(CalendarDay::None);
575     }
576     return true;
577 
578 }
579 
load(StandardWorktime * swt,const KoXmlElement & element,XMLLoaderObject & status)580 bool KPlatoXmlLoaderBase::load(StandardWorktime *swt, const KoXmlElement &element, XMLLoaderObject &status)
581 {
582     debugPlanXml<<"swt";
583     swt->setYear(Duration::fromString(element.attribute("year"), Duration::Format_Hour));
584     swt->setMonth(Duration::fromString(element.attribute("month"), Duration::Format_Hour));
585     swt->setWeek(Duration::fromString(element.attribute("week"), Duration::Format_Hour));
586     swt->setDay(Duration::fromString(element.attribute("day"), Duration::Format_Hour));
587 
588     KoXmlNode n = element.firstChild();
589     for (; ! n.isNull(); n = n.nextSibling()) {
590         if (! n.isElement()) {
591             continue;
592         }
593         KoXmlElement e = n.toElement();
594         if (e.tagName() == "calendar") {
595             // pre 0.6 version stored base calendar in standard worktime
596             if (status.version() >= "0.6") {
597                 warnPlanXml<<"Old format, calendar in standard worktime";
598                 warnPlanXml<<"Tries to load anyway";
599             }
600             // try to load anyway
601             Calendar *calendar = new Calendar;
602             if (load(calendar, e, status)) {
603                 status.project().addCalendar(calendar);
604                 calendar->setDefault(true);
605                 status.project().setDefaultCalendar(calendar); // hmmm
606                 status.setBaseCalendar(calendar);
607             } else {
608                 delete calendar;
609                 errorPlanXml<<"Failed to load calendar";
610             }
611         }
612     }
613     return true;
614 }
615 
load(Relation * relation,const KoXmlElement & element,XMLLoaderObject & status)616 bool KPlatoXmlLoaderBase::load(Relation *relation, const KoXmlElement &element, XMLLoaderObject &status)
617 {
618     debugPlanXml<<"relation";
619     relation->setParent(status.project().findNode(element.attribute("parent-id")));
620     if (relation->parent() == 0) {
621         warnPlanXml<<"Parent node == 0, cannot find id:"<<element.attribute("parent-id");
622         return false;
623     }
624     relation->setChild(status.project().findNode(element.attribute("child-id")));
625     if (relation->child() == 0) {
626         warnPlanXml<<"Child node == 0, cannot find id:"<<element.attribute("child-id");
627         return false;
628     }
629     if (relation->child() == relation->parent()) {
630         warnPlanXml<<"Parent node == child node";
631         return false;
632     }
633     if (! relation->parent()->legalToLink(relation->child())) {
634         warnPlanXml<<"Realation is not legal:"<<relation->parent()->name()<<"->"<<relation->child()->name();
635         return false;
636     }
637     relation->setType(element.attribute("type"));
638 
639     relation->setLag(Duration::fromString(element.attribute("lag")));
640 
641     if (! relation->parent()->addDependChildNode(relation)) {
642         errorPlanXml<<"Failed to add relation: Child="<<relation->child()->name()<<" parent="<<relation->parent()->name();
643         return false;
644     }
645     if (! relation->child()->addDependParentNode(relation)) {
646         relation->parent()->takeDependChildNode(relation);
647         errorPlanXml<<"Failed to add relation: Child="<<relation->child()->name()<<" parent="<<relation->parent()->name();
648         return false;
649     }
650     //debugPlanXml<<"Added relation: Child="<<relation->child()->name()<<" parent="<<relation->parent()->name();
651     return true;
652 }
653 
load(ResourceGroup * rg,const KoXmlElement & element,XMLLoaderObject & status)654 bool KPlatoXmlLoaderBase::load(ResourceGroup *rg, const KoXmlElement &element, XMLLoaderObject &status)
655 {
656     debugPlanXml<<"resource-group";
657     rg->setId(element.attribute("id"));
658     rg->setName(element.attribute("name"));
659     rg->setType(element.attribute("type"));
660 
661     KoXmlNode n = element.firstChild();
662     for (; ! n.isNull(); n = n.nextSibling()) {
663         if (! n.isElement()) {
664             continue;
665         }
666         KoXmlElement e = n.toElement();
667         if (e.tagName() == "resource") {
668             // Load the resource
669             Resource *child = new Resource();
670             if (load(child, e, status)) {
671                 status.project().addResource(rg, child);
672             } else {
673                 // TODO: Complain about this
674                 delete child;
675             }
676         }
677     }
678     return true;
679 }
680 
load(Resource * resource,const KoXmlElement & element,XMLLoaderObject & status)681 bool KPlatoXmlLoaderBase::load(Resource *resource, const KoXmlElement &element, XMLLoaderObject &status)
682 {
683     debugPlanXml<<"resource";
684     const Locale *locale = status.project().locale();
685     QString s;
686     resource->setId(element.attribute("id"));
687     resource->setName(element.attribute("name"));
688     resource->setInitials(element.attribute("initials"));
689     resource->setEmail(element.attribute("email"));
690     resource->setType(element.attribute("type"));
691     resource->setCalendar(status.project().findCalendar(element.attribute("calendar-id")));
692     resource->setUnits(element.attribute("units", "100").toInt());
693     s = element.attribute("available-from");
694     if (! s.isEmpty()) {
695         resource->setAvailableFrom(DateTime::fromString(s, status.projectTimeZone()));
696     }
697     s = element.attribute("available-until");
698     if (! s.isEmpty()) {
699         resource->setAvailableUntil(DateTime::fromString(s, status.projectTimeZone()));
700     }
701     resource->setNormalRate(locale->readMoney(element.attribute("normal-rate")));
702     resource->setOvertimeRate(locale->readMoney(element.attribute("overtime-rate")));
703     resource->setAccount(status.project().accounts().findAccount(element.attribute("account")));
704 
705     KoXmlElement e;
706     KoXmlElement parent = element.namedItem("required-resources").toElement();
707     forEachElement(e, parent) {
708         if (e.nodeName() == "resource") {
709             QString id = e.attribute("id");
710             if (id.isEmpty()) {
711                 warnPlanXml<<"Missing resource id";
712                 continue;
713             }
714             resource->addRequiredId(id);
715         }
716     }
717     parent = element.namedItem("external-appointments").toElement();
718     forEachElement(e, parent) {
719         if (e.nodeName() == "project") {
720             QString id = e.attribute("id");
721             if (id.isEmpty()) {
722                 errorPlanXml<<"Missing project id";
723                 continue;
724             }
725             resource->clearExternalAppointments(id); // in case...
726             AppointmentIntervalList lst;
727             load(lst, e, status);
728             Appointment *a = new Appointment();
729             a->setIntervals(lst);
730             a->setAuxcilliaryInfo(e.attribute("name", "Unknown"));
731             resource->addExternalAppointment(id, a);
732         }
733     }
734     return true;
735 }
736 
load(Accounts & accounts,const KoXmlElement & element,XMLLoaderObject & status)737 bool KPlatoXmlLoaderBase::load(Accounts &accounts, const KoXmlElement &element, XMLLoaderObject &status)
738 {
739     debugPlanXml<<"accounts";
740     KoXmlNode n = element.firstChild();
741     for (; ! n.isNull(); n = n.nextSibling()) {
742         if (! n.isElement()) {
743             continue;
744         }
745         KoXmlElement e = n.toElement();
746         if (e.tagName() == "account") {
747             Account *child = new Account();
748             if (load(child, e, status)) {
749                 accounts.insert(child);
750             } else {
751                 // TODO: Complain about this
752                 warnPlanXml<<"Loading failed";
753                 delete child;
754             }
755         }
756     }
757     if (element.hasAttribute("default-account")) {
758         accounts.setDefaultAccount(accounts.findAccount(element.attribute("default-account")));
759         if (accounts.defaultAccount() == 0) {
760             warnPlanXml<<"Could not find default account.";
761         }
762     }
763     return true;
764 }
765 
load(Account * account,const KoXmlElement & element,XMLLoaderObject & status)766 bool KPlatoXmlLoaderBase::load(Account* account, const KoXmlElement& element, XMLLoaderObject& status)
767 {
768     debugPlanXml<<"account";
769     account->setName(element.attribute("name"));
770     account->setDescription(element.attribute("description"));
771     KoXmlNode n = element.firstChild();
772     for (; ! n.isNull(); n = n.nextSibling()) {
773         if (! n.isElement()) {
774             continue;
775         }
776         KoXmlElement e = n.toElement();
777         if (e.tagName() == "costplace") {
778             Account::CostPlace *child = new Account::CostPlace(account);
779             if (load(child, e, status)) {
780                 account->append(child);
781             } else {
782                 delete child;
783             }
784         } else if (e.tagName() == "account") {
785             Account *child = new Account();
786             if (load(child, e, status)) {
787                 account->insert(child);
788             } else {
789                 // TODO: Complain about this
790                 warnPlanXml<<"Loading failed";
791                 delete child;
792             }
793         }
794     }
795     return true;
796 }
797 
load(Account::CostPlace * cp,const KoXmlElement & element,XMLLoaderObject & status)798 bool KPlatoXmlLoaderBase::load(Account::CostPlace* cp, const KoXmlElement& element, XMLLoaderObject& status)
799 {
800     debugPlanXml<<"cost-place";
801     cp->setObjectId(element.attribute("object-id"));
802     if (cp->objectId().isEmpty()) {
803         // check old format
804         cp->setObjectId(element.attribute("node-id"));
805         if (cp->objectId().isEmpty()) {
806             errorPlanXml<<"No object id";
807             return false;
808         }
809     }
810     cp->setNode(status.project().findNode(cp->objectId()));
811     if (cp->node() == 0) {
812         cp->setResource(status.project().findResource(cp->objectId()));
813         if (cp->resource() == 0) {
814             errorPlanXml<<"Cannot find object with id: "<<cp->objectId();
815             return false;
816         }
817     }
818     bool on = (bool)(element.attribute("running-cost").toInt());
819     if (on) cp->setRunning(on);
820     on = (bool)(element.attribute("startup-cost").toInt());
821     if (on) cp->setStartup(on);
822     on = (bool)(element.attribute("shutdown-cost").toInt());
823     if (on) cp->setShutdown(on);
824     return true;
825 }
826 
load(ScheduleManager * manager,const KoXmlElement & element,XMLLoaderObject & status)827 bool KPlatoXmlLoaderBase::load(ScheduleManager *manager, const KoXmlElement &element, XMLLoaderObject &status)
828 {
829     debugPlanXml<<"schedule-manager";
830     MainSchedule *sch = 0;
831     if (status.version() <= "0.5") {
832         manager->setUsePert(false);
833         MainSchedule *sch = loadMainSchedule(manager, element, status);
834         if (sch && sch->type() == Schedule::Expected) {
835             sch->setManager(manager);
836             manager->setExpected(sch);
837         } else {
838             delete sch;
839         }
840         return true;
841     }
842     manager->setName(element.attribute("name"));
843     manager->setManagerId(element.attribute("id"));
844     manager->setUsePert(element.attribute("distribution").toInt() == 1);
845     manager->setAllowOverbooking((bool)(element.attribute("overbooking").toInt()));
846     manager->setCheckExternalAppointments((bool)(element.attribute("check-external-appointments").toInt()));
847     manager->setSchedulingDirection((bool)(element.attribute("scheduling-direction").toInt()));
848     manager->setBaselined((bool)(element.attribute("baselined").toInt()));
849     manager->setSchedulerPluginId(element.attribute("scheduler-plugin-id"));
850     manager->setRecalculate((bool)(element.attribute("recalculate").toInt()));
851     manager->setRecalculateFrom(DateTime::fromString(element.attribute("recalculate-from"), status.projectTimeZone()));
852     KoXmlNode n = element.firstChild();
853     for (; ! n.isNull(); n = n.nextSibling()) {
854         if (! n.isElement()) {
855             continue;
856         }
857         KoXmlElement e = n.toElement();
858         //debugPlanXml<<e.tagName();
859         if (e.tagName() == "schedule") {
860             sch = loadMainSchedule(manager, e, status);
861             if (sch && sch->type() == Schedule::Expected) {
862                 sch->setManager(manager);
863                 manager->setExpected(sch); break;
864             } else {
865                 delete sch;
866             }
867         } else if (e.tagName() == "plan") {
868             ScheduleManager *sm = new ScheduleManager(status.project());
869             if (load(sm, e, status)) {
870                 status.project().addScheduleManager(sm, manager);
871             } else {
872                 errorPlanXml<<"Failed to load schedule manager";
873                 delete sm;
874             }
875         }
876     }
877     return true;
878 }
879 
load(Schedule * schedule,const KoXmlElement & element,XMLLoaderObject &)880 bool KPlatoXmlLoaderBase::load(Schedule *schedule, const KoXmlElement& element, XMLLoaderObject& /*status*/)
881 {
882     debugPlanXml<<"schedule";
883     schedule->setName(element.attribute("name"));
884     schedule->setType(element.attribute("type"));
885     schedule->setId(element.attribute("id").toLong());
886 
887     return true;
888 }
889 
loadMainSchedule(ScheduleManager *,const KoXmlElement & element,XMLLoaderObject & status)890 MainSchedule* KPlatoXmlLoaderBase::loadMainSchedule(ScheduleManager* /*manager*/, const KoXmlElement& element, XMLLoaderObject& status)
891 {
892     debugPlanXml<<"main-schedule";
893     MainSchedule *sch = new MainSchedule();
894     if (loadMainSchedule(sch, element, status)) {
895         status.project().addSchedule(sch);
896         sch->setNode(&(status.project()));
897         status.project().setParentSchedule(sch);
898         // If it's here, it's scheduled!
899         sch->setScheduled(true);
900     } else {
901         errorPlanXml << "Failed to load schedule";
902         delete sch;
903         sch = 0;
904     }
905     return sch;
906 }
907 
loadMainSchedule(MainSchedule * ms,const KoXmlElement & element,XMLLoaderObject & status)908 bool KPlatoXmlLoaderBase::loadMainSchedule(MainSchedule *ms, const KoXmlElement &element, XMLLoaderObject &status)
909 {
910     debugPlanXml;
911     QString s;
912     load(ms, element, status);
913 
914     s = element.attribute("start");
915     if (!s.isEmpty()) {
916         ms->startTime = DateTime::fromString(s, status.projectTimeZone());
917     }
918     s = element.attribute("end");
919     if (!s.isEmpty()) {
920         ms->endTime = DateTime::fromString(s, status.projectTimeZone());
921     }
922     ms->duration = Duration::fromString(element.attribute("duration"));
923     ms->constraintError = element.attribute("scheduling-conflict", "0").toInt();
924 
925     KoXmlNode n = element.firstChild();
926     for (; ! n.isNull(); n = n.nextSibling()) {
927         if (! n.isElement()) {
928             continue;
929         }
930         KoXmlElement el = n.toElement();
931         if (el.tagName() == "appointment") {
932             // Load the appointments.
933             // Resources and tasks must already be loaded
934             Appointment * child = new Appointment();
935             if (! load(child, el, status, *ms)) {
936                 // TODO: Complain about this
937                 errorPlanXml << "Failed to load appointment" << endl;
938                 delete child;
939             }
940         } else if (el.tagName() == "criticalpath-list") {
941             // Tasks must already be loaded
942             for (KoXmlNode n1 = el.firstChild(); ! n1.isNull(); n1 = n1.nextSibling()) {
943                 if (! n1.isElement()) {
944                     continue;
945                 }
946                 KoXmlElement e1 = n1.toElement();
947                 if (e1.tagName() != "criticalpath") {
948                     continue;
949                 }
950                 QList<Node*> lst;
951                 for (KoXmlNode n2 = e1.firstChild(); ! n2.isNull(); n2 = n2.nextSibling()) {
952                     if (! n2.isElement()) {
953                         continue;
954                     }
955                     KoXmlElement e2 = n2.toElement();
956                     if (e2.tagName() != "node") {
957                         continue;
958                     }
959                     QString s = e2.attribute("id");
960                     Node *node = status.project().findNode(s);
961                     if (node) {
962                         lst.append(node);
963                     } else {
964                         errorPlanXml<<"Failed to find node id="<<s;
965                     }
966                 }
967                 ms->m_pathlists.append(lst);
968             }
969             ms->criticalPathListCached = true;
970         }
971     }
972     return true;
973 }
974 
loadNodeSchedule(NodeSchedule * schedule,const KoXmlElement & element,XMLLoaderObject & status)975 bool KPlatoXmlLoaderBase::loadNodeSchedule(NodeSchedule* schedule, const KoXmlElement &element, XMLLoaderObject& status)
976 {
977     debugPlanXml<<"node-schedule";
978     QString s;
979     load(schedule, element, status);
980     s = element.attribute("earlystart");
981     if (s.isEmpty()) { // try version < 0.6
982         s = element.attribute("earlieststart");
983     }
984     if (! s.isEmpty()) {
985         schedule->earlyStart = DateTime::fromString(s, status.projectTimeZone());
986     }
987     s = element.attribute("latefinish");
988     if (s.isEmpty()) { // try version < 0.6
989         s = element.attribute("latestfinish");
990     }
991     if (! s.isEmpty()) {
992         schedule->lateFinish = DateTime::fromString(s, status.projectTimeZone());
993     }
994     s = element.attribute("latestart");
995     if (! s.isEmpty()) {
996         schedule->lateStart = DateTime::fromString(s, status.projectTimeZone());
997     }
998     s = element.attribute("earlyfinish");
999     if (! s.isEmpty()) {
1000         schedule->earlyFinish = DateTime::fromString(s, status.projectTimeZone());
1001     }
1002     s = element.attribute("start");
1003     if (! s.isEmpty())
1004         schedule->startTime = DateTime::fromString(s, status.projectTimeZone());
1005     s = element.attribute("end");
1006     if (!s.isEmpty())
1007         schedule->endTime = DateTime::fromString(s, status.projectTimeZone());
1008     s = element.attribute("start-work");
1009     if (!s.isEmpty())
1010         schedule->workStartTime = DateTime::fromString(s, status.projectTimeZone());
1011     s = element.attribute("end-work");
1012     if (!s.isEmpty())
1013         schedule->workEndTime = DateTime::fromString(s, status.projectTimeZone());
1014     schedule->duration = Duration::fromString(element.attribute("duration"));
1015 
1016     schedule->inCriticalPath = element.attribute("in-critical-path", "0").toInt();
1017     schedule->resourceError = element.attribute("resource-error", "0").toInt();
1018     schedule->resourceOverbooked = element.attribute("resource-overbooked", "0").toInt();
1019     schedule->resourceNotAvailable = element.attribute("resource-not-available", "0").toInt();
1020     schedule->constraintError = element.attribute("scheduling-conflict", "0").toInt();
1021     schedule->notScheduled = element.attribute("not-scheduled", "1").toInt();
1022 
1023     schedule->positiveFloat = Duration::fromString(element.attribute("positive-float"));
1024     schedule->negativeFloat = Duration::fromString(element.attribute("negative-float"));
1025     schedule->freeFloat = Duration::fromString(element.attribute("free-float"));
1026 
1027     return true;
1028 }
1029 
load(WBSDefinition & def,const KoXmlElement & element,XMLLoaderObject &)1030 bool KPlatoXmlLoaderBase::load(WBSDefinition &def, const KoXmlElement &element, XMLLoaderObject &/*status*/)
1031 {
1032     debugPlanXml<<"wbs-def";
1033     def.setProjectCode(element.attribute("project-code"));
1034     def.setProjectSeparator(element.attribute("project-separator"));
1035     def.setLevelsDefEnabled((bool)element.attribute("levels-enabled", "0").toInt());
1036     KoXmlNode n = element.firstChild();
1037     for (; ! n.isNull(); n = n.nextSibling()) {
1038         if (! n.isElement()) {
1039             continue;
1040         }
1041         KoXmlElement e = n.toElement();
1042         if (e.tagName() == "default") {
1043             def.defaultDef().code = e.attribute("code", "Number");
1044             def.defaultDef().separator = e.attribute("separator", ".");
1045         } else if (e.tagName() == "levels") {
1046             KoXmlNode n = e.firstChild();
1047             for (; ! n.isNull(); n = n.nextSibling()) {
1048                 if (! n.isElement()) {
1049                     continue;
1050                 }
1051                 KoXmlElement el = n.toElement();
1052                 WBSDefinition::CodeDef d;
1053                 d.code = el.attribute("code");
1054                 d.separator = el.attribute("separator");
1055                 int lvl = el.attribute("level", "-1").toInt();
1056                 if (lvl >= 0) {
1057                     def.setLevelsDef(lvl, d);
1058                 } else errorPlanXml<<"Invalid levels definition";
1059             }
1060         }
1061     }
1062     return true;
1063 }
1064 
load(Documents & documents,const KoXmlElement & element,XMLLoaderObject & status)1065 bool KPlatoXmlLoaderBase::load(Documents &documents, const KoXmlElement &element, XMLLoaderObject &status)
1066 {
1067     debugPlanXml<<"documents";
1068     KoXmlNode n = element.firstChild();
1069     for (; ! n.isNull(); n = n.nextSibling()) {
1070         if (! n.isElement()) {
1071             continue;
1072         }
1073         KoXmlElement e = n.toElement();
1074         if (e.tagName() == "document") {
1075             Document *doc = new Document();
1076             if (! load(doc, e, status)) {
1077                 warnPlanXml<<"Failed to load document";
1078                 status.addMsg(XMLLoaderObject::Errors, "Failed to load document");
1079                 delete doc;
1080             } else {
1081                 documents.addDocument(doc);
1082                 status.addMsg(i18n("Document loaded, URL=%1",  doc->url().url()));
1083             }
1084         }
1085     }
1086     return true;
1087 }
1088 
load(Document * document,const KoXmlElement & element,XMLLoaderObject & status)1089 bool KPlatoXmlLoaderBase::load(Document *document, const KoXmlElement &element, XMLLoaderObject &status)
1090 {
1091     debugPlanXml<<"document";
1092     Q_UNUSED(status);
1093     document->setUrl(QUrl(element.attribute("url")));
1094     document->setType((Document::Type)(element.attribute("type").toInt()));
1095     document->setStatus(element.attribute("status"));
1096     document->setSendAs((Document::SendAs)(element.attribute("sendas").toInt()));
1097     return true;
1098 }
1099 
load(Estimate * estimate,const KoXmlElement & element,XMLLoaderObject & status)1100 bool KPlatoXmlLoaderBase::load(Estimate* estimate, const KoXmlElement& element, XMLLoaderObject& status)
1101 {
1102     debugPlanXml<<"estimate";
1103     estimate->setType(element.attribute("type"));
1104     estimate->setRisktype(element.attribute("risk"));
1105     if (status.version() <= "0.6") {
1106         estimate->setUnit((Duration::Unit)(element.attribute("display-unit", QString().number(Duration::Unit_h)).toInt()));
1107         QList<qint64> s = status.project().standardWorktime()->scales();
1108         estimate->setExpectedEstimate(Estimate::scale(Duration::fromString(element.attribute("expected")), estimate->unit(), s));
1109         estimate->setOptimisticEstimate(Estimate::scale(Duration::fromString(element.attribute("optimistic")), estimate->unit(), s));
1110         estimate->setPessimisticEstimate(Estimate::scale(Duration::fromString(element.attribute("pessimistic")), estimate->unit(), s));
1111     } else {
1112         if (status.version() <= "0.6.2") {
1113             // 0 pos in unit is now Unit_Y, so add 3 to get the correct new unit
1114             estimate->setUnit((Duration::Unit)(element.attribute("unit", QString().number(Duration::Unit_ms - 3)).toInt() + 3));
1115         } else {
1116             estimate->setUnit(Duration::unitFromString(element.attribute("unit")));
1117         }
1118         estimate->setExpectedEstimate(element.attribute("expected", "0.0").toDouble());
1119         estimate->setOptimisticEstimate(element.attribute("optimistic", "0.0").toDouble());
1120         estimate->setPessimisticEstimate(element.attribute("pessimistic", "0.0").toDouble());
1121 
1122         estimate->setCalendar(status.project().findCalendar(element.attribute("calendar-id")));
1123     }
1124     return true;
1125 }
1126 
load(ResourceGroupRequest * gr,const KoXmlElement & element,XMLLoaderObject & status)1127 bool KPlatoXmlLoaderBase::load(ResourceGroupRequest* gr, const KoXmlElement& element, XMLLoaderObject& status)
1128 {
1129     debugPlanXml<<"resourcegroup-request";
1130     gr->setGroup(status.project().findResourceGroup(element.attribute("group-id")));
1131     if (gr->group() == 0) {
1132         errorPlanXml<<"The referenced resource group does not exist: group id="<<element.attribute("group-id");
1133         return false;
1134     }
1135     gr->group()->registerRequest(gr);
1136 
1137 
1138     KoXmlNode n = element.firstChild();
1139     for (; ! n.isNull(); n = n.nextSibling()) {
1140         if (! n.isElement()) {
1141             continue;
1142         }
1143         KoXmlElement e = n.toElement();
1144         if (e.tagName() == "resource-request") {
1145             ResourceRequest *r = new ResourceRequest();
1146             if (load(r, e, status)) {
1147                 gr->addResourceRequest(r);
1148             } else {
1149                 errorPlanXml<<"Failed to load resource request";
1150                 delete r;
1151             }
1152         }
1153     }
1154     // meaning of m_units changed
1155     int x = element.attribute("units").toInt() -gr->count();
1156     gr->setUnits(x > 0 ? x : 0);
1157 
1158     return true;
1159 }
1160 
load(ResourceRequest * rr,const KoXmlElement & element,XMLLoaderObject & status)1161 bool KPlatoXmlLoaderBase::load(ResourceRequest *rr, const KoXmlElement& element, XMLLoaderObject& status)
1162 {
1163     debugPlanXml<<"resource-request";
1164     rr->setResource(status.project().resource(element.attribute("resource-id")));
1165     if (rr->resource() == 0) {
1166         warnPlanXml<<"The referenced resource does not exist: resource id="<<element.attribute("resource-id");
1167         return false;
1168     }
1169     rr->setUnits(element.attribute("units").toInt());
1170 
1171     KoXmlElement parent = element.namedItem("required-resources").toElement();
1172     KoXmlElement e;
1173     QList<Resource*> required;
1174     forEachElement(e, parent) {
1175         if (e.nodeName() == "resource") {
1176             QString id = e.attribute("id");
1177             if (id.isEmpty()) {
1178                 errorPlanXml<<"Missing project id";
1179                 continue;
1180             }
1181             Resource *r = status.project().resource(id);
1182             if (r == 0) {
1183                 warnPlanXml<<"The referenced resource does not exist: resource id="<<element.attribute("resource-id");
1184             } else {
1185                 if (r != rr->resource()) {
1186                     required << r;
1187                 }
1188             }
1189         }
1190     }
1191     rr->setRequiredResources(required);
1192     return true;
1193 
1194 }
1195 
load(WorkPackage & wp,const KoXmlElement & element,XMLLoaderObject & status)1196 bool KPlatoXmlLoaderBase::load(WorkPackage &wp, const KoXmlElement& element, XMLLoaderObject& status)
1197 {
1198     debugPlanXml<<"workpackage";
1199     Q_UNUSED(status);
1200     wp.setOwnerName(element.attribute("owner"));
1201     wp.setOwnerId(element.attribute("owner-id"));
1202     return true;
1203 }
1204 
loadWpLog(WorkPackage * wp,KoXmlElement & element,XMLLoaderObject & status)1205 bool KPlatoXmlLoaderBase::loadWpLog(WorkPackage *wp, KoXmlElement& element, XMLLoaderObject& status)
1206 {
1207     debugPlanXml<<"wplog";
1208     wp->setOwnerName(element.attribute("owner"));
1209     wp->setOwnerId(element.attribute("owner-id"));
1210     wp->setTransmitionStatus(wp->transmitionStatusFromString(element.attribute("status")));
1211     wp->setTransmitionTime(DateTime(QDateTime::fromString(element.attribute("time"), Qt::ISODate)));
1212     return load(wp->completion(), element, status);
1213 }
1214 
load(Completion & completion,const KoXmlElement & element,XMLLoaderObject & status)1215 bool KPlatoXmlLoaderBase::load(Completion &completion, const KoXmlElement& element, XMLLoaderObject& status)
1216 {
1217     debugPlanXml<<"completion";
1218     QString s;
1219     completion.setStarted((bool)element.attribute("started", "0").toInt());
1220     completion.setFinished((bool)element.attribute("finished", "0").toInt());
1221     s = element.attribute("startTime");
1222     if (!s.isEmpty()) {
1223         completion.setStartTime(DateTime::fromString(s, status.projectTimeZone()));
1224     }
1225     s = element.attribute("finishTime");
1226     if (!s.isEmpty()) {
1227         completion.setFinishTime(DateTime::fromString(s, status.projectTimeZone()));
1228     }
1229     completion.setEntrymode(element.attribute("entrymode"));
1230     if (status.version() < "0.6") {
1231         if (completion.isStarted()) {
1232             Completion::Entry *entry = new Completion::Entry(element.attribute("percent-finished", "0").toInt(), Duration::fromString(element.attribute("remaining-effort")),  Duration::fromString(element.attribute("performed-effort")));
1233             entry->note = element.attribute("note");
1234             QDate date = completion.startTime().date();
1235             if (completion.isFinished()) {
1236                 date = completion.finishTime().date();
1237             }
1238             // almost the best we can do ;)
1239             completion.addEntry(date, entry);
1240         }
1241     } else {
1242         KoXmlElement e;
1243         forEachElement(e, element) {
1244                 if (e.tagName() == "completion-entry") {
1245                     QDate date;
1246                     s = e.attribute("date");
1247                     if (!s.isEmpty()) {
1248                         date = QDate::fromString(s, Qt::ISODate);
1249                     }
1250                     if (!date.isValid()) {
1251                         warnPlanXml<<"Invalid date: "<<date<<s;
1252                         continue;
1253                     }
1254                     Completion::Entry *entry = new Completion::Entry(e.attribute("percent-finished", "0").toInt(), Duration::fromString(e.attribute("remaining-effort")),  Duration::fromString(e.attribute("performed-effort")));
1255                     completion.addEntry(date, entry);
1256                 } else if (e.tagName() == "used-effort") {
1257                     KoXmlElement el;
1258                     forEachElement(el, e) {
1259                             if (el.tagName() == "resource") {
1260                                 QString id = el.attribute("id");
1261                                 Resource *r = status.project().resource(id);
1262                                 if (r == 0) {
1263                                     warnPlanXml<<"Cannot find resource, id="<<id;
1264                                     continue;
1265                                 }
1266                                 Completion::UsedEffort *ue = new Completion::UsedEffort();
1267                                 completion.addUsedEffort(r, ue);
1268                                 load(ue, el, status);
1269                             }
1270                     }
1271                 }
1272         }
1273     }
1274     return true;
1275 }
1276 
load(Completion::UsedEffort * ue,const KoXmlElement & element,XMLLoaderObject &)1277 bool KPlatoXmlLoaderBase::load(Completion::UsedEffort* ue, const KoXmlElement& element, XMLLoaderObject& /*status*/)
1278 {
1279     debugPlanXml<<"used-effort";
1280     KoXmlElement e;
1281     forEachElement(e, element) {
1282         if (e.tagName() == "actual-effort") {
1283             QDate date = QDate::fromString(e.attribute("date"), Qt::ISODate);
1284             if (date.isValid()) {
1285                 Completion::UsedEffort::ActualEffort a;
1286                 a.setNormalEffort(Duration::fromString(e.attribute("normal-effort")));
1287                 a.setOvertimeEffort(Duration::fromString(e.attribute("overtime-effort")));
1288                 ue->setEffort(date, a);
1289             }
1290         }
1291     }
1292     return true;
1293 }
1294 
load(Appointment * appointment,const KoXmlElement & element,XMLLoaderObject & status,Schedule & sch)1295 bool KPlatoXmlLoaderBase::load(Appointment *appointment, const KoXmlElement& element, XMLLoaderObject& status, Schedule &sch)
1296 {
1297     debugPlanXml<<"appointment";
1298     Node *node = status.project().findNode(element.attribute("task-id"));
1299     if (node == 0) {
1300         errorPlanXml<<"The referenced task does not exists: "<<element.attribute("task-id");
1301         return false;
1302     }
1303     Resource *res = status.project().resource(element.attribute("resource-id"));
1304     if (res == 0) {
1305         errorPlanXml<<"The referenced resource does not exists: resource id="<<element.attribute("resource-id");
1306         return false;
1307     }
1308     if (!res->addAppointment(appointment, sch)) {
1309         errorPlanXml<<"Failed to add appointment to resource: "<<res->name();
1310         return false;
1311     }
1312     if (! node->addAppointment(appointment, sch)) {
1313         errorPlanXml<<"Failed to add appointment to node: "<<node->name();
1314         appointment->resource()->takeAppointment(appointment);
1315         return false;
1316     }
1317     //debugPlanXml<<"res="<<m_resource<<" node="<<m_node;
1318     AppointmentIntervalList lst = appointment->intervals();
1319     load(lst, element, status);
1320     if (lst.isEmpty()) {
1321         errorPlanXml<<"Appointment interval list is empty (added anyway): "<<node->name()<<res->name();
1322         return false;
1323     }
1324     appointment->setIntervals(lst);
1325     return true;
1326 }
1327 
load(AppointmentIntervalList & lst,const KoXmlElement & element,XMLLoaderObject & status)1328 bool KPlatoXmlLoaderBase::load(AppointmentIntervalList& lst, const KoXmlElement& element, XMLLoaderObject& status)
1329 {
1330     debugPlanXml<<"appointment-interval-list";
1331     KoXmlElement e;
1332     forEachElement(e, element) {
1333         if (e.tagName() == "interval") {
1334             AppointmentInterval a;
1335             if (load(a, e, status)) {
1336                 lst.add(a);
1337             } else {
1338                 errorPlanXml<<"Could not load interval";
1339             }
1340         }
1341     }
1342     return true;
1343 }
1344 
load(AppointmentInterval & interval,const KoXmlElement & element,XMLLoaderObject & status)1345 bool KPlatoXmlLoaderBase::load(AppointmentInterval& interval, const KoXmlElement& element, XMLLoaderObject& status)
1346 {
1347     bool ok;
1348     QString s = element.attribute("start");
1349     if (!s.isEmpty()) {
1350         interval.setStartTime(DateTime::fromString(s, status.projectTimeZone()));
1351     }
1352     s = element.attribute("end");
1353     if (!s.isEmpty()) {
1354         interval.setEndTime(DateTime::fromString(s, status.projectTimeZone()));
1355     }
1356     double l = element.attribute("load", "100").toDouble(&ok);
1357     if (ok) {
1358         interval.setLoad(l);
1359     }
1360     debugPlanXml<<"interval:"<<interval;
1361     return interval.isValid();
1362 }
1363