/*************************************************************************** copyright : (C) 2003-2006 by Robby Stephenson email : robby@periapsis.org ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of version 2 of the GNU General Public License as * * published by the Free Software Foundation; * * * ***************************************************************************/ #include "tellicoxmlexporter.h" #include "../collections/bibtexcollection.h" #include "../imagefactory.h" #include "../image.h" #include "../controller.h" // needed for getting groupView pointer #include "../entryitem.h" #include "../latin1literal.h" #include "../filehandler.h" #include "../groupiterator.h" #include "../tellico_utils.h" #include "../tellico_kernel.h" #include "../tellico_debug.h" #include "tellico_xml.h" #include "../document.h" // needed for sorting groups #include "../translators/bibtexhandler.h" // needed for cleaning text #include #include #include #include #include #include #include #include #include #include #include using Tellico::Export::TellicoXMLExporter; TellicoXMLExporter::TellicoXMLExporter() : Exporter(), m_includeImages(false), m_includeGroups(false), m_widget(0) { setOptions(options() | Export::ExportImages | Export::ExportImageSize); // not included by default } TellicoXMLExporter::TellicoXMLExporter(Data::CollPtr coll) : Exporter(coll), m_includeImages(false), m_includeGroups(false), m_widget(0) { setOptions(options() | Export::ExportImages | Export::ExportImageSize); // not included by default } TQString TellicoXMLExporter::formatString() const { return i18n("XML"); } TQString TellicoXMLExporter::fileFilter() const { return i18n("*.xml|XML Files (*.xml)") + TQChar('\n') + i18n("*|All Files"); } bool TellicoXMLExporter::exec() { TQDomDocument doc = exportXML(); if(doc.isNull()) { return false; } return FileHandler::writeTextURL(url(), doc.toString(), options() & ExportUTF8, options() & Export::ExportForce); } TQDomDocument TellicoXMLExporter::exportXML() const { // don't be hard on people with older versions. The only difference with DTD 10 was adding // a board game collection, so use 9 still unless it's a board game int exportVersion = (XML::syntaxVersion == 10 && collection()->type() != Data::Collection::BoardGame) ? 9 : XML::syntaxVersion; TQDomImplementation impl; TQDomDocumentType doctype = impl.createDocumentType(TQString::fromLatin1("tellico"), XML::pubTellico(exportVersion), XML::dtdTellico(exportVersion)); //default namespace const TQString& ns = XML::nsTellico; TQDomDocument dom = impl.createDocument(ns, TQString::fromLatin1("tellico"), doctype); // root tellico element TQDomElement root = dom.documentElement(); TQString encodeStr = TQString::fromLatin1("version=\"1.0\" encoding=\""); if(options() & Export::ExportUTF8) { encodeStr += TQString::fromLatin1("UTF-8"); } else { encodeStr += TQString::fromLatin1(TQTextCodec::codecForLocale()->mimeName()); } encodeStr += TQChar('"'); // createDocument creates a root node, insert the processing instruction before it dom.insertBefore(dom.createProcessingInstruction(TQString::fromLatin1("xml"), encodeStr), root); root.setAttribute(TQString::fromLatin1("syntaxVersion"), exportVersion); exportCollectionXML(dom, root, options() & Export::ExportFormatted); // clear image list m_images.clear(); return dom; } TQString TellicoXMLExporter::exportXMLString() const { return exportXML().toString(); } void TellicoXMLExporter::exportCollectionXML(TQDomDocument& dom_, TQDomElement& parent_, bool format_) const { Data::CollPtr coll = collection(); if(!coll) { kdWarning() << "TellicoXMLExporter::exportCollectionXML() - no collection pointer!" << endl; return; } TQDomElement collElem = dom_.createElement(TQString::fromLatin1("collection")); collElem.setAttribute(TQString::fromLatin1("type"), coll->type()); collElem.setAttribute(TQString::fromLatin1("title"), coll->title()); TQDomElement fieldsElem = dom_.createElement(TQString::fromLatin1("fields")); collElem.appendChild(fieldsElem); Data::FieldVec fields = coll->fields(); for(Data::FieldVec::Iterator fIt = fields.begin(); fIt != fields.end(); ++fIt) { exportFieldXML(dom_, fieldsElem, fIt); } if(coll->type() == Data::Collection::Bibtex) { const Data::BibtexCollection* c = static_cast(coll.data()); if(!c->preamble().isEmpty()) { TQDomElement preElem = dom_.createElement(TQString::fromLatin1("bibtex-preamble")); preElem.appendChild(dom_.createTextNode(c->preamble())); collElem.appendChild(preElem); } TQDomElement macrosElem = dom_.createElement(TQString::fromLatin1("macros")); for(StringMap::ConstIterator macroIt = c->macroList().constBegin(); macroIt != c->macroList().constEnd(); ++macroIt) { if(!macroIt.data().isEmpty()) { TQDomElement macroElem = dom_.createElement(TQString::fromLatin1("macro")); macroElem.setAttribute(TQString::fromLatin1("name"), macroIt.key()); macroElem.appendChild(dom_.createTextNode(macroIt.data())); macrosElem.appendChild(macroElem); } } if(macrosElem.childNodes().count() > 0) { collElem.appendChild(macrosElem); } } Data::EntryVec evec = entries(); for(Data::EntryVec::Iterator entry = evec.begin(); entry != evec.end(); ++entry) { exportEntryXML(dom_, collElem, entry, format_); } if(!m_images.isEmpty() && (options() & Export::ExportImages)) { TQDomElement imgsElem = dom_.createElement(TQString::fromLatin1("images")); collElem.appendChild(imgsElem); const TQStringList imageIds = m_images.toList(); for(TQStringList::ConstIterator it = imageIds.begin(); it != imageIds.end(); ++it) { exportImageXML(dom_, imgsElem, *it); } } if(m_includeGroups) { exportGroupXML(dom_, collElem); } parent_.appendChild(collElem); // the borrowers and filters are in the tellico object, not the collection if(options() & Export::ExportComplete) { TQDomElement bElem = dom_.createElement(TQString::fromLatin1("borrowers")); Data::BorrowerVec borrowers = coll->borrowers(); for(Data::BorrowerVec::Iterator bIt = borrowers.begin(); bIt != borrowers.end(); ++bIt) { exportBorrowerXML(dom_, bElem, bIt); } if(bElem.hasChildNodes()) { parent_.appendChild(bElem); } TQDomElement fElem = dom_.createElement(TQString::fromLatin1("filters")); FilterVec filters = coll->filters(); for(FilterVec::Iterator fIt = filters.begin(); fIt != filters.end(); ++fIt) { exportFilterXML(dom_, fElem, fIt); } if(fElem.hasChildNodes()) { parent_.appendChild(fElem); } } } void TellicoXMLExporter::exportFieldXML(TQDomDocument& dom_, TQDomElement& parent_, Data::FieldPtr field_) const { TQDomElement elem = dom_.createElement(TQString::fromLatin1("field")); elem.setAttribute(TQString::fromLatin1("name"), field_->name()); elem.setAttribute(TQString::fromLatin1("title"), field_->title()); elem.setAttribute(TQString::fromLatin1("category"), field_->category()); elem.setAttribute(TQString::fromLatin1("type"), field_->type()); elem.setAttribute(TQString::fromLatin1("flags"), field_->flags()); elem.setAttribute(TQString::fromLatin1("format"), field_->formatFlag()); if(field_->type() == Data::Field::Choice) { elem.setAttribute(TQString::fromLatin1("allowed"), field_->allowed().join(TQString::fromLatin1(";"))); } // only save description if it's not equal to title, which is the default // title is never empty, so this indirectly checks for empty descriptions if(field_->description() != field_->title()) { elem.setAttribute(TQString::fromLatin1("description"), field_->description()); } for(StringMap::ConstIterator it = field_->propertyList().begin(); it != field_->propertyList().end(); ++it) { if(it.data().isEmpty()) { continue; } TQDomElement e = dom_.createElement(TQString::fromLatin1("prop")); e.setAttribute(TQString::fromLatin1("name"), it.key()); e.appendChild(dom_.createTextNode(it.data())); elem.appendChild(e); } parent_.appendChild(elem); } void TellicoXMLExporter::exportEntryXML(TQDomDocument& dom_, TQDomElement& parent_, Data::EntryPtr entry_, bool format_) const { TQDomElement entryElem = dom_.createElement(TQString::fromLatin1("entry")); entryElem.setAttribute(TQString::fromLatin1("id"), entry_->id()); // iterate through every field for the entry Data::FieldVec fields = entry_->collection()->fields(); for(Data::FieldVec::Iterator fIt = fields.begin(); fIt != fields.end(); ++fIt) { TQString fieldName = fIt->name(); // Date fields are special, don't format in export TQString fieldValue = (format_ && fIt->type() != Data::Field::Date) ? entry_->formattedField(fieldName) : entry_->field(fieldName); if(options() & ExportClean) { BibtexHandler::cleanText(fieldValue); } // if empty, then no field element is added and just continue if(fieldValue.isEmpty()) { continue; } // optionally, verify images exist if(fIt->type() == Data::Field::Image && (options() & Export::ExportVerifyImages)) { if(!ImageFactory::validImage(fieldValue)) { myDebug() << "TellicoXMLExporter::exportEntryXML() - entry: " << entry_->title() << endl; myDebug() << "TellicoXMLExporter::exportEntryXML() - skipping image: " << fieldValue << endl; continue; } } // if multiple versions are allowed, split them into separate elements if(fIt->flags() & Data::Field::AllowMultiple) { // parent element if field contains multiple values, child of entryElem // who cares about grammar, just add an 's' to the name TQDomElement parElem = dom_.createElement(fieldName + 's'); entryElem.appendChild(parElem); // the space after the semi-colon is enforced when the field is set for the entry TQStringList fields = TQStringList::split(TQString::fromLatin1("; "), fieldValue, true); for(TQStringList::ConstIterator it = fields.begin(); it != fields.end(); ++it) { // element for field value, child of either entryElem or ParentElem TQDomElement fieldElem = dom_.createElement(fieldName); // special case for multi-column tables int ncols = 0; if(fIt->type() == Data::Field::Table) { bool ok; ncols = Tellico::toUInt(fIt->property(TQString::fromLatin1("columns")), &ok); if(!ok) { ncols = 1; } } if(ncols > 1) { for(int col = 0; col < ncols; ++col) { TQDomElement elem; elem = dom_.createElement(TQString::fromLatin1("column")); elem.appendChild(dom_.createTextNode((*it).section(TQString::fromLatin1("::"), col, col))); fieldElem.appendChild(elem); } } else { fieldElem.appendChild(dom_.createTextNode(*it)); } parElem.appendChild(fieldElem); } } else { TQDomElement fieldElem = dom_.createElement(fieldName); entryElem.appendChild(fieldElem); // Date fields get special treatment if(fIt->type() == Data::Field::Date) { fieldElem.setAttribute(TQString::fromLatin1("calendar"), KGlobal::locale()->calendar()->calendarName()); TQStringList s = TQStringList::split('-', fieldValue, true); if(s.count() > 0 && !s[0].isEmpty()) { TQDomElement e = dom_.createElement(TQString::fromLatin1("year")); fieldElem.appendChild(e); e.appendChild(dom_.createTextNode(s[0])); } if(s.count() > 1 && !s[1].isEmpty()) { TQDomElement e = dom_.createElement(TQString::fromLatin1("month")); fieldElem.appendChild(e); e.appendChild(dom_.createTextNode(s[1])); } if(s.count() > 2 && !s[2].isEmpty()) { TQDomElement e = dom_.createElement(TQString::fromLatin1("day")); fieldElem.appendChild(e); e.appendChild(dom_.createTextNode(s[2])); } } else if(fIt->type() == Data::Field::URL && fIt->property(TQString::fromLatin1("relative")) == Latin1Literal("true") && !url().isEmpty()) { // if a relative URL and url() is not empty, change the value! KURL old_url(Kernel::self()->URL(), fieldValue); fieldElem.appendChild(dom_.createTextNode(KURL::relativeURL(url(), old_url))); } else { fieldElem.appendChild(dom_.createTextNode(fieldValue)); } } if(fIt->type() == Data::Field::Image) { // possible to have more than one entry with the same image // only want to include it in the output xml once m_images.add(fieldValue); } } // end field loop parent_.appendChild(entryElem); } void TellicoXMLExporter::exportImageXML(TQDomDocument& dom_, TQDomElement& parent_, const TQString& id_) const { if(id_.isEmpty()) { myDebug() << "TellicoXMLExporter::exportImageXML() - empty image!" << endl; return; } // myLog() << "TellicoXMLExporter::exportImageXML() - id = " << id_ << endl; TQDomElement imgElem = dom_.createElement(TQString::fromLatin1("image")); if(m_includeImages) { const Data::Image& img = ImageFactory::imageById(id_); if(img.isNull()) { myDebug() << "TellicoXMLExporter::exportImageXML() - null image - " << id_ << endl; return; } imgElem.setAttribute(TQString::fromLatin1("format"), img.format().data()); imgElem.setAttribute(TQString::fromLatin1("id"), img.id()); imgElem.setAttribute(TQString::fromLatin1("width"), img.width()); imgElem.setAttribute(TQString::fromLatin1("height"), img.height()); if(img.linkOnly()) { imgElem.setAttribute(TQString::fromLatin1("link"), TQString::fromLatin1("true")); } TQCString imgText = KCodecs::base64Encode(img.byteArray()); imgElem.appendChild(dom_.createTextNode(TQString::fromLatin1(imgText))); } else { const Data::ImageInfo& info = ImageFactory::imageInfo(id_); if(info.isNull()) { return; } imgElem.setAttribute(TQString::fromLatin1("format"), info.format.data()); imgElem.setAttribute(TQString::fromLatin1("id"), info.id); // only load the images to read the size if necessary const bool loadImageIfNecessary = options() & Export::ExportImageSize; imgElem.setAttribute(TQString::fromLatin1("width"), info.width(loadImageIfNecessary)); imgElem.setAttribute(TQString::fromLatin1("height"), info.height(loadImageIfNecessary)); if(info.linkOnly) { imgElem.setAttribute(TQString::fromLatin1("link"), TQString::fromLatin1("true")); } } parent_.appendChild(imgElem); } void TellicoXMLExporter::exportGroupXML(TQDomDocument& dom_, TQDomElement& parent_) const { Data::EntryVec vec = entries(); // need a copy for ::contains(); bool exportAll = collection()->entries().count() == vec.count(); // iterate over each group, which are the first children for(GroupIterator gIt = Controller::self()->groupIterator(); gIt.group(); ++gIt) { if(gIt.group()->isEmpty()) { continue; } TQDomElement groupElem = dom_.createElement(TQString::fromLatin1("group")); groupElem.setAttribute(TQString::fromLatin1("title"), gIt.group()->groupName()); // now iterate over all entry items in the group Data::EntryVec sorted = Data::Document::self()->sortEntries(*gIt.group()); for(Data::EntryVec::Iterator eIt = sorted.begin(); eIt != sorted.end(); ++eIt) { if(!exportAll && !vec.contains(eIt)) { continue; } TQDomElement entryRefElem = dom_.createElement(TQString::fromLatin1("entryRef")); entryRefElem.setAttribute(TQString::fromLatin1("id"), eIt->id()); groupElem.appendChild(entryRefElem); } if(groupElem.hasChildNodes()) { parent_.appendChild(groupElem); } } } void TellicoXMLExporter::exportFilterXML(TQDomDocument& dom_, TQDomElement& parent_, FilterPtr filter_) const { TQDomElement filterElem = dom_.createElement(TQString::fromLatin1("filter")); filterElem.setAttribute(TQString::fromLatin1("name"), filter_->name()); TQString match = (filter_->op() == Filter::MatchAll) ? TQString::fromLatin1("all") : TQString::fromLatin1("any"); filterElem.setAttribute(TQString::fromLatin1("match"), match); for(TQPtrListIterator it(*filter_); it.current(); ++it) { TQDomElement ruleElem = dom_.createElement(TQString::fromLatin1("rule")); ruleElem.setAttribute(TQString::fromLatin1("field"), it.current()->fieldName()); ruleElem.setAttribute(TQString::fromLatin1("pattern"), it.current()->pattern()); switch(it.current()->function()) { case FilterRule::FuncContains: ruleElem.setAttribute(TQString::fromLatin1("function"), TQString::fromLatin1("contains")); break; case FilterRule::FuncNotContains: ruleElem.setAttribute(TQString::fromLatin1("function"), TQString::fromLatin1("notcontains")); break; case FilterRule::FuncEquals: ruleElem.setAttribute(TQString::fromLatin1("function"), TQString::fromLatin1("equals")); break; case FilterRule::FuncNotEquals: ruleElem.setAttribute(TQString::fromLatin1("function"), TQString::fromLatin1("notequals")); break; case FilterRule::FuncRegExp: ruleElem.setAttribute(TQString::fromLatin1("function"), TQString::fromLatin1("regexp")); break; case FilterRule::FuncNotRegExp: ruleElem.setAttribute(TQString::fromLatin1("function"), TQString::fromLatin1("notregexp")); break; default: kdWarning() << "TellicoXMLExporter::exportFilterXML() - no matching rule function!" << endl; } filterElem.appendChild(ruleElem); } parent_.appendChild(filterElem); } void TellicoXMLExporter::exportBorrowerXML(TQDomDocument& dom_, TQDomElement& parent_, Data::BorrowerPtr borrower_) const { if(borrower_->isEmpty()) { return; } TQDomElement bElem = dom_.createElement(TQString::fromLatin1("borrower")); parent_.appendChild(bElem); bElem.setAttribute(TQString::fromLatin1("name"), borrower_->name()); bElem.setAttribute(TQString::fromLatin1("uid"), borrower_->uid()); const Data::LoanVec& loans = borrower_->loans(); for(Data::LoanVec::ConstIterator it = loans.constBegin(); it != loans.constEnd(); ++it) { TQDomElement lElem = dom_.createElement(TQString::fromLatin1("loan")); bElem.appendChild(lElem); lElem.setAttribute(TQString::fromLatin1("uid"), it->uid()); lElem.setAttribute(TQString::fromLatin1("entryRef"), it->entry()->id()); lElem.setAttribute(TQString::fromLatin1("loanDate"), it->loanDate().toString(Qt::ISODate)); lElem.setAttribute(TQString::fromLatin1("dueDate"), it->dueDate().toString(Qt::ISODate)); if(it->inCalendar()) { lElem.setAttribute(TQString::fromLatin1("calendar"), TQString::fromLatin1("true")); } lElem.appendChild(dom_.createTextNode(it->note())); } } TQWidget* TellicoXMLExporter::widget(TQWidget* parent_, const char* name_/*=0*/) { if(m_widget && TQT_BASE_OBJECT(m_widget->parent()) == TQT_BASE_OBJECT(parent_)) { return m_widget; } m_widget = new TQWidget(parent_, name_); TQVBoxLayout* l = new TQVBoxLayout(m_widget); TQGroupBox* box = new TQGroupBox(1, Qt::Horizontal, i18n("Tellico XML Options"), m_widget); l->addWidget(box); m_checkIncludeImages = new TQCheckBox(i18n("Include images in XML document"), box); m_checkIncludeImages->setChecked(m_includeImages); TQWhatsThis::add(m_checkIncludeImages, i18n("If checked, the images in the document will be included " "in the XML stream as base64 encoded elements.")); return m_widget; } void TellicoXMLExporter::readOptions(KConfig* config_) { KConfigGroup group(config_, TQString::fromLatin1("ExportOptions - %1").arg(formatString())); m_includeImages = group.readBoolEntry("Include Images", m_includeImages); } void TellicoXMLExporter::saveOptions(KConfig* config_) { m_includeImages = m_checkIncludeImages->isChecked(); KConfigGroup group(config_, TQString::fromLatin1("ExportOptions - %1").arg(formatString())); group.writeEntry("Include Images", m_includeImages); } #include "tellicoxmlexporter.moc"