File indexing completed on 2024-05-12 05:10:13

0001 /***************************************************************************
0002     Copyright (C) 2003-2009 Robby Stephenson <robby@periapsis.org>
0003  ***************************************************************************/
0004 
0005 /***************************************************************************
0006  *                                                                         *
0007  *   This program is free software; you can redistribute it and/or         *
0008  *   modify it under the terms of the GNU General Public License as        *
0009  *   published by the Free Software Foundation; either version 2 of        *
0010  *   the License or (at your option) version 3 or any later version        *
0011  *   accepted by the membership of KDE e.V. (or its successor approved     *
0012  *   by the membership of KDE e.V.), which shall act as a proxy            *
0013  *   defined in Section 14 of version 3 of the license.                    *
0014  *                                                                         *
0015  *   This program is distributed in the hope that it will be useful,       *
0016  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0017  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0018  *   GNU General Public License for more details.                          *
0019  *                                                                         *
0020  *   You should have received a copy of the GNU General Public License     *
0021  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
0022  *                                                                         *
0023  ***************************************************************************/
0024 
0025 #include "tellicoxmlexporter.h"
0026 #include "tellico_xml.h"
0027 #include "../utils/bibtexhandler.h" // needed for cleaning text
0028 #include "../utils/string_utils.h"
0029 #include "../utils/urlfieldlogic.h"
0030 #include "../entrygroup.h"
0031 #include "../collections/bibtexcollection.h"
0032 #include "../images/imagefactory.h"
0033 #include "../images/image.h"
0034 #include "../images/imageinfo.h"
0035 #include "../core/filehandler.h"
0036 #include "../document.h"
0037 #include "../fieldformat.h"
0038 #include "../models/entrysortmodel.h"
0039 #include "../models/modelmanager.h"
0040 #include "../models/modeliterator.h"
0041 #include "../models/models.h"
0042 #include "../tellico_debug.h"
0043 
0044 #include <KLocalizedString>
0045 #include <KConfigGroup>
0046 
0047 #include <QDir>
0048 #include <QGroupBox>
0049 #include <QCheckBox>
0050 #include <QDomDocument>
0051 #include <QTextCodec>
0052 #include <QVBoxLayout>
0053 
0054 #include <algorithm>
0055 
0056 using namespace Tellico;
0057 using Tellico::Export::TellicoXMLExporter;
0058 
0059 TellicoXMLExporter::TellicoXMLExporter(Tellico::Data::CollPtr coll) : Exporter(coll),
0060       m_includeImages(false), m_includeGroups(false), m_widget(nullptr), m_checkIncludeImages(nullptr) {
0061   setOptions(options() | Export::ExportImages | Export::ExportImageSize); // not included by default
0062 }
0063 
0064 TellicoXMLExporter::~TellicoXMLExporter() {
0065 }
0066 
0067 QString TellicoXMLExporter::formatString() const {
0068   return QStringLiteral("XML");
0069 }
0070 
0071 QString TellicoXMLExporter::fileFilter() const {
0072   return i18n("XML Files") + QLatin1String(" (*.xml)") + QLatin1String(";;") + i18n("All Files") + QLatin1String(" (*)");
0073 }
0074 
0075 bool TellicoXMLExporter::exec() {
0076   QDomDocument doc = exportXML();
0077   if(doc.isNull()) {
0078     return false;
0079   }
0080   return FileHandler::writeTextURL(url(), doc.toString(),
0081                                    options() & ExportUTF8,
0082                                    options() & Export::ExportForce);
0083 }
0084 
0085 QString TellicoXMLExporter::text() const {
0086   return exportXML().toString();
0087 }
0088 
0089 QDomDocument TellicoXMLExporter::exportXML() const {
0090   int exportVersion = XML::syntaxVersion;
0091 
0092   if(exportVersion == 12 && !version12Needed()) {
0093     exportVersion = 11;
0094   }
0095 
0096   QDomImplementation impl;
0097   // Bug 443845 - but do not just silent drop the invalid characters
0098   // since that drops emojis and unicode points with surrogate encoding
0099   // instead Tellico::removeControlCodes is used everywhere that
0100   // QDomDocument::createTextNode() is called
0101 //  impl.setInvalidDataPolicy(QDomImplementation::DropInvalidChars);
0102   QDomDocumentType doctype = impl.createDocumentType(QStringLiteral("tellico"),
0103                                                      XML::pubTellico(exportVersion),
0104                                                      XML::dtdTellico(exportVersion));
0105   //default namespace
0106   const QString& ns = XML::nsTellico;
0107 
0108   QDomDocument dom = impl.createDocument(ns, QStringLiteral("tellico"), doctype);
0109 
0110   // root tellico element
0111   QDomElement root = dom.documentElement();
0112 
0113   QString encodeStr = QStringLiteral("version=\"1.0\" encoding=\"");
0114   if(options() & Export::ExportUTF8) {
0115     encodeStr += QLatin1String("UTF-8");
0116   } else {
0117     encodeStr += QLatin1String(QTextCodec::codecForLocale()->name());
0118   }
0119   encodeStr += QLatin1Char('"');
0120 
0121   // createDocument creates a root node, insert the processing instruction before it
0122   dom.insertBefore(dom.createProcessingInstruction(QStringLiteral("xml"), encodeStr), root);
0123 
0124   root.setAttribute(QStringLiteral("syntaxVersion"), exportVersion);
0125 
0126   FieldFormat::Request format = (options() & Export::ExportFormatted ?
0127                                                 FieldFormat::ForceFormat :
0128                                                 FieldFormat::AsIsFormat);
0129 
0130   exportCollectionXML(dom, root, format);
0131 
0132   // clear image list
0133   m_images.clear();
0134 
0135   return dom;
0136 }
0137 
0138 void TellicoXMLExporter::exportCollectionXML(QDomDocument& dom_, QDomElement& parent_, int format_) const {
0139   Data::CollPtr coll = collection();
0140   if(!coll) {
0141     myWarning() << "no collection pointer!";
0142     return;
0143   }
0144 
0145   QDomElement collElem = dom_.createElement(QStringLiteral("collection"));
0146   collElem.setAttribute(QStringLiteral("type"), coll->type());
0147   collElem.setAttribute(QStringLiteral("title"), coll->title());
0148 
0149   QDomElement fieldsElem = dom_.createElement(QStringLiteral("fields"));
0150   collElem.appendChild(fieldsElem);
0151 
0152   foreach(Data::FieldPtr field, fields()) {
0153     exportFieldXML(dom_, fieldsElem, field);
0154   }
0155 
0156   if(coll->type() == Data::Collection::Bibtex) {
0157     const Data::BibtexCollection* c = static_cast<const Data::BibtexCollection*>(coll.data());
0158     if(!c->preamble().isEmpty()) {
0159       QDomElement preElem = dom_.createElement(QStringLiteral("bibtex-preamble"));
0160       preElem.appendChild(dom_.createTextNode(removeControlCodes(c->preamble())));
0161       collElem.appendChild(preElem);
0162     }
0163 
0164     QDomElement macrosElem = dom_.createElement(QStringLiteral("macros"));
0165     for(StringMap::ConstIterator macroIt = c->macroList().constBegin(); macroIt != c->macroList().constEnd(); ++macroIt) {
0166       if(!macroIt.value().isEmpty()) {
0167         QDomElement macroElem = dom_.createElement(QStringLiteral("macro"));
0168         macroElem.setAttribute(QStringLiteral("name"), macroIt.key());
0169         macroElem.appendChild(dom_.createTextNode(removeControlCodes(macroIt.value())));
0170         macrosElem.appendChild(macroElem);
0171       }
0172     }
0173     if(macrosElem.childNodes().count() > 0) {
0174       collElem.appendChild(macrosElem);
0175     }
0176   }
0177 
0178   foreach(Data::EntryPtr entry, entries()) {
0179     exportEntryXML(dom_, collElem, entry, format_);
0180   }
0181 
0182   if(!m_images.isEmpty() && (options() & Export::ExportImages)) {
0183     QDomElement imgsElem = dom_.createElement(QStringLiteral("images"));
0184     const QStringList imageIds = m_images.values();
0185     foreach(const QString& id, m_images) {
0186       exportImageXML(dom_, imgsElem, id);
0187     }
0188     if(imgsElem.hasChildNodes()) {
0189       collElem.appendChild(imgsElem);
0190     }
0191   }
0192 
0193   if(m_includeGroups) {
0194     exportGroupXML(dom_, collElem);
0195   }
0196 
0197   parent_.appendChild(collElem);
0198 
0199   // the borrowers and filters are in the tellico object, not the collection
0200   if(options() & Export::ExportComplete) {
0201     QDomElement bElem = dom_.createElement(QStringLiteral("borrowers"));
0202     foreach(Data::BorrowerPtr borrower, coll->borrowers()) {
0203       exportBorrowerXML(dom_, bElem, borrower);
0204     }
0205     if(bElem.hasChildNodes()) {
0206       parent_.appendChild(bElem);
0207     }
0208 
0209     QDomElement fElem = dom_.createElement(QStringLiteral("filters"));
0210     foreach(FilterPtr filter, coll->filters()) {
0211       exportFilterXML(dom_, fElem, filter);
0212     }
0213     if(fElem.hasChildNodes()) {
0214       parent_.appendChild(fElem);
0215     }
0216   }
0217 }
0218 
0219 void TellicoXMLExporter::exportFieldXML(QDomDocument& dom_, QDomElement& parent_, Tellico::Data::FieldPtr field_) const {
0220   QDomElement elem = dom_.createElement(QStringLiteral("field"));
0221 
0222   elem.setAttribute(QStringLiteral("name"),     field_->name());
0223   elem.setAttribute(QStringLiteral("title"),    field_->title());
0224   elem.setAttribute(QStringLiteral("category"), field_->category());
0225   elem.setAttribute(QStringLiteral("type"),     field_->type());
0226   elem.setAttribute(QStringLiteral("flags"),    field_->flags());
0227   elem.setAttribute(QStringLiteral("format"),   field_->formatType());
0228 
0229   if(field_->type() == Data::Field::Choice) {
0230     elem.setAttribute(QStringLiteral("allowed"), field_->allowed().join(QLatin1String(";")));
0231   }
0232 
0233   // only save description if it's not equal to title, which is the default
0234   // title is never empty, so this indirectly checks for empty descriptions
0235   if(field_->description() != field_->title()) {
0236     elem.setAttribute(QStringLiteral("description"), field_->description());
0237   }
0238 
0239   for(StringMap::ConstIterator it = field_->propertyList().begin(); it != field_->propertyList().end(); ++it) {
0240     if(it.value().isEmpty()) {
0241       continue;
0242     }
0243     QDomElement e = dom_.createElement(QStringLiteral("prop"));
0244     e.setAttribute(QStringLiteral("name"), it.key());
0245     e.appendChild(dom_.createTextNode(removeControlCodes(it.value())));
0246     elem.appendChild(e);
0247   }
0248 
0249   parent_.appendChild(elem);
0250 }
0251 
0252 void TellicoXMLExporter::exportEntryXML(QDomDocument& dom_, QDomElement& parent_, Tellico::Data::EntryPtr entry_, int format_) const {
0253   QDomElement entryElem = dom_.createElement(QStringLiteral("entry"));
0254   entryElem.setAttribute(QStringLiteral("id"), QString::number(entry_->id()));
0255 
0256   // iterate through every field for the entry
0257   foreach(Data::FieldPtr fIt, fields()) {
0258     QString fieldName = fIt->name();
0259 
0260     // Date fields are special, don't format in export
0261     QString fieldValue = (format_ == FieldFormat::ForceFormat && fIt->type() != Data::Field::Date) ?
0262                                                            entry_->formattedField(fieldName, FieldFormat::ForceFormat) :
0263                                                            entry_->field(fieldName);
0264     if(options() & ExportClean) {
0265       BibtexHandler::cleanText(fieldValue);
0266     }
0267 
0268     // if empty, then no field element is added and just continue
0269     if(fieldValue.isEmpty()) {
0270       continue;
0271     }
0272 
0273     // optionally, verify images exist
0274     if(fIt->type() == Data::Field::Image && (options() & Export::ExportVerifyImages)) {
0275       if(!ImageFactory::validImage(fieldValue)) {
0276         myDebug() << "entry: " << entry_->title();
0277         myDebug() << "skipping image: " << fieldValue;
0278         continue;
0279       }
0280     }
0281 
0282     if(fIt->type() == Data::Field::Table) {
0283       // who cares about grammar, just add an 's' to the name
0284       QDomElement parElem = dom_.createElement(fieldName + QLatin1Char('s'));
0285       entryElem.appendChild(parElem);
0286 
0287       bool ok;
0288       int ncols = Tellico::toUInt(fIt->property(QStringLiteral("columns")), &ok);
0289       if(!ok || ncols < 1) {
0290         ncols = 1;
0291       }
0292       foreach(const QString& rowValue, FieldFormat::splitTable(fieldValue)) {
0293         QDomElement fieldElem = dom_.createElement(fieldName);
0294         parElem.appendChild(fieldElem);
0295 
0296         QStringList columnValues = FieldFormat::splitRow(rowValue);
0297         if(ncols < columnValues.count()) {
0298           // need to combine all the last values, from ncols-1 to end
0299           QString lastValue = QStringList(columnValues.mid(ncols-1)).join(FieldFormat::columnDelimiterString());
0300           columnValues = columnValues.mid(0, ncols);
0301           columnValues.replace(ncols-1, lastValue);
0302         }
0303         for(int col = 0; col < columnValues.count(); ++col) {
0304           QDomElement elem = dom_.createElement(QStringLiteral("column"));
0305           elem.appendChild(dom_.createTextNode(removeControlCodes(columnValues.at(col))));
0306           fieldElem.appendChild(elem);
0307         }
0308       }
0309       continue;
0310     }
0311 
0312     if(fIt->hasFlag(Data::Field::AllowMultiple)) {
0313       // if multiple versions are allowed, split them into separate elements
0314       // parent element if field contains multiple values, child of entryElem
0315       // who cares about grammar, just add an QLatin1Char('s') to the name
0316       QDomElement parElem = dom_.createElement(fieldName + QLatin1Char('s'));
0317       entryElem.appendChild(parElem);
0318 
0319       // the space after the semi-colon is enforced when the field is set for the entry
0320       QStringList fields = FieldFormat::splitValue(fieldValue);
0321       for(QStringList::ConstIterator it = fields.constBegin(); it != fields.constEnd(); ++it) {
0322         // element for field value, child of either entryElem or ParentElem
0323         QDomElement fieldElem = dom_.createElement(fieldName);
0324         fieldElem.appendChild(dom_.createTextNode(removeControlCodes(*it)));
0325         parElem.appendChild(fieldElem);
0326       }
0327     } else {
0328       QDomElement fieldElem = dom_.createElement(fieldName);
0329       entryElem.appendChild(fieldElem);
0330       // Date fields get special treatment
0331       if(fIt->type() == Data::Field::Date) {
0332         // as of Tellico in KF5 (3.0), just forget about the calendar attribute for the moment, always use gregorian
0333         fieldElem.setAttribute(QStringLiteral("calendar"), QStringLiteral("gregorian"));
0334 #if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
0335         QStringList s = fieldValue.split(QLatin1Char('-'), QString::KeepEmptyParts);
0336 #else
0337         QStringList s = fieldValue.split(QLatin1Char('-'), Qt::KeepEmptyParts);
0338 #endif
0339         if(s.count() > 0 && !s[0].isEmpty()) {
0340           QDomElement e = dom_.createElement(QStringLiteral("year"));
0341           fieldElem.appendChild(e);
0342           e.appendChild(dom_.createTextNode(s[0]));
0343         }
0344         if(s.count() > 1 && !s[1].isEmpty()) {
0345           QDomElement e = dom_.createElement(QStringLiteral("month"));
0346           fieldElem.appendChild(e);
0347           e.appendChild(dom_.createTextNode(s[1]));
0348         }
0349         if(s.count() > 2 && !s[2].isEmpty()) {
0350           QDomElement e = dom_.createElement(QStringLiteral("day"));
0351           fieldElem.appendChild(e);
0352           e.appendChild(dom_.createTextNode(s[2]));
0353         }
0354       } else if(fIt->type() == Data::Field::URL &&
0355                 fIt->property(QStringLiteral("relative")) == QLatin1String("true")) {
0356         // if a relative URL and url() is not empty, change the value!
0357         QUrl old_url = Data::Document::self()->URL().resolved(QUrl(fieldValue));
0358         if(options() & Export::ExportAbsoluteLinks) {
0359           fieldElem.appendChild(dom_.createTextNode(old_url.url()));
0360         } else if(!url().isEmpty()) {
0361           QUrl new_url(url());
0362           if(new_url.scheme() == old_url.scheme() &&
0363              new_url.host() == old_url.host()) {
0364             // calculate a new relative url
0365             UrlFieldLogic logic;
0366             logic.setRelative(true);
0367             logic.setBaseUrl(url());
0368             fieldElem.appendChild(dom_.createTextNode(logic.urlText(old_url)));
0369           } else {
0370             // use the absolute url here
0371             fieldElem.appendChild(dom_.createTextNode(old_url.url()));
0372           }
0373         } else {
0374           fieldElem.appendChild(dom_.createTextNode(removeControlCodes(fieldValue)));
0375         }
0376       } else {
0377         fieldElem.appendChild(dom_.createTextNode(removeControlCodes(fieldValue)));
0378       }
0379     }
0380 
0381     if(fIt->type() == Data::Field::Image) {
0382       // possible to have more than one entry with the same image
0383       // only want to include it in the output xml once
0384       m_images.add(fieldValue);
0385     }
0386   } // end field loop
0387 
0388   parent_.appendChild(entryElem);
0389 }
0390 
0391 void TellicoXMLExporter::exportImageXML(QDomDocument& dom_, QDomElement& parent_, const QString& id_) const {
0392   if(id_.isEmpty()) {
0393     myDebug() << "empty image!";
0394     return;
0395   }
0396 //  myLog() << "id = " << id_;
0397 
0398   QDomElement imgElem = dom_.createElement(QStringLiteral("image"));
0399   if(m_includeImages) {
0400     const Data::Image& img = ImageFactory::imageById(id_);
0401     if(img.isNull()) {
0402       return;
0403     }
0404     imgElem.setAttribute(QStringLiteral("format"), QLatin1String(img.format()));
0405     imgElem.setAttribute(QStringLiteral("id"),     QString(img.id()));
0406     imgElem.setAttribute(QStringLiteral("width"),  img.width());
0407     imgElem.setAttribute(QStringLiteral("height"), img.height());
0408     if(img.linkOnly()) {
0409       imgElem.setAttribute(QStringLiteral("link"), QStringLiteral("true"));
0410     }
0411     QByteArray imgText = img.byteArray().toBase64();
0412     imgElem.appendChild(dom_.createTextNode(QLatin1String(imgText)));
0413   } else {
0414     const Data::ImageInfo& info = ImageFactory::imageInfo(id_);
0415     if(info.isNull()) {
0416       return;
0417     }
0418     imgElem.setAttribute(QStringLiteral("format"), QLatin1String(info.format));
0419     imgElem.setAttribute(QStringLiteral("id"),     QString(info.id));
0420     // only load the images to read the size if necessary
0421     const bool loadImageIfNecessary = options() & Export::ExportImageSize;
0422     imgElem.setAttribute(QStringLiteral("width"),  info.width(loadImageIfNecessary));
0423     imgElem.setAttribute(QStringLiteral("height"), info.height(loadImageIfNecessary));
0424     if(info.linkOnly) {
0425       imgElem.setAttribute(QStringLiteral("link"), QStringLiteral("true"));
0426     }
0427   }
0428   parent_.appendChild(imgElem);
0429 }
0430 
0431 void TellicoXMLExporter::exportGroupXML(QDomDocument& dom_, QDomElement& parent_) const {
0432   Data::EntryList vec = entries();
0433   bool exportAll = collection()->entries().count() == vec.count();
0434   // iterate over each group, which are the first children
0435   for(ModelIterator gIt(ModelManager::self()->groupModel()); gIt.group(); ++gIt) {
0436     if(gIt.group()->isEmpty()) {
0437       continue;
0438     }
0439     QDomElement groupElem = dom_.createElement(QStringLiteral("group"));
0440     groupElem.setAttribute(QStringLiteral("title"), gIt.group()->groupName());
0441     // now iterate over all entry items in the group
0442     Data::EntryList sorted = sortEntries(*gIt.group());
0443     foreach(Data::EntryPtr eIt, sorted) {
0444       if(!exportAll && vec.indexOf(eIt) == -1) {
0445         continue;
0446       }
0447       QDomElement entryRefElem = dom_.createElement(QStringLiteral("entryRef"));
0448       entryRefElem.setAttribute(QStringLiteral("id"), QString::number(eIt->id()));
0449       groupElem.appendChild(entryRefElem);
0450     }
0451     if(groupElem.hasChildNodes()) {
0452       parent_.appendChild(groupElem);
0453     }
0454   }
0455 }
0456 
0457 void TellicoXMLExporter::exportFilterXML(QDomDocument& dom_, QDomElement& parent_, Tellico::FilterPtr filter_) const {
0458   QDomElement filterElem = dom_.createElement(QStringLiteral("filter"));
0459   filterElem.setAttribute(QStringLiteral("name"), filter_->name());
0460 
0461   QString match = (filter_->op() == Filter::MatchAll) ? QStringLiteral("all") : QStringLiteral("any");
0462   filterElem.setAttribute(QStringLiteral("match"), match);
0463 
0464   foreach(FilterRule* rule, *filter_) {
0465     QDomElement ruleElem = dom_.createElement(QStringLiteral("rule"));
0466     ruleElem.setAttribute(QStringLiteral("field"), rule->fieldName());
0467     ruleElem.setAttribute(QStringLiteral("pattern"), rule->pattern());
0468     switch(rule->function()) {
0469       case FilterRule::FuncContains:
0470         ruleElem.setAttribute(QStringLiteral("function"), QStringLiteral("contains"));
0471         break;
0472       case FilterRule::FuncNotContains:
0473         ruleElem.setAttribute(QStringLiteral("function"), QStringLiteral("notcontains"));
0474         break;
0475       case FilterRule::FuncEquals:
0476         ruleElem.setAttribute(QStringLiteral("function"), QStringLiteral("equals"));
0477         break;
0478       case FilterRule::FuncNotEquals:
0479         ruleElem.setAttribute(QStringLiteral("function"), QStringLiteral("notequals"));
0480         break;
0481       case FilterRule::FuncRegExp:
0482         ruleElem.setAttribute(QStringLiteral("function"), QStringLiteral("regexp"));
0483         break;
0484       case FilterRule::FuncNotRegExp:
0485         ruleElem.setAttribute(QStringLiteral("function"), QStringLiteral("notregexp"));
0486         break;
0487       case FilterRule::FuncBefore:
0488         ruleElem.setAttribute(QStringLiteral("function"), QStringLiteral("before"));
0489         break;
0490       case FilterRule::FuncAfter:
0491         ruleElem.setAttribute(QStringLiteral("function"), QStringLiteral("after"));
0492         break;
0493       case FilterRule::FuncGreater:
0494         ruleElem.setAttribute(QStringLiteral("function"), QStringLiteral("greaterthan"));
0495         break;
0496       case FilterRule::FuncLess:
0497         ruleElem.setAttribute(QStringLiteral("function"), QStringLiteral("lessthan"));
0498         break;
0499       /* If anything is updated here, be sure to update xmlstatehandler */
0500     }
0501     filterElem.appendChild(ruleElem);
0502   }
0503 
0504   parent_.appendChild(filterElem);
0505 }
0506 
0507 void TellicoXMLExporter::exportBorrowerXML(QDomDocument& dom_, QDomElement& parent_,
0508                                            Tellico::Data::BorrowerPtr borrower_) const {
0509   if(borrower_->isEmpty()) {
0510     return;
0511   }
0512 
0513   QDomElement bElem = dom_.createElement(QStringLiteral("borrower"));
0514   parent_.appendChild(bElem);
0515 
0516   bElem.setAttribute(QStringLiteral("name"), borrower_->name());
0517   bElem.setAttribute(QStringLiteral("uid"), borrower_->uid());
0518 
0519   foreach(Data::LoanPtr it, borrower_->loans()) {
0520     QDomElement lElem = dom_.createElement(QStringLiteral("loan"));
0521     bElem.appendChild(lElem);
0522 
0523     lElem.setAttribute(QStringLiteral("uid"), it->uid());
0524     lElem.setAttribute(QStringLiteral("entryRef"), QString::number(it->entry()->id()));
0525     lElem.setAttribute(QStringLiteral("loanDate"), it->loanDate().toString(Qt::ISODate));
0526     lElem.setAttribute(QStringLiteral("dueDate"), it->dueDate().toString(Qt::ISODate));
0527     if(it->inCalendar()) {
0528       lElem.setAttribute(QStringLiteral("calendar"), QStringLiteral("true"));
0529     }
0530 
0531     lElem.appendChild(dom_.createTextNode(it->note()));
0532   }
0533 }
0534 
0535 QWidget* TellicoXMLExporter::widget(QWidget* parent_) {
0536   if(m_widget) {
0537     return m_widget;
0538   }
0539 
0540   m_widget = new QWidget(parent_);
0541   QVBoxLayout* l = new QVBoxLayout(m_widget);
0542 
0543   QGroupBox* gbox = new QGroupBox(i18n("Tellico XML Options"), m_widget);
0544   QVBoxLayout* vlay = new QVBoxLayout(gbox);
0545 
0546   m_checkIncludeImages = new QCheckBox(i18n("Include images in XML document"), gbox);
0547   m_checkIncludeImages->setChecked(m_includeImages);
0548   m_checkIncludeImages->setWhatsThis(i18n("If checked, the images in the document will be included "
0549                                           "in the XML stream as base64 encoded elements."));
0550 
0551   vlay->addWidget(m_checkIncludeImages);
0552 
0553   l->addWidget(gbox);
0554   l->addStretch(1);
0555   return m_widget;
0556 }
0557 
0558 void TellicoXMLExporter::readOptions(KSharedConfigPtr config_) {
0559   KConfigGroup group(config_, QStringLiteral("ExportOptions - %1").arg(formatString()));
0560   m_includeImages = group.readEntry("Include Images", m_includeImages);
0561 }
0562 
0563 void TellicoXMLExporter::saveOptions(KSharedConfigPtr config_) {
0564   m_includeImages = m_checkIncludeImages->isChecked();
0565 
0566   KConfigGroup group(config_, QStringLiteral("ExportOptions - %1").arg(formatString()));
0567   group.writeEntry("Include Images", m_includeImages);
0568 }
0569 
0570 Tellico::Data::EntryList TellicoXMLExporter::sortEntries(const Data::EntryList& entries_) const {
0571   Data::EntryList sorted = entries_;
0572 
0573   EntrySortModel* model = static_cast<EntrySortModel*>(ModelManager::self()->entryModel());
0574   // have to go in reverse for sorting
0575   Data::FieldList fields;
0576   Data::FieldPtr field;
0577   if(model->tertiarySortColumn() > -1) {
0578     field = model->headerData(model->tertiarySortColumn(), Qt::Horizontal, FieldPtrRole).value<Data::FieldPtr>();
0579     if(field) {
0580       fields << field;
0581     } else {
0582       myDebug() << "no field for tertiary sort column" << model->tertiarySortColumn();
0583     }
0584   }
0585   if(model->secondarySortColumn() > -1) {
0586     field = model->headerData(model->secondarySortColumn(), Qt::Horizontal, FieldPtrRole).value<Data::FieldPtr>();
0587     if(field) {
0588       fields << field;
0589     } else {
0590       myDebug() << "no field for secondary sort column" << model->secondarySortColumn();
0591     }
0592   }
0593   if(model->sortColumn() > -1) {
0594     field = model->headerData(model->sortColumn(), Qt::Horizontal, FieldPtrRole).value<Data::FieldPtr>();
0595     if(field) {
0596       fields << field;
0597     } else {
0598       myDebug() << "no field for primary sort column" << model->sortColumn();
0599     }
0600   }
0601 
0602   // now sort
0603   foreach(Data::FieldPtr field, fields) {
0604     std::sort(sorted.begin(), sorted.end(), Data::EntryCmp(field->name()));
0605   }
0606 
0607   return sorted;
0608 }
0609 
0610 bool TellicoXMLExporter::version12Needed() const {
0611   // version 12 is only necessary if the new filter rules are used
0612   foreach(FilterPtr filter, collection()->filters()) {
0613     foreach(FilterRule* rule, *filter) {
0614       if(rule->function() == FilterRule::FuncBefore ||
0615          rule->function() == FilterRule::FuncAfter ||
0616          rule->function() == FilterRule::FuncGreater ||
0617          rule->function() == FilterRule::FuncLess) {
0618         return true;
0619       }
0620     }
0621   }
0622   return false;
0623 }