diff options
| author | 2014-12-06 17:33:25 -0500 | |
|---|---|---|
| committer | 2014-12-06 17:33:25 -0500 | |
| commit | 7167ce41b61d2ba2cdb526777a4233eb84a3b66a (patch) | |
| tree | a35c14143716e1f2c98f808c81f89426045a946f /Plugins/HtmlExport/htmlexport.cpp | |
Imported Upstream version 2.99.6upstream/2.99.6
Diffstat (limited to 'Plugins/HtmlExport/htmlexport.cpp')
| -rw-r--r-- | Plugins/HtmlExport/htmlexport.cpp | 615 |
1 files changed, 615 insertions, 0 deletions
diff --git a/Plugins/HtmlExport/htmlexport.cpp b/Plugins/HtmlExport/htmlexport.cpp new file mode 100644 index 0000000..75b3868 --- /dev/null +++ b/Plugins/HtmlExport/htmlexport.cpp @@ -0,0 +1,615 @@ +#include "htmlexport.h" +#include "services/pluginmanager.h" +#include "common/unused.h" +#include <QFile> +#include <QTextCodec> +#include <QDebug> +#include <QRegExp> +#include <schemaresolver.h> + +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<QueryExecutor::ResultColumnPtr>& columns, const QHash<ExportManager::ExportProviderFlag, QVariant> providedData) +{ + UNUSED(query); + UNUSED(providedData); + + if (!beginDoc(tr("SQL query results"))) + return false; + + columnTypes = QueryExecutor::resolveColumnTypes(db, columns, true); + + writeln("<table>"); + incrIndent(); + if (printHeader) + { + writeln("<tr class=\"header\">"); + incrIndent(); + if (printRownum) + { + writeln("<td align=\"right\">"); + incrIndent(); + writeln("<b><i>#</i></b>"); + decrIndent(); + writeln("</td>"); + } + + QString column; + int i = 0; + for (const QueryExecutor::ResultColumnPtr& col : columns) + { + writeln("<td>"); + incrIndent(); + column = QString("<b>%1</b>").arg(col->displayName); + if (printDatatypes) + { + if (!columnTypes[i].isNull()) + column.append("<br/>" + columnTypes[i].toFullTypeString()); + else + column.append("<br/>" + tr("no type")); + } + writeln(column); + decrIndent(); + writeln("</td>"); + i++; + } + decrIndent(); + writeln("</tr>"); + } + + currentDataRow = 0; + return true; +} + +bool HtmlExport::exportQueryResultsRow(SqlResultsRowPtr row) +{ + return exportDataRow(row); +} + +bool HtmlExport::afterExportQueryResults() +{ + decrIndent(); + writeln("</table>"); + writeln("<br/><br/>"); + return true; +} + +bool HtmlExport::exportTable(const QString& database, const QString& table, const QStringList& columnNames, const QString& ddl, SqliteCreateTablePtr createTable, const QHash<ExportManager::ExportProviderFlag, QVariant> 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("<table>"); + incrIndent(); + + writeln("<tr class=\"title\">"); + incrIndent(); + writeln(QString("<td colspan=\"%1\" align=\"center\">%2</td>").arg(colSpan).arg(tr("Table: %1").arg(table))); + decrIndent(); + writeln("</tr>"); + + if (printHeader) + { + writeln("<tr class=\"header\">"); + incrIndent(); + if (printRownum) + { + writeln("<td align=\"right\">"); + incrIndent(); + writeln("<b><i>#</i></b>"); + decrIndent(); + writeln("</td>"); + } + + QString column; + for (SqliteCreateTable::Column* col : createTable->columns) + { + writeln("<td>"); + incrIndent(); + column = QString("<b>%1</b>").arg(col->name); + if (printDatatypes) + { + if (col->type) + column.append("<br/>" + col->type->toDataType().toFullTypeString()); + else + column.append("<br/>" + tr("no type")); + } + writeln(column); + decrIndent(); + writeln("</td>"); + } + decrIndent(); + writeln("</tr>"); + } + + 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("<tr>"); + incrIndent(); + if (printRownum) + { + writeln("<td class=\"rownum\">"); + incrIndent(); + writeln(QString("<i>%1</i>").arg(currentDataRow)); + decrIndent(); + writeln("</td>"); + } + + 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 = "<i>NULL</i>"; + cellStyle = " class=\"null\""; + } + else + { + cellStyle = ""; + if (value.toString().trimmed().isEmpty()) + cellValue = " "; + else + { + cellValue = value.toString(); + cellValue.truncate(byteLengthLimit); + cellValue = escape(cellValue); + } + } + writeln(QString("<td align=\"%1\"%2>").arg(align, cellStyle)); + incrIndent(); + writeln(cellValue); + decrIndent(); + writeln("</td>"); + i++; + } + decrIndent(); + writeln("</tr>"); + return true; +} + +bool HtmlExport::exportVirtualTable(const QString& database, const QString& table, const QStringList& columnNames, const QString& ddl, SqliteCreateVirtualTablePtr createTable, const QHash<ExportManager::ExportProviderFlag, QVariant> 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("<table>"); + incrIndent(); + + writeln("<tr class=\"title\">"); + incrIndent(); + writeln(QString("<td colspan=\"%1\" align=\"center\">%2 <font color=\"#777777\">(%3)</font></td>").arg(colSpan).arg(tr("Table: %1").arg(table), tr("virtual"))); + decrIndent(); + writeln("</tr>"); + + if (printHeader) + { + writeln("<tr class=\"header\">"); + incrIndent(); + if (printRownum) + { + writeln("<td align=\"right\">"); + incrIndent(); + writeln("<b><i>#</i></b>"); + decrIndent(); + writeln("</td>"); + } + + QString column; + for (const QString& col : columnNames) + { + writeln("<td>"); + incrIndent(); + writeln(QString("<b>%1</b>").arg(col)); + decrIndent(); + writeln("</td>"); + } + decrIndent(); + writeln("</tr>"); + } + + 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("</table>"); + writeln("<br/><br/>"); + 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("<table>"); + incrIndent(); + + writeln("<tr class=\"title\">"); + incrIndent(); + writeln(QString("<td align=\"center\" colspan=\"3\">%1</td>").arg(tr("Index: %1").arg("<b>" + name + "</b>"))); + decrIndent(); + writeln("</tr>"); + + writeln("<tr>"); + incrIndent(); + writeln(QString("<td align=\"right\" class=\"rownum\">%1</td>").arg(tr("For table:"))); + writeln(QString("<td colspan=\"2\">%1</td>").arg(createIndex->table)); + decrIndent(); + writeln("</tr>"); + + writeln("<tr>"); + incrIndent(); + writeln(QString("<td align=\"right\" class=\"rownum\">%1</td>").arg(tr("Unique:"))); + writeln(QString("<td colspan=\"2\">%1</td>").arg(createIndex->uniqueKw ? tr("Yes") : tr("No"))); + decrIndent(); + writeln("</tr>"); + + writeln("<tr class=\"header\">"); + incrIndent(); + writeln(QString("<td><b>%1</b></td>").arg(tr("Column"))); + writeln(QString("<td><b>%1</b></td>").arg(tr("Collating"))); + writeln(QString("<td><b>%1</b></td>").arg(tr("Sort order"))); + decrIndent(); + writeln("</tr>"); + + for (SqliteIndexedColumn* idxCol : createIndex->indexedColumns) + { + writeln("<tr>"); + incrIndent(); + writeln(QString("<td>%1</td>").arg(escape(idxCol->name))); + writeln(QString("<td align=\"center\">%1</td>").arg(idxCol->collate.isNull() ? " " : escape(idxCol->collate))); + writeln(QString("<td align=\"center\">%1</td>").arg(idxCol->sortOrder == SqliteSortOrder::null ? " " : sqliteSortOrder(idxCol->sortOrder))); + decrIndent(); + writeln("</tr>"); + } + + decrIndent(); + writeln("</table>"); + writeln("<br/><br/>"); + return true; +} + +bool HtmlExport::exportTrigger(const QString& database, const QString& name, const QString& ddl, SqliteCreateTriggerPtr createTrigger) +{ + UNUSED(database); + UNUSED(ddl); + + writeln("<table>"); + incrIndent(); + + writeln("<tr class=\"title\">"); + incrIndent(); + writeln(QString("<td align=\"center\" colspan=\"2\">%1</td>").arg(tr("Trigger: %1").arg("<b>" + name + "</b>"))); + decrIndent(); + writeln("</tr>"); + + writeln("<tr>"); + incrIndent(); + writeln(QString("<td align=\"right\" class=\"rownum\">%1</td>").arg(tr("Activated:"))); + writeln(QString("<td><code>%1</code></td>").arg(SqliteCreateTrigger::time(createTrigger->eventTime))); + decrIndent(); + writeln("</tr>"); + + QString event = createTrigger->event ? SqliteCreateTrigger::Event::typeToString(createTrigger->event->type) : ""; + writeln("<tr>"); + incrIndent(); + writeln(QString("<td align=\"right\" class=\"rownum\">%1</td>").arg(tr("Action:"))); + writeln(QString("<td><code>%1</code></td>").arg(event)); + decrIndent(); + writeln("</tr>"); + + QString onObj; + if (createTrigger->eventTime == SqliteCreateTrigger::Time::INSTEAD_OF) + onObj = tr("On view:"); + else + onObj = tr("On table:"); + + writeln("<tr>"); + incrIndent(); + writeln(QString("<td align=\"right\" class=\"rownum\">%1</td>").arg(onObj)); + writeln(QString("<td><code>%1</code></td>").arg(createTrigger->table)); + decrIndent(); + writeln("</tr>"); + + writeln("<tr>"); + incrIndent(); + writeln(QString("<td align=\"right\" class=\"rownum\">%1</td>").arg(tr("Activate condition:"))); + writeln(QString("<td><code>%1</code></td>").arg(createTrigger->precondition ? escape(createTrigger->precondition->detokenize()) : "")); + decrIndent(); + writeln("</tr>"); + + writeln("<tr>"); + incrIndent(); + writeln(QString("<td colspan=\"2\" class=\"separator\">%1</td>").arg(tr("Code executed:"))); + decrIndent(); + writeln("</tr>"); + + QStringList queryStrings; + for (SqliteQuery* q : createTrigger->queries) + queryStrings << escape(q->detokenize()); + + writeln("<tr>"); + incrIndent(); + writeln("<td colspan=\"2\">"); + incrIndent(); + writeln(QString("<pre>%1</pre>").arg(queryStrings.join("<br/>"))); + decrIndent(); + writeln("</td>"); + decrIndent(); + writeln("</tr>"); + + decrIndent(); + writeln("</table>"); + writeln("<br/><br/>"); + return true; +} + +bool HtmlExport::exportView(const QString& database, const QString& name, const QString& ddl, SqliteCreateViewPtr view) +{ + UNUSED(database); + UNUSED(ddl); + + writeln("<table>"); + incrIndent(); + + writeln("<tr class=\"title\">"); + incrIndent(); + writeln(QString("<td align=\"center\">%1</td>").arg(tr("View: %1").arg("<b>" + name + "</b>"))); + decrIndent(); + writeln("</tr>"); + + writeln("<tr>"); + incrIndent(); + writeln("<td>"); + incrIndent(); + writeln(QString("<pre>%1</pre>").arg(escape(view->select->detokenize()))); + decrIndent(); + writeln("</td>"); + decrIndent(); + writeln("</tr>"); + + decrIndent(); + writeln("</table>"); + writeln("<br/><br/>"); + return true; +} + +bool HtmlExport::afterExport() +{ + static const QString bodyEndTpl = QStringLiteral("</body>"); + static const QString docEnd = QStringLiteral("</html>"); + + writeln("<i>" + tr("Document generated by SQLiteStudio v%1 on %2").arg(SQLITESTUDIO->getVersionString(), QDateTime::currentDateTime().toString()) + "</i>"); + decrIndent(); + writeln(bodyEndTpl); + decrIndent(); + writeln(docEnd); + return true; +} + +bool HtmlExport::beginDoc(const QString& title) +{ + static const QString docStart = QStringLiteral( + R"(<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">)" + "\n<html>" + ); + + static const QString metaCodecTpl = QStringLiteral(R"(<meta http-equiv="Content-Type" content="text/html; charset=%1"/>)"); + static const QString titletpl = QStringLiteral(R"(<title>%1</title>)"); + static const QString styleStartTpl = QStringLiteral(R"(<style type="text/css">)"); + static const QString styleEndTpl = QStringLiteral(R"(</style>)"); + static const QString bodyStartTpl = QStringLiteral(R"(<body>)"); + + 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); +} |
