You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tellico/src/translators/htmlexporter.cpp

820 lines
28 KiB

/***************************************************************************
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 "htmlexporter.h"
#include "xslthandler.h"
#include "tellicoxmlexporter.h"
#include "../document.h"
#include "../collection.h"
#include "../filehandler.h"
#include "../imagefactory.h"
#include "../latin1literal.h"
#include "../tellico_kernel.h"
#include "../tellico_utils.h"
#include "../progressmanager.h"
#include "../core/tellico_config.h"
#include "../tellico_debug.h"
#include <kstandarddirs.h>
#include <tdeconfig.h>
#include <tdeglobal.h>
#include <tdeio/netaccess.h>
#include <tdeapplication.h>
#include <tdelocale.h>
#include <kuser.h>
#include <tqdom.h>
#include <tqgroupbox.h>
#include <tqlayout.h>
#include <tqcheckbox.h>
#include <tqwhatsthis.h>
#include <tqfile.h>
#include <tqhbox.h>
#include <tqlabel.h>
extern "C" {
#include <libxml/HTMLparser.h>
#include <libxml/HTMLtree.h>
}
using Tellico::Export::HTMLExporter;
HTMLExporter::HTMLExporter() : Tellico::Export::Exporter(),
m_handler(0),
m_printHeaders(true),
m_printGrouped(false),
m_exportEntryFiles(false),
m_cancelled(false),
m_parseDOM(true),
m_checkCreateDir(true),
m_imageWidth(0),
m_imageHeight(0),
m_widget(0),
m_xsltFile(TQString::fromLatin1("tellico2html.xsl")) {
}
HTMLExporter::HTMLExporter(Data::CollPtr coll_) : Tellico::Export::Exporter(coll_),
m_handler(0),
m_printHeaders(true),
m_printGrouped(false),
m_exportEntryFiles(false),
m_cancelled(false),
m_parseDOM(true),
m_checkCreateDir(true),
m_imageWidth(0),
m_imageHeight(0),
m_widget(0),
m_xsltFile(TQString::fromLatin1("tellico2html.xsl")) {
}
HTMLExporter::~HTMLExporter() {
delete m_handler;
m_handler = 0;
}
TQString HTMLExporter::formatString() const {
return i18n("HTML");
}
TQString HTMLExporter::fileFilter() const {
return i18n("*.html|HTML Files (*.html)") + TQChar('\n') + i18n("*|All Files");
}
void HTMLExporter::reset() {
// since the ExportUTF8 option may have changed, need to delete handler
delete m_handler;
m_handler = 0;
m_files.clear();
m_links.clear();
m_copiedFiles.clear();
}
bool HTMLExporter::exec() {
if(url().isEmpty() || !url().isValid()) {
kdWarning() << "HTMLExporter::exec() - trying to export to invalid URL" << endl;
return false;
}
// check file exists first
// if we're not forcing, ask use
bool force = (options() & Export::ExportForce) || FileHandler::queryExists(url());
if(!force) {
return false;
}
if(!m_parseDOM) {
return FileHandler::writeTextURL(url(), text(), options() & Export::ExportUTF8, force);
}
m_cancelled = false;
// TODO: maybe need label?
if(options() & ExportProgress) {
ProgressItem& item = ProgressManager::self()->newProgressItem(this, TQString(), true);
item.setTotalSteps(100);
connect(&item, TQT_SIGNAL(signalCancelled(ProgressItem*)), TQT_SLOT(slotCancel()));
}
// ok if not ExportProgress, no worries
ProgressItem::Done done(this);
htmlDocPtr htmlDoc = htmlParseDoc(reinterpret_cast<xmlChar*>(text().utf8().data()), NULL);
xmlNodePtr root = xmlDocGetRootElement(htmlDoc);
if(root == 0) {
myDebug() << "HTMLExporter::exec() - no root" << endl;
return false;
}
parseDOM(root);
if(m_cancelled) {
return true; // intentionally cancelled
}
ProgressManager::self()->setProgress(this, 15);
xmlChar* c;
int bytes;
htmlDocDumpMemory(htmlDoc, &c, &bytes);
TQString allText;
if(bytes > 0) {
allText = TQString::fromUtf8(reinterpret_cast<const char*>(c), bytes);
xmlFree(c);
}
if(m_cancelled) {
return true; // intentionally cancelled
}
ProgressManager::self()->setProgress(this, 20);
bool success = FileHandler::writeTextURL(url(), allText, options() & Export::ExportUTF8, force);
success &= copyFiles() && (!m_exportEntryFiles || writeEntryFiles());
return success;
}
bool HTMLExporter::loadXSLTFile() {
TQString xsltfile = locate("appdata", m_xsltFile);
if(xsltfile.isNull()) {
myDebug() << "HTMLExporter::loadXSLTFile() - no xslt file for " << m_xsltFile << endl;
return false;
}
KURL u;
u.setPath(xsltfile);
// do NOT do namespace processing, it messes up the XSL declaration since
// TQDom thinks there are no elements in the Tellico namespace and as a result
// removes the namespace declaration
TQDomDocument dom = FileHandler::readXMLFile(u, false);
if(dom.isNull()) {
myDebug() << "HTMLExporter::loadXSLTFile() - error loading xslt file: " << xsltfile << endl;
return false;
}
// notes about utf-8 encoding:
// all params should be passed to XSLTHandler in utf8
// input string to XSLTHandler should be in utf-8, EVEN IF DOM STRING SAYS OTHERWISE
// the stylesheet prints utf-8 by default, if using locale encoding, need
// to change the encoding attribute on the xsl:output element
if(!(options() & Export::ExportUTF8)) {
XSLTHandler::setLocaleEncoding(dom);
}
delete m_handler;
m_handler = new XSLTHandler(dom, TQFile::encodeName(xsltfile), true /*translate*/);
if(!m_handler->isValid()) {
delete m_handler;
m_handler = 0;
return false;
}
m_handler->addStringParam("date", TQDate::currentDate().toString(TQt::ISODate).latin1());
m_handler->addStringParam("time", TQTime::currentTime().toString(TQt::ISODate).latin1());
m_handler->addStringParam("user", KUser(KUser::UseRealUserID).loginName().latin1());
if(m_exportEntryFiles) {
// export entries to same place as all the other date files
m_handler->addStringParam("entrydir", TQFile::encodeName(fileDir().fileName())+ '/');
// be sure to link all the entries
m_handler->addParam("link-entries", "true()");
}
if(!m_collectionURL.isEmpty()) {
TQString s = TQString::fromLatin1("../") + m_collectionURL.fileName();
m_handler->addStringParam("collection-file", s.utf8());
}
// look for a file that gets installed to know the installation directory
// if parseDOM, that means we want the locations to be the actual location
// otherwise, we assume it'll be relative
if(m_parseDOM && m_dataDir.isEmpty()) {
m_dataDir = TDEGlobal::dirs()->findResourceDir("appdata", TQString::fromLatin1("pics/tellico.png"));
} else if(!m_parseDOM) {
m_dataDir.truncate(0);
}
if(!m_dataDir.isEmpty()) {
m_handler->addStringParam("datadir", TQFile::encodeName(m_dataDir));
}
setFormattingOptions(collection());
return m_handler->isValid();
}
TQString HTMLExporter::text() {
if((!m_handler || !m_handler->isValid()) && !loadXSLTFile()) {
kdWarning() << "HTMLExporter::text() - error loading xslt file: " << m_xsltFile << endl;
return TQString();
}
Data::CollPtr coll = collection();
if(!coll) {
myDebug() << "HTMLExporter::text() - no collection pointer!" << endl;
return TQString();
}
if(m_groupBy.isEmpty()) {
m_printGrouped = false; // can't group if no groups exist
}
GUI::CursorSaver cs;
writeImages(coll);
// now grab the XML
TellicoXMLExporter exporter(coll);
exporter.setURL(url());
exporter.setEntries(entries());
exporter.setIncludeGroups(m_printGrouped);
// yes, this should be in utf8, always
exporter.setOptions(options() | Export::ExportUTF8 | Export::ExportImages);
TQDomDocument output = exporter.exportXML();
#if 0
TQFile f(TQString::fromLatin1("/tmp/test.xml"));
if(f.open(IO_WriteOnly)) {
TQTextStream t(&f);
t << output.toString();
}
f.close();
#endif
TQString text = m_handler->applyStylesheet(output.toString());
#if 0
TQFile f2(TQString::fromLatin1("/tmp/test.html"));
if(f2.open(IO_WriteOnly)) {
TQTextStream t(&f2);
t << text;
// t << "\n\n-------------------------------------------------------\n\n";
// t << Tellico::i18nReplace(text);
}
f2.close();
#endif
// the XSLT file gets translated instead
// return Tellico::i18nReplace(text);
return text;
}
void HTMLExporter::setFormattingOptions(Data::CollPtr coll) {
TQString file = Kernel::self()->URL().fileName();
if(file != i18n("Untitled")) {
m_handler->addStringParam("filename", TQFile::encodeName(file));
}
m_handler->addStringParam("cdate", TDEGlobal::locale()->formatDate(TQDate::currentDate()).utf8());
m_handler->addParam("show-headers", m_printHeaders ? "true()" : "false()");
m_handler->addParam("group-entries", m_printGrouped ? "true()" : "false()");
TQStringList sortTitles;
if(!m_sort1.isEmpty()) {
sortTitles << m_sort1;
}
if(!m_sort2.isEmpty()) {
sortTitles << m_sort2;
}
// the third sort column may be same as first
if(!m_sort3.isEmpty() && sortTitles.findIndex(m_sort3) == -1) {
sortTitles << m_sort3;
}
if(sortTitles.count() > 0) {
m_handler->addStringParam("sort-name1", coll->fieldNameByTitle(sortTitles[0]).utf8());
if(sortTitles.count() > 1) {
m_handler->addStringParam("sort-name2", coll->fieldNameByTitle(sortTitles[1]).utf8());
if(sortTitles.count() > 2) {
m_handler->addStringParam("sort-name3", coll->fieldNameByTitle(sortTitles[2]).utf8());
}
}
}
// no longer showing "sorted by..." since the column headers are clickable
// but still use "grouped by"
TQString sortString;
if(m_printGrouped) {
TQString s;
// if more than one, then it's the People pseudo-group
if(m_groupBy.count() > 1) {
s = i18n("People");
} else {
s = coll->fieldTitleByName(m_groupBy[0]);
}
sortString = i18n("(grouped by %1)").arg(s);
TQString groupFields;
for(TQStringList::ConstIterator it = m_groupBy.begin(); it != m_groupBy.end(); ++it) {
Data::FieldPtr f = coll->fieldByName(*it);
if(!f) {
continue;
}
if(f->flags() & Data::Field::AllowMultiple) {
groupFields += TQString::fromLatin1("tc:") + *it + TQString::fromLatin1("s/tc:") + *it;
} else {
groupFields += TQString::fromLatin1("tc:") + *it;
}
int ncols = 0;
if(f->type() == Data::Field::Table) {
bool ok;
ncols = Tellico::toUInt(f->property(TQString::fromLatin1("columns")), &ok);
if(!ok) {
ncols = 1;
}
}
if(ncols > 1) {
groupFields += TQString::fromLatin1("/tc:column[1]");
}
if(*it != m_groupBy.last()) {
groupFields += '|';
}
}
// myDebug() << groupFields << endl;
m_handler->addStringParam("group-fields", groupFields.utf8());
m_handler->addStringParam("sort-title", sortString.utf8());
}
TQString pageTitle = coll->title();
pageTitle += TQChar(' ') + sortString;
m_handler->addStringParam("page-title", pageTitle.utf8());
TQStringList showFields;
for(TQStringList::ConstIterator it = m_columns.begin(); it != m_columns.end(); ++it) {
showFields << coll->fieldNameByTitle(*it);
}
m_handler->addStringParam("column-names", showFields.join(TQChar(' ')).utf8());
if(m_imageWidth > 0 && m_imageHeight > 0) {
m_handler->addParam("image-width", TQCString().setNum(m_imageWidth));
m_handler->addParam("image-height", TQCString().setNum(m_imageHeight));
}
// add system colors to stylesheet
const int type = coll->type();
m_handler->addStringParam("font", TQString(Config::templateFont(type).family()).latin1());
m_handler->addStringParam("fontsize", TQCString().setNum(Config::templateFont(type).pointSize()));
m_handler->addStringParam("bgcolor", TQString(Config::templateBaseColor(type).name()).latin1());
m_handler->addStringParam("fgcolor", TQString(Config::templateTextColor(type).name()).latin1());
m_handler->addStringParam("color1", TQString(Config::templateHighlightedTextColor(type).name()).latin1());
m_handler->addStringParam("color2", TQString(Config::templateHighlightedBaseColor(type).name()).latin1());
// add locale code to stylesheet (for sorting)
m_handler->addStringParam("lang", TDEGlobal::locale()->languagesTwoAlpha().first().utf8());
}
void HTMLExporter::writeImages(Data::CollPtr coll_) {
// keep track of which image fields to write, this is for field names
StringSet imageFields;
for(TQStringList::ConstIterator it = m_columns.begin(); it != m_columns.end(); ++it) {
if(coll_->fieldByTitle(*it)->type() == Data::Field::Image) {
imageFields.add(*it);
}
}
// all the images potentially used in the HTML export need to be written to disk
// if we're exporting entry files, then we'll certainly want all the image fields written
// if we're not exporting to a file, then we might be exporting an entry template file
// and so we need to write all of them too.
if(m_exportEntryFiles || url().isEmpty()) {
// add all image fields to string list
Data::FieldVec fields = coll_->imageFields();
for(Data::FieldVec::Iterator fieldIt = fields.begin(); fieldIt != fields.end(); ++fieldIt) {
imageFields.add(fieldIt->name());
}
}
// all of them are going to get written to tmp file
bool useTemp = url().isEmpty();
KURL imgDir;
TQString imgDirRelative;
// really some convoluted logic here
// basically, four cases. 1) we're writing to a tmp file, for printing probably
// so then write all the images to the tmp directory, 2) we're exporting to HTML, and
// this is the main collection file, in which case m_parseDOM is always true;
// 3) we're exporting HTML, and this is the first entry file, for which parseDOM is true
// and exportEntryFiles is false. Then the image file will get copied in copyFiles() and is
// probably an image in the entry template. 4) we're exporting HTML, and this is not the
// first entry file, in which case, we want to refer directly to the target dir
if(useTemp) { // everything goes in the tmp dir
imgDir.setPath(ImageFactory::tempDir());
imgDirRelative = imgDir.path();
} else if(m_parseDOM) {
imgDir = fileDir(); // copy to fileDir
imgDirRelative = Data::Document::self()->allImagesOnDisk() ? ImageFactory::dataDir() : ImageFactory::tempDir();
createDir();
} else {
imgDir = fileDir();
imgDirRelative = KURL::relativeURL(url(), imgDir);
createDir();
}
m_handler->addStringParam("imgdir", TQFile::encodeName(imgDirRelative));
int count = 0;
const int processCount = 100; // process after every 100 events
TQStringList fieldsList = imageFields.toList();
StringSet imageSet; // track which images are written
for(TQStringList::ConstIterator fieldName = fieldsList.begin(); fieldName != fieldsList.end(); ++fieldName) {
for(Data::EntryVec::ConstIterator entryIt = entries().begin(); entryIt != entries().end(); ++entryIt) {
TQString id = entryIt->field(*fieldName);
// if no id or is already writen, continue
if(id.isEmpty() || imageSet.has(id)) {
continue;
}
imageSet.add(id);
// try writing
bool success = useTemp ? ImageFactory::writeCachedImage(id, ImageFactory::TempDir)
: ImageFactory::writeImage(id, imgDir, true);
if(!success) {
kdWarning() << "HTMLExporter::writeImages() - unable to write image file: "
<< imgDir.path() << id << endl;
}
if(++count == processCount) {
kapp->processEvents();
count = 0;
}
}
}
}
TQWidget* HTMLExporter::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("HTML Options"), m_widget);
l->addWidget(box);
m_checkPrintHeaders = new TQCheckBox(i18n("Print field headers"), box);
TQWhatsThis::add(m_checkPrintHeaders, i18n("If checked, the field names will be "
"printed as table headers."));
m_checkPrintHeaders->setChecked(m_printHeaders);
m_checkPrintGrouped = new TQCheckBox(i18n("Group the entries"), box);
TQWhatsThis::add(m_checkPrintGrouped, i18n("If checked, the entries will be grouped by "
"the selected field."));
m_checkPrintGrouped->setChecked(m_printGrouped);
m_checkExportEntryFiles = new TQCheckBox(i18n("Export individual entry files"), box);
TQWhatsThis::add(m_checkExportEntryFiles, i18n("If checked, individual files will be created for each entry."));
m_checkExportEntryFiles->setChecked(m_exportEntryFiles);
l->addStretch(1);
return m_widget;
}
void HTMLExporter::readOptions(TDEConfig* config_) {
TDEConfigGroup exportConfig(config_, TQString::fromLatin1("ExportOptions - %1").arg(formatString()));
m_printHeaders = exportConfig.readBoolEntry("Print Field Headers", m_printHeaders);
m_printGrouped = exportConfig.readBoolEntry("Print Grouped", m_printGrouped);
m_exportEntryFiles = exportConfig.readBoolEntry("Export Entry Files", m_exportEntryFiles);
// read current entry export template
m_entryXSLTFile = Config::templateName(collection()->type());
m_entryXSLTFile = locate("appdata", TQString::fromLatin1("entry-templates/")
+ m_entryXSLTFile + TQString::fromLatin1(".xsl"));
}
void HTMLExporter::saveOptions(TDEConfig* config_) {
TDEConfigGroup cfg(config_, TQString::fromLatin1("ExportOptions - %1").arg(formatString()));
m_printHeaders = m_checkPrintHeaders->isChecked();
cfg.writeEntry("Print Field Headers", m_printHeaders);
m_printGrouped = m_checkPrintGrouped->isChecked();
cfg.writeEntry("Print Grouped", m_printGrouped);
m_exportEntryFiles = m_checkExportEntryFiles->isChecked();
cfg.writeEntry("Export Entry Files", m_exportEntryFiles);
}
void HTMLExporter::setXSLTFile(const TQString& filename_) {
if(m_xsltFile == filename_) {
return;
}
m_xsltFile = filename_;
m_xsltFilePath = TQString();
reset();
}
KURL HTMLExporter::fileDir() const {
if(url().isEmpty()) {
return KURL();
}
KURL fileDir = url();
// cd to directory of target URL
fileDir.cd(TQString::fromLatin1(".."));
fileDir.addPath(fileDirName());
return fileDir;
}
TQString HTMLExporter::fileDirName() const {
if(!m_collectionURL.isEmpty()) {
return TQString::fromLatin1("/");
}
return url().fileName().section('.', 0, 0) + TQString::fromLatin1("_files/");
}
// how ugly is this?
const xmlChar* HTMLExporter::handleLink(const xmlChar* link_) {
return reinterpret_cast<xmlChar*>(tqstrdup(handleLink(TQString::fromUtf8(reinterpret_cast<const char*>(link_))).utf8()));
}
TQString HTMLExporter::handleLink(const TQString& link_) {
if(m_links.contains(link_)) {
return m_links[link_];
}
// assume that if the link_ is not relative, then we don't need to copy it
if(!KURL::isRelativeURL(link_)) {
return link_;
}
if(m_xsltFilePath.isEmpty()) {
m_xsltFilePath = locate("appdata", m_xsltFile);
if(m_xsltFilePath.isNull()) {
kdWarning() << "HTMLExporter::handleLink() - no xslt file for " << m_xsltFile << endl;
}
}
KURL u;
u.setPath(m_xsltFilePath);
u = KURL(u, link_);
// one of the "quirks" of the html export is that img src urls are set to point to
// the tmpDir() when exporting entry files from a collection, but those images
// don't actually exist, and they get copied in writeImages() instead.
// so we only need to keep track of the url if it exists
const bool exists = TDEIO::NetAccess::exists(u, false, 0);
if(exists) {
m_files.append(u);
}
// if we're exporting entry files, we want pics/ to
// go in pics/
const bool isPic = link_.startsWith(m_dataDir + TQString::fromLatin1("pics/"));
TQString midDir;
if(m_exportEntryFiles && isPic) {
midDir = TQString::fromLatin1("pics/");
}
// pictures are special since they might not exist when the HTML is exported, since they might get copied later
// on the other hand, don't change the file location if it doesn't exist
if(isPic || exists) {
m_links.insert(link_, fileDirName() + midDir + u.fileName());
} else {
m_links.insert(link_, link_);
}
return m_links[link_];
}
const xmlChar* HTMLExporter::analyzeInternalCSS(const xmlChar* str_) {
return reinterpret_cast<xmlChar*>(tqstrdup(analyzeInternalCSS(TQString::fromUtf8(reinterpret_cast<const char*>(str_))).utf8()));
}
TQString HTMLExporter::analyzeInternalCSS(const TQString& str_) {
TQString str = str_;
int start = 0;
int end = 0;
const TQString url = TQString::fromLatin1("url(");
for(int pos = str.find(url); pos >= 0; pos = str.find(url, pos+1)) {
pos += 4; // url(
if(str[pos] == '"' || str[pos] == '\'') {
++pos;
}
start = pos;
pos = str.find(')', start);
end = pos;
if(str[pos-1] == '"' || str[pos-1] == '\'') {
--end;
}
str.replace(start, end-start, handleLink(str.mid(start, end-start)));
}
return str;
}
void HTMLExporter::createDir() {
if(!m_checkCreateDir) {
return;
}
KURL dir = fileDir();
if(dir.isEmpty()) {
myDebug() << "HTMLExporter::createDir() - called on empty URL!" << endl;
return;
}
if(TDEIO::NetAccess::exists(dir, false, 0)) {
m_checkCreateDir = false;
} else {
m_checkCreateDir = !TDEIO::NetAccess::mkdir(dir, m_widget);
}
}
bool HTMLExporter::copyFiles() {
if(m_files.isEmpty()) {
return true;
}
const uint start = 20;
const uint maxProgress = m_exportEntryFiles ? 40 : 80;
const uint stepSize = TQMAX(1, m_files.count()/maxProgress);
uint j = 0;
createDir();
KURL target;
for(KURL::List::ConstIterator it = m_files.begin(); it != m_files.end() && !m_cancelled; ++it, ++j) {
if(m_copiedFiles.has((*it).url())) {
continue;
}
if(target.isEmpty()) {
target = fileDir();
}
target.setFileName((*it).fileName());
bool success = TDEIO::NetAccess::file_copy(*it, target, -1, true /* overwrite */, false /* resume */, m_widget);
if(success) {
m_copiedFiles.add((*it).url());
} else {
kdWarning() << "HTMLExporter::copyFiles() - can't copy " << target << endl;
kdWarning() << TDEIO::NetAccess::lastErrorString() << endl;
}
if(j%stepSize == 0) {
if(options() & ExportProgress) {
ProgressManager::self()->setProgress(this, TQMIN(start+j/stepSize, 99));
}
kapp->processEvents();
}
}
return true;
}
bool HTMLExporter::writeEntryFiles() {
if(m_entryXSLTFile.isEmpty()) {
kdWarning() << "HTMLExporter::writeEntryFiles() - no entry XSLT file" << endl;
return false;
}
const uint start = 60;
const uint stepSize = TQMAX(1, entries().count()/40);
uint j = 0;
// now worry about actually exporting entry files
// I can't reliable encode a string as a URI, so I'm punting, and I'll just replace everything but
// a-zA-Z0-9 with an underscore. This MUST match the filename template in tellico2html.xsl
// the id is used so uniqueness is guaranteed
const TQRegExp badChars(TQString::fromLatin1("[^-a-zA-Z0-9]"));
bool formatted = options() & Export::ExportFormatted;
KURL outputFile = fileDir();
GUI::CursorSaver cs(TQt::waitCursor);
HTMLExporter exporter(collection());
long opt = options() | Export::ExportForce;
opt &= ~ExportProgress;
exporter.setOptions(opt);
exporter.setXSLTFile(m_entryXSLTFile);
exporter.setCollectionURL(url());
bool parseDOM = true;
const TQString title = TQString::fromLatin1("title");
const TQString html = TQString::fromLatin1(".html");
bool multipleTitles = collection()->fieldByName(title)->flags() & Data::Field::AllowMultiple;
Data::EntryVec entries = this->entries(); // not const since the pointer has to be copied
for(Data::EntryVecIt entryIt = entries.begin(); entryIt != entries.end() && !m_cancelled; ++entryIt, ++j) {
TQString file = entryIt->field(title, formatted);
// but only use the first title if it has multiple
if(multipleTitles) {
file = file.section(';', 0, 0);
}
file.replace(badChars, TQChar('_'));
file += TQChar('-') + TQString::number(entryIt->id()) + html;
outputFile.setFileName(file);
exporter.setEntries(Data::EntryVec(entryIt));
exporter.setURL(outputFile);
exporter.exec();
// no longer need to parse DOM
if(parseDOM) {
parseDOM = false;
exporter.setParseDOM(false);
// this is rather stupid, but I'm too lazy to figure out the better way
// since we parsed the DOM for the first entry file to grab any
// images used in the template, need to resave it so the image links
// get written correctly
exporter.exec();
}
if(j%stepSize == 0) {
if(options() & ExportProgress) {
ProgressManager::self()->setProgress(this, TQMIN(start+j/stepSize, 99));
}
kapp->processEvents();
}
}
// the images in "pics/" are special data images, copy them always
// since the entry files may refer to them, but we don't know that
TQStringList dataImages;
dataImages << TQString::fromLatin1("checkmark.png");
for(uint i = 1; i <= 10; ++i) {
dataImages << TQString::fromLatin1("stars%1.png").arg(i);
}
KURL dataDir;
dataDir.setPath(TDEGlobal::dirs()->findResourceDir("appdata", TQString::fromLatin1("pics/tellico.png")) + "pics/");
KURL target = fileDir();
target.addPath(TQString::fromLatin1("pics/"));
TDEIO::NetAccess::mkdir(target, m_widget);
for(TQStringList::ConstIterator it = dataImages.begin(); it != dataImages.end(); ++it) {
dataDir.setFileName(*it);
target.setFileName(*it);
TDEIO::NetAccess::copy(dataDir, target, m_widget);
}
return true;
}
void HTMLExporter::slotCancel() {
m_cancelled = true;
}
void HTMLExporter::parseDOM(xmlNode* node_) {
if(node_ == 0) {
myDebug() << "HTMLExporter::parseDOM() - no node" << endl;
return;
}
bool parseChildren = true;
if(node_->type == XML_ELEMENT_NODE) {
const TQCString nodeName = TQCString(reinterpret_cast<const char*>(node_->name)).upper();
xmlElement* elem = reinterpret_cast<xmlElement*>(node_);
// to speed up things, check now for nodename
if(nodeName == "IMG" || nodeName == "SCRIPT" || nodeName == "LINK") {
for(xmlAttribute* attr = elem->attributes; attr; attr = reinterpret_cast<xmlAttribute*>(attr->next)) {
TQCString attrName = TQCString(reinterpret_cast<const char*>(attr->name)).upper();
if( (attrName == "SRC" && (nodeName == "IMG" || nodeName == "SCRIPT")) ||
(attrName == "HREF" && nodeName == "LINK")) {
/* (attrName == "BACKGROUND" && (nodeName == "BODY" ||
nodeName == "TABLE" ||
nodeName == "TH" ||
nodeName == "TD"))) */
xmlChar* value = xmlGetProp(node_, attr->name);
if(value) {
xmlSetProp(node_, attr->name, handleLink(value));
xmlFree(value);
}
// each node only has one significant attribute, so break now
break;
}
}
} else if(nodeName == "STYLE") {
// if the first child is a CDATA, use it, otherwise replace complete node
xmlNode* nodeToReplace = node_;
xmlNode* child = node_->children;
if(child && child->type == XML_CDATA_SECTION_NODE) {
nodeToReplace = child;
}
xmlChar* value = xmlNodeGetContent(nodeToReplace);
if(value) {
xmlNodeSetContent(nodeToReplace, analyzeInternalCSS(value));
xmlFree(value);
}
// no longer need to parse child text nodes
parseChildren = false;
}
}
if(parseChildren) {
xmlNode* child = node_->children;
while(child) {
parseDOM(child);
child = child->next;
}
}
}
#include "htmlexporter.moc"