#include "htmlexport.h" #include "services/pluginmanager.h" #include "common/unused.h" #include #include #include #include #include QString HtmlExport::getFormatName() const { return "HTML"; } ExportManager::StandardConfigFlags HtmlExport::standardOptionsToEnable() const { return ExportManager::CODEC; } QString HtmlExport::getExportConfigFormName() const { return "HtmlExportConfig"; } void HtmlExport::validateOptions() { bool header = cfg.HtmlExport.PrintHeader.get(); EXPORT_MANAGER->updateVisibilityAndEnabled(cfg.HtmlExport.PrintDataTypes, true, header); } QString HtmlExport::defaultFileExtension() const { return "html"; } CfgMain* HtmlExport::getConfig() { return &cfg; } bool HtmlExport::beforeExportQueryResults(const QString& query, QList& columns, const QHash providedData) { UNUSED(query); UNUSED(providedData); if (!beginDoc(tr("SQL query results"))) return false; columnTypes = QueryExecutor::resolveColumnTypes(db, columns, true); writeln(""); incrIndent(); if (printHeader) { writeln(""); incrIndent(); if (printRownum) { writeln(""); } QString column; int i = 0; for (const QueryExecutor::ResultColumnPtr& col : columns) { writeln(""); i++; } decrIndent(); writeln(""); } currentDataRow = 0; return true; } bool HtmlExport::exportQueryResultsRow(SqlResultsRowPtr row) { return exportDataRow(row); } bool HtmlExport::afterExportQueryResults() { decrIndent(); writeln("
"); incrIndent(); writeln("#"); decrIndent(); writeln(""); incrIndent(); column = QString("%1").arg(col->displayName); if (printDatatypes) { if (!columnTypes[i].isNull()) column.append("
" + columnTypes[i].toFullTypeString()); else column.append("
" + tr("no type")); } writeln(column); decrIndent(); writeln("
"); writeln("

