1 /* This file is part of the KDE project
2    Copyright (C) 1998, 1999, 2000 Torben Weis <weis@kde.org>
3    Copyright (C) 2004 David Faure <faure@kde.org>
4 
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public
7    License as published by the Free Software Foundation; either
8    version 2 of the License, or (at your option) any later version.
9 
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14 
15    You should have received a copy of the GNU Library General Public License
16    along with this library; see the file COPYING.LIB.  If not, write to
17    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19 */
20 
21 // clazy:excludeall=qstring-arg
22 #include "KoDocumentInfo.h"
23 
24 #include "config.h"
25 #include "KoDocumentBase.h"
26 #include "KoOdfWriteStore.h"
27 #include "KoXmlNS.h"
28 
29 #include <QDateTime>
30 #include <KoStoreDevice.h>
31 #include <KoXmlWriter.h>
32 
33 #include <kconfig.h>
34 #include <kconfiggroup.h>
35 #include <OdfDebug.h>
36 #include <klocalizedstring.h>
37 #include <kuser.h>
38 #include <kemailsettings.h>
39 
40 
KoDocumentInfo(QObject * parent)41 KoDocumentInfo::KoDocumentInfo(QObject *parent) : QObject(parent)
42 {
43     m_aboutTags << "title" << "description" << "subject" << "comments"
44     << "keyword" << "initial-creator" << "editing-cycles"
45     << "date" << "creation-date" << "language";
46 
47     m_authorTags << "creator" << "initial" << "author-title"
48     << "email" << "telephone" << "telephone-work"
49     << "fax" << "country" << "postal-code" << "city"
50     << "street" << "position" << "company";
51 
52     setAboutInfo("editing-cycles", "0");
53     setAboutInfo("initial-creator", i18n("Unknown"));
54     setAboutInfo("creation-date", QDateTime::currentDateTime()
55                  .toString(Qt::ISODate));
56 }
57 
~KoDocumentInfo()58 KoDocumentInfo::~KoDocumentInfo()
59 {
60 }
61 
load(const KoXmlDocument & doc)62 bool KoDocumentInfo::load(const KoXmlDocument &doc)
63 {
64     m_authorInfo.clear();
65 
66     if (!loadAboutInfo(doc.documentElement()))
67         return false;
68 
69     if (!loadAuthorInfo(doc.documentElement()))
70         return false;
71 
72     return true;
73 }
74 
loadOasis(const KoXmlDocument & metaDoc)75 bool KoDocumentInfo::loadOasis(const KoXmlDocument &metaDoc)
76 {
77     m_authorInfo.clear();
78 
79     KoXmlNode t = KoXml::namedItemNS(metaDoc, KoXmlNS::office, "document-meta");
80     KoXmlNode office = KoXml::namedItemNS(t, KoXmlNS::office, "meta");
81 
82     if (office.isNull())
83         return false;
84 
85     if (!loadOasisAboutInfo(office))
86         return false;
87 
88     if (!loadOasisAuthorInfo(office))
89         return false;
90 
91     return true;
92 }
93 
save(QDomDocument & doc)94 QDomDocument KoDocumentInfo::save(QDomDocument &doc)
95 {
96     updateParametersAndBumpNumCycles();
97 
98     QDomElement s = saveAboutInfo(doc);
99     if (!s.isNull())
100         doc.documentElement().appendChild(s);
101 
102     s = saveAuthorInfo(doc);
103     if (!s.isNull())
104         doc.documentElement().appendChild(s);
105 
106 
107     if (doc.documentElement().isNull())
108         return QDomDocument();
109 
110     return doc;
111 }
112 
saveOasis(KoStore * store)113 bool KoDocumentInfo::saveOasis(KoStore *store)
114 {
115     updateParametersAndBumpNumCycles();
116 
117     KoStoreDevice dev(store);
118     KoXmlWriter* xmlWriter = KoOdfWriteStore::createOasisXmlWriter(&dev,
119                              "office:document-meta");
120     xmlWriter->startElement("office:meta");
121 
122     xmlWriter->startElement("meta:generator");
123     xmlWriter->addTextNode(QString("Calligra Plan/%1")
124                            .arg(PLAN_VERSION_STRING));
125     xmlWriter->endElement();
126 
127     if (!saveOasisAboutInfo(*xmlWriter))
128         return false;
129     if (!saveOasisAuthorInfo(*xmlWriter))
130         return false;
131 
132     xmlWriter->endElement();
133     xmlWriter->endElement(); // root element
134     xmlWriter->endDocument();
135     delete xmlWriter;
136     return true;
137 }
138 
setAuthorInfo(const QString & info,const QString & data)139 void KoDocumentInfo::setAuthorInfo(const QString &info, const QString &data)
140 {
141     if (!m_authorTags.contains(info)) {
142         return;
143     }
144 
145     m_authorInfoOverride.insert(info, data);
146 }
147 
setActiveAuthorInfo(const QString & info,const QString & data)148 void KoDocumentInfo::setActiveAuthorInfo(const QString &info, const QString &data)
149 {
150     if (!m_authorTags.contains(info)) {
151         return;
152     }
153 
154     if (data.isEmpty()) {
155         m_authorInfo.remove(info);
156     } else {
157         m_authorInfo.insert(info, data);
158     }
159     emit infoUpdated(info, data);
160 }
161 
authorInfo(const QString & info) const162 QString KoDocumentInfo::authorInfo(const QString &info) const
163 {
164     if (!m_authorTags.contains(info))
165         return QString();
166 
167     return m_authorInfo[ info ];
168 }
169 
setAboutInfo(const QString & info,const QString & data)170 void KoDocumentInfo::setAboutInfo(const QString &info, const QString &data)
171 {
172     if (!m_aboutTags.contains(info))
173         return;
174 
175     m_aboutInfo.insert(info, data);
176     emit infoUpdated(info, data);
177 }
178 
aboutInfo(const QString & info) const179 QString KoDocumentInfo::aboutInfo(const QString &info) const
180 {
181     if (!m_aboutTags.contains(info)) {
182         return QString();
183     }
184 
185     return m_aboutInfo[info];
186 }
187 
saveOasisAuthorInfo(KoXmlWriter & xmlWriter)188 bool KoDocumentInfo::saveOasisAuthorInfo(KoXmlWriter &xmlWriter)
189 {
190     foreach(const QString & tag, m_authorTags) {
191         if (!authorInfo(tag).isEmpty() && tag == "creator") {
192             xmlWriter.startElement("dc:creator");
193             xmlWriter.addTextNode(authorInfo("creator"));
194             xmlWriter.endElement();
195         } else if (!authorInfo(tag).isEmpty()) {
196             xmlWriter.startElement("meta:user-defined");
197             xmlWriter.addAttribute("meta:name", tag);
198             xmlWriter.addTextNode(authorInfo(tag));
199             xmlWriter.endElement();
200         }
201     }
202 
203     return true;
204 }
205 
loadOasisAuthorInfo(const KoXmlNode & metaDoc)206 bool KoDocumentInfo::loadOasisAuthorInfo(const KoXmlNode &metaDoc)
207 {
208     KoXmlElement e = KoXml::namedItemNS(metaDoc, KoXmlNS::dc, "creator");
209     if (!e.isNull() && !e.text().isEmpty())
210         setActiveAuthorInfo("creator", e.text());
211 
212     KoXmlNode n = metaDoc.firstChild();
213     for (; !n.isNull(); n = n.nextSibling()) {
214         if (!n.isElement())
215             continue;
216 
217         KoXmlElement e = n.toElement();
218         if (!(e.namespaceURI() == KoXmlNS::meta &&
219                 e.localName() == "user-defined" && !e.text().isEmpty()))
220             continue;
221 
222         QString name = e.attributeNS(KoXmlNS::meta, "name", QString());
223         setActiveAuthorInfo(name, e.text());
224     }
225 
226     return true;
227 }
228 
loadAuthorInfo(const KoXmlElement & e)229 bool KoDocumentInfo::loadAuthorInfo(const KoXmlElement &e)
230 {
231     KoXmlNode n = e.namedItem("author").firstChild();
232     for (; !n.isNull(); n = n.nextSibling()) {
233         KoXmlElement e = n.toElement();
234         if (e.isNull())
235             continue;
236 
237         if (e.tagName() == "full-name")
238             setActiveAuthorInfo("creator", e.text().trimmed());
239         else
240             setActiveAuthorInfo(e.tagName(), e.text().trimmed());
241     }
242 
243     return true;
244 }
245 
saveAuthorInfo(QDomDocument & doc)246 QDomElement KoDocumentInfo::saveAuthorInfo(QDomDocument &doc)
247 {
248     QDomElement e = doc.createElement("author");
249     QDomElement t;
250 
251     foreach(const QString &tag, m_authorTags) {
252         if (tag == "creator")
253             t = doc.createElement("full-name");
254         else
255             t = doc.createElement(tag);
256 
257         e.appendChild(t);
258         t.appendChild(doc.createTextNode(authorInfo(tag)));
259     }
260 
261     return e;
262 }
263 
saveOasisAboutInfo(KoXmlWriter & xmlWriter)264 bool KoDocumentInfo::saveOasisAboutInfo(KoXmlWriter &xmlWriter)
265 {
266     foreach(const QString &tag, m_aboutTags) {
267         if (!aboutInfo(tag).isEmpty() || tag == "title") {
268             if (tag == "keyword") {
269                 foreach(const QString & tmp, aboutInfo("keyword").split(m_keywordSeparator)) {
270                     xmlWriter.startElement("meta:keyword");
271                     xmlWriter.addTextNode(tmp);
272                     xmlWriter.endElement();
273                 }
274             } else if (tag == "title" || tag == "description" || tag == "subject" ||
275                        tag == "date" || tag == "language") {
276                 QByteArray elementName(QString("dc:" + tag).toLatin1());
277                 xmlWriter.startElement(elementName.constData());
278                 xmlWriter.addTextNode(aboutInfo(tag));
279                 xmlWriter.endElement();
280             } else {
281                 QByteArray elementName(QString("meta:" + tag).toLatin1());
282                 xmlWriter.startElement(elementName.constData());
283                 xmlWriter.addTextNode(aboutInfo(tag));
284                 xmlWriter.endElement();
285             }
286         }
287     }
288 
289     return true;
290 }
291 
loadOasisAboutInfo(const KoXmlNode & metaDoc)292 bool KoDocumentInfo::loadOasisAboutInfo(const KoXmlNode &metaDoc)
293 {
294     QStringList keywords;
295     KoXmlElement e;
296     forEachElement(e, metaDoc) {
297         QString tag(e.localName());
298         if (! m_aboutTags.contains(tag) && tag != "generator") {
299             continue;
300         }
301 
302         //debugOdf<<"localName="<<e.localName();
303         if (tag == "keyword") {
304             if (!e.text().isEmpty())
305                 keywords << e.text().trimmed();
306         } else if (tag == "description") {
307             //this is the odf way but add meta:comment if is already loaded
308             KoXmlElement e  = KoXml::namedItemNS(metaDoc, KoXmlNS::dc, tag);
309             if (!e.isNull() && !e.text().isEmpty())
310                 setAboutInfo("description", aboutInfo("description") + e.text().trimmed());
311         } else if (tag == "comments") {
312             //this was the old way so add it to dc:description
313             KoXmlElement e  = KoXml::namedItemNS(metaDoc, KoXmlNS::meta, tag);
314             if (!e.isNull() && !e.text().isEmpty())
315                 setAboutInfo("description", aboutInfo("description") + e.text().trimmed());
316         } else if (tag == "title"|| tag == "subject"
317                    || tag == "date" || tag == "language") {
318             KoXmlElement e  = KoXml::namedItemNS(metaDoc, KoXmlNS::dc, tag);
319             if (!e.isNull() && !e.text().isEmpty())
320                 setAboutInfo(tag, e.text().trimmed());
321         } else if (tag == "generator") {
322             setOriginalGenerator(e.text().trimmed());
323         } else {
324             KoXmlElement e  = KoXml::namedItemNS(metaDoc, KoXmlNS::meta, tag);
325             if (!e.isNull() && !e.text().isEmpty())
326                 setAboutInfo(tag, e.text().trimmed());
327         }
328     }
329 
330     if (keywords.count() > 0) {
331         setAboutInfo("keyword", keywords.join(m_keywordSeparator));
332     }
333 
334     return true;
335 }
336 
loadAboutInfo(const KoXmlElement & e)337 bool KoDocumentInfo::loadAboutInfo(const KoXmlElement &e)
338 {
339     KoXmlNode n = e.namedItem("about").firstChild();
340     KoXmlElement tmp;
341     for (; !n.isNull(); n = n.nextSibling()) {
342         tmp = n.toElement();
343         if (tmp.isNull())
344             continue;
345 
346         if (tmp.tagName() == "abstract")
347             setAboutInfo("comments", tmp.text());
348 
349         setAboutInfo(tmp.tagName(), tmp.text());
350     }
351 
352     return true;
353 }
354 
saveAboutInfo(QDomDocument & doc)355 QDomElement KoDocumentInfo::saveAboutInfo(QDomDocument &doc)
356 {
357     QDomElement e = doc.createElement("about");
358     QDomElement t;
359 
360     foreach(const QString &tag, m_aboutTags) {
361         if (tag == "comments") {
362             t = doc.createElement("abstract");
363             e.appendChild(t);
364             t.appendChild(doc.createCDATASection(aboutInfo(tag)));
365         } else {
366             t = doc.createElement(tag);
367             e.appendChild(t);
368             t.appendChild(doc.createTextNode(aboutInfo(tag)));
369         }
370     }
371 
372     return e;
373 }
374 
updateParametersAndBumpNumCycles()375 void KoDocumentInfo::updateParametersAndBumpNumCycles()
376 {
377     KoDocumentBase *doc = dynamic_cast< KoDocumentBase *>(parent());
378     if (doc && doc->isAutosaving()) {
379         return;
380     }
381 
382     setAboutInfo("editing-cycles", QString::number(aboutInfo("editing-cycles").toInt() + 1));
383     setAboutInfo("date", QDateTime::currentDateTime().toString(Qt::ISODate));
384 
385     updateParameters();
386 }
387 
updateParameters()388 void KoDocumentInfo::updateParameters()
389 {
390     KoDocumentBase *doc = dynamic_cast< KoDocumentBase *>(parent());
391     if (doc && (!doc->isModified() && !doc->isEmpty())) {
392         return;
393     }
394 
395     KConfig config("calligrarc");
396     config.reparseConfiguration();
397     KConfigGroup authorGroup(&config, "Author");
398     QStringList profiles = authorGroup.readEntry("profile-names", QStringList());
399 
400     config.reparseConfiguration();
401     KConfigGroup appAuthorGroup(&config, "Author");
402     QString profile = appAuthorGroup.readEntry("active-profile", "");
403 
404     if (profiles.contains(profile)) {
405         KConfigGroup cgs(&authorGroup, "Author-" + profile);
406         setActiveAuthorInfo("creator", cgs.readEntry("creator"));
407         setActiveAuthorInfo("initial", cgs.readEntry("initial"));
408         setActiveAuthorInfo("author-title", cgs.readEntry("author-title"));
409         setActiveAuthorInfo("email", cgs.readEntry("email"));
410         setActiveAuthorInfo("telephone", cgs.readEntry("telephone"));
411         setActiveAuthorInfo("telephone-work", cgs.readEntry("telephone-work"));
412         setActiveAuthorInfo("fax", cgs.readEntry("fax"));
413         setActiveAuthorInfo("country",cgs.readEntry("country"));
414         setActiveAuthorInfo("postal-code",cgs.readEntry("postal-code"));
415         setActiveAuthorInfo("city", cgs.readEntry("city"));
416         setActiveAuthorInfo("street", cgs.readEntry("street"));
417         setActiveAuthorInfo("position", cgs.readEntry("position"));
418         setActiveAuthorInfo("company", cgs.readEntry("company"));
419     } else {
420         if (profile == "anonymous") {
421             setActiveAuthorInfo("creator", QString());
422             setActiveAuthorInfo("telephone", QString());
423             setActiveAuthorInfo("telephone-work", QString());
424             setActiveAuthorInfo("email", QString());
425         } else {
426             KUser user(KUser::UseRealUserID);
427             setActiveAuthorInfo("creator", user.property(KUser::FullName).toString());
428             setActiveAuthorInfo("telephone-work", user.property(KUser::WorkPhone).toString());
429             setActiveAuthorInfo("telephone", user.property(KUser::HomePhone).toString());
430             KEMailSettings eMailSettings;
431             setActiveAuthorInfo("email", eMailSettings.getSetting(KEMailSettings::EmailAddress));
432         }
433         setActiveAuthorInfo("initial", "");
434         setActiveAuthorInfo("author-title", "");
435         setActiveAuthorInfo("fax", "");
436         setActiveAuthorInfo("country", "");
437         setActiveAuthorInfo("postal-code", "");
438         setActiveAuthorInfo("city", "");
439         setActiveAuthorInfo("street", "");
440         setActiveAuthorInfo("position", "");
441         setActiveAuthorInfo("company", "");
442     }
443 
444     //alllow author info set programatically to override info from author profile
445     foreach(const QString &tag, m_authorTags) {
446         if (m_authorInfoOverride.contains(tag)) {
447             setActiveAuthorInfo(tag, m_authorInfoOverride.value(tag));
448         }
449     }
450 }
451 
resetMetaData()452 void KoDocumentInfo::resetMetaData()
453 {
454     setAboutInfo("editing-cycles", QString::number(0));
455     setAboutInfo("initial-creator", authorInfo("creator"));
456     setAboutInfo("creation-date", QDateTime::currentDateTime().toString(Qt::ISODate));
457 }
458 
originalGenerator() const459 QString KoDocumentInfo::originalGenerator() const
460 {
461     return m_generator;
462 }
463 
setOriginalGenerator(const QString & generator)464 void KoDocumentInfo::setOriginalGenerator(const QString &generator)
465 {
466     m_generator = generator;
467 }
468