"); return true; } bool HtmlExport::exportTable(const QString& database, const QString& table, const QStringList& columnNames, const QString& ddl, SqliteCreateTablePtr createTable, const QHash providedData) { UNUSED(database); UNUSED(ddl); UNUSED(columnNames); UNUSED(providedData); if (isTableExport()) { if (!beginDoc(tr("Exported table: %1").arg(table))) return false; } int colCount = createTable->columns.size(); int colSpan = printRownum ? colCount + 1 : colCount; writeln(""); incrIndent(); writeln(""); incrIndent(); writeln(QString("").arg(colSpan).arg(tr("Table: %1").arg(table))); decrIndent(); writeln(""); if (printHeader) { writeln(""); incrIndent(); if (printRownum) { writeln(""); } QString column; for (SqliteCreateTable::Column* col : createTable->columns) { writeln(""); } decrIndent(); writeln(""); } columnTypes.clear(); for (SqliteCreateTable::Column* col : createTable->columns) { if (col->type) columnTypes << col->type->toDataType(); else columnTypes << DataType(); } currentDataRow = 0; return true; } bool HtmlExport::exportDataRow(SqlResultsRowPtr data) { currentDataRow++; writeln(""); incrIndent(); if (printRownum) { writeln(""); } QString align; QString cellValue; QString cellStyle; int i = 0; for (const QVariant& value : data->valueList()) { if (columnTypes[i].isNumeric()) align = "right"; else align = "left"; if (value.isNull()) { cellValue = "NULL"; cellStyle = " class=\"null\""; } else { cellStyle = ""; if (value.toString().trimmed().isEmpty()) cellValue = " "; else { cellValue = value.toString(); cellValue.truncate(byteLengthLimit); cellValue = escape(cellValue); } } writeln(QString(""); i++; } decrIndent(); writeln(""); return true; } bool HtmlExport::exportVirtualTable(const QString& database, const QString& table, const QStringList& columnNames, const QString& ddl, SqliteCreateVirtualTablePtr createTable, const QHash providedData) { UNUSED(database); UNUSED(ddl); UNUSED(columnNames); UNUSED(createTable); UNUSED(providedData); if (isTableExport()) { if (!beginDoc(tr("Exported table: %1").arg(table))) return false; } int colCount = columnNames.size(); int colSpan = printRownum ? colCount + 1 : colCount; writeln("
%2
"); incrIndent(); writeln("#"); decrIndent(); writeln(""); incrIndent(); column = QString("%1").arg(col->name); if (printDatatypes) { if (col->type) column.append("
" + col->type->toDataType().toFullTypeString()); else column.append("
" + tr("no type")); } writeln(column); decrIndent(); writeln("
"); incrIndent(); writeln(QString("%1").arg(currentDataRow)); decrIndent(); writeln("").arg(align, cellStyle)); incrIndent(); writeln(cellValue); decrIndent(); writeln("
"); incrIndent(); writeln(""); incrIndent(); writeln(QString("").arg(colSpan).arg(tr("Table: %1").arg(table), tr("virtual"))); decrIndent(); writeln(""); if (printHeader) { writeln(""); incrIndent(); if (printRownum) { writeln(""); } QString column; for (const QString& col : columnNames) { writeln(""); } decrIndent(); writeln(""); } columnTypes.clear(); for (int i = 0, total = columnNames.size(); i < total; ++i) columnTypes << DataType(); currentDataRow = 0; return true; } bool HtmlExport::exportTableRow(SqlResultsRowPtr data) { return exportDataRow(data); } bool HtmlExport::afterExportTable() { decrIndent(); writeln("
%2 (%3)
"); incrIndent(); writeln("#"); decrIndent(); writeln(""); incrIndent(); writeln(QString("%1").arg(col)); decrIndent(); writeln("
"); writeln("

"); return true; } bool HtmlExport::beforeExportDatabase(const QString& database) { if (!beginDoc(tr("Exported database: %1").arg(database))) return false; return true; } bool HtmlExport::exportIndex(const QString& database, const QString& name, const QString& ddl, SqliteCreateIndexPtr createIndex) { UNUSED(database); UNUSED(ddl); writeln(""); incrIndent(); writeln(""); incrIndent(); writeln(QString("").arg(tr("Index: %1").arg("" + name + ""))); decrIndent(); writeln(""); writeln(""); incrIndent(); writeln(QString("").arg(tr("For table:"))); writeln(QString("").arg(createIndex->table)); decrIndent(); writeln(""); writeln(""); incrIndent(); writeln(QString("").arg(tr("Unique:"))); writeln(QString("").arg(createIndex->uniqueKw ? tr("Yes") : tr("No"))); decrIndent(); writeln(""); writeln(""); incrIndent(); writeln(QString("").arg(tr("Column"))); writeln(QString("").arg(tr("Collating"))); writeln(QString("").arg(tr("Sort order"))); decrIndent(); writeln(""); QString collate; for (SqliteOrderBy* idxCol : createIndex->indexedColumns) { collate = idxCol->getCollation(); writeln(""); incrIndent(); writeln(QString("").arg(escape(idxCol->getColumnString()))); writeln(QString("").arg(collate.isNull() ? " " : escape(collate))); writeln(QString("").arg(idxCol->order == SqliteSortOrder::null ? " " : sqliteSortOrder(idxCol->order))); decrIndent(); writeln(""); } decrIndent(); writeln("
%1
%1%1
%1%1
%1%1%1
%1%1%1
"); writeln("

"); return true; } bool HtmlExport::exportTrigger(const QString& database, const QString& name, const QString& ddl, SqliteCreateTriggerPtr createTrigger) { UNUSED(database); UNUSED(ddl); writeln(""); incrIndent(); writeln(""); incrIndent(); writeln(QString("").arg(tr("Trigger: %1").arg("" + name + ""))); decrIndent(); writeln(""); writeln(""); incrIndent(); writeln(QString("").arg(tr("Activated:"))); writeln(QString("").arg(SqliteCreateTrigger::time(createTrigger->eventTime))); decrIndent(); writeln(""); QString event = createTrigger->event ? SqliteCreateTrigger::Event::typeToString(createTrigger->event->type) : ""; writeln(""); incrIndent(); writeln(QString("").arg(tr("Action:"))); writeln(QString("").arg(event)); decrIndent(); writeln(""); QString onObj; if (createTrigger->eventTime == SqliteCreateTrigger::Time::INSTEAD_OF) onObj = tr("On view:"); else onObj = tr("On table:"); writeln(""); incrIndent(); writeln(QString("").arg(onObj)); writeln(QString("").arg(createTrigger->table)); decrIndent(); writeln(""); writeln(""); incrIndent(); writeln(QString("").arg(tr("Activate condition:"))); writeln(QString("").arg(createTrigger->precondition ? escape(createTrigger->precondition->detokenize()) : "")); decrIndent(); writeln(""); writeln(""); incrIndent(); writeln(QString("").arg(tr("Code executed:"))); decrIndent(); writeln(""); QStringList queryStrings; for (SqliteQuery* q : createTrigger->queries) queryStrings << escape(q->detokenize()); writeln(""); incrIndent(); writeln(""); decrIndent(); writeln(""); decrIndent(); writeln("
%1
%1%1
%1%1
%1%1
%1%1
%1
"); incrIndent(); writeln(QString("
%1
").arg(queryStrings.join("
"))); decrIndent(); writeln("
"); writeln("

"); return true; } bool HtmlExport::exportView(const QString& database, const QString& name, const QString& ddl, SqliteCreateViewPtr view) { UNUSED(database); UNUSED(ddl); writeln(""); incrIndent(); writeln(""); incrIndent(); writeln(QString("").arg(tr("View: %1").arg("" + name + ""))); decrIndent(); writeln(""); writeln(""); incrIndent(); writeln(""); decrIndent(); writeln(""); decrIndent(); writeln("
%1
"); incrIndent(); writeln(QString("
%1
").arg(escape(view->select->detokenize()))); decrIndent(); writeln("
"); writeln("

"); return true; } bool HtmlExport::afterExport() { static const QString bodyEndTpl = QStringLiteral(""); static const QString docEnd = QStringLiteral(""); writeln("" + tr("Document generated by SQLiteStudio v%1 on %2").arg(SQLITESTUDIO->getVersionString(), QDateTime::currentDateTime().toString()) + ""); decrIndent(); writeln(bodyEndTpl); decrIndent(); writeln(docEnd); return true; } bool HtmlExport::beginDoc(const QString& title) { static const QString docStart = QStringLiteral( R"()" "\n" ); static const QString metaCodecTpl = QStringLiteral(R"()"); static const QString titletpl = QStringLiteral(R"(%1)"); static const QString styleStartTpl = QStringLiteral(R"()"); static const QString bodyStartTpl = QStringLiteral(R"()"); setupConfig(); QFile file(":/htmlexport/htmlexport.css"); if (!file.open(QIODevice::ReadOnly)) { qCritical() << "Could not open htmlexport.css resource while exporting to HTML:" << file.errorString(); return false; } writeln(docStart); incrIndent(); writeln(metaCodecTpl.arg(codecName)); writeln(titletpl.arg(title)); writeln(styleStartTpl); incrIndent(); writeln(indent ? file.readAll() : compressCss(file.readAll())); decrIndent(); writeln(styleEndTpl); writeln(bodyStartTpl); incrIndent(); file.close(); return true; } void HtmlExport::setupConfig() { codecName = codec->name(); indentDepth = 0; newLineStr = ""; indentStr = ""; indent = (cfg.HtmlExport.Format.get() == "format"); if (indent) newLineStr = "\n"; printRownum = cfg.HtmlExport.PrintRowNum.get(); printHeader = cfg.HtmlExport.PrintHeader.get(); printDatatypes = printHeader && cfg.HtmlExport.PrintDataTypes.get(); byteLengthLimit = cfg.HtmlExport.ByteLengthLimit.get(); } void HtmlExport::incrIndent() { if (indent) { indentDepth++; updateIndent(); } } void HtmlExport::decrIndent() { if (indent) { indentDepth--; updateIndent(); } } void HtmlExport::updateIndent() { indentStr = QString(" ").repeated(indentDepth); } void HtmlExport::writeln(const QString& str) { QString newStr; if (str.contains("\n")) { QStringList lines = str.split("\n"); QMutableStringListIterator it(lines); while (it.hasNext()) it.next().prepend(indentStr); newStr = lines.join("\n") + newLineStr; } else { newStr = indentStr + str + newLineStr; } GenericExportPlugin::write(newStr); } QString HtmlExport::escape(const QString& str) { if (cfg.HtmlExport.DontEscapeHtml.get()) return str; return str.toHtmlEscaped(); } QString HtmlExport::compressCss(QString css) { static const QRegExp spacesLeftRe(R"REGEXP(([^a-zA-Z0-9_\s]+)\s+(\S+))REGEXP"); static const QRegExp spacesRightRe(R"REGEXP((\S+)\s+([^a-zA-Z0-9_\s]+))REGEXP"); static const QRegExp spacesBetweenWordsRe(R"REGEXP((\S+)\s{2,}(\S+))REGEXP"); while (spacesLeftRe.indexIn(css) > -1) css.replace(spacesLeftRe, R"(\1\2)"); while (spacesRightRe.indexIn(css) > -1) css.replace(spacesRightRe, R"(\1\2)"); while (spacesBetweenWordsRe.indexIn(css) > -1) css.replace(spacesBetweenWordsRe, R"(\1 \2)"); return css.trimmed(); } bool HtmlExport::init() { Q_INIT_RESOURCE(htmlexport); return GenericExportPlugin::init(); } void HtmlExport::deinit() { Q_CLEANUP_RESOURCE(htmlexport); }