From 9209382c1868045baf49da42c5700da43556a49f Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Thu, 23 Nov 2017 01:59:26 +0200 Subject: printing: add set_bundled_templates_as_read_only() Add the function set_bundled_templates_as_read_only() in templatelayout.cpp/h. The function is used to mark the bundled template files as read-only in the user folder. It is called in mainwindow.cpp, after the files are copied from the bundle. Signed-off-by: Lubomir I. Ivanov --- desktop-widgets/mainwindow.cpp | 1 + desktop-widgets/templatelayout.cpp | 24 ++++++++++++++++++++++++ desktop-widgets/templatelayout.h | 1 + 3 files changed, 26 insertions(+) (limited to 'desktop-widgets') diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp index f685a2b74..05314af1d 100644 --- a/desktop-widgets/mainwindow.cpp +++ b/desktop-widgets/mainwindow.cpp @@ -258,6 +258,7 @@ MainWindow::MainWindow() : QMainWindow(), #ifndef NO_PRINTING // copy the bundled print templates to the user path; no overwriting occurs! copyPath(getPrintingTemplatePathBundle(), getPrintingTemplatePathUser()); + set_bundled_templates_as_read_only(); find_all_templates(); #endif diff --git a/desktop-widgets/templatelayout.cpp b/desktop-widgets/templatelayout.cpp index 9f933e91c..b53f55741 100644 --- a/desktop-widgets/templatelayout.cpp +++ b/desktop-widgets/templatelayout.cpp @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 +#include #include #include "templatelayout.h" @@ -39,6 +40,29 @@ void find_all_templates() } } +/* find templates which are part of the bundle in the user path + * and set them as read only. + */ +void set_bundled_templates_as_read_only() +{ + QDir dir; + const QString stats("statistics"); + QStringList list, listStats; + QString pathBundle = getPrintingTemplatePathBundle(); + QString pathUser = getPrintingTemplatePathUser(); + + dir.setPath(pathBundle); + list = dir.entryList(QDir::Files | QDir::NoDotAndDotDot); + dir.setPath(pathBundle + QDir::separator() + stats); + listStats = dir.entryList(QDir::Files | QDir::NoDotAndDotDot); + for (int i = 0; i < listStats.length(); i++) + listStats[i] = stats + QDir::separator() + listStats.at(i); + list += listStats; + + foreach (const QString& f, list) + QFile::setPermissions(pathUser + QDir::separator() + f, QFileDevice::ReadOwner | QFileDevice::ReadUser); +} + TemplateLayout::TemplateLayout(print_options *PrintOptions, template_options *templateOptions) : m_engine(NULL) { diff --git a/desktop-widgets/templatelayout.h b/desktop-widgets/templatelayout.h index 9c24d096d..8ec4eadc7 100644 --- a/desktop-widgets/templatelayout.h +++ b/desktop-widgets/templatelayout.h @@ -12,6 +12,7 @@ int getTotalWork(print_options *printOptions); void find_all_templates(); +void set_bundled_templates_as_read_only(); extern QList grantlee_templates, grantlee_statistics_templates; -- cgit v1.2.3-70-g09d2 From c6c9b3bd8ba41290d4484b168401976571166d70 Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Thu, 23 Nov 2017 02:24:29 +0200 Subject: printing: minor improvements to import / export 1) Always open the user path on Import / Export 2) Update the list after Export, as the user might have exported to the user path Signed-off-by: Lubomir I. Ivanov --- desktop-widgets/printoptions.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'desktop-widgets') diff --git a/desktop-widgets/printoptions.cpp b/desktop-widgets/printoptions.cpp index eaeb6954b..34aac318f 100644 --- a/desktop-widgets/printoptions.cpp +++ b/desktop-widgets/printoptions.cpp @@ -128,12 +128,13 @@ void PrintOptions::on_editButton_clicked() void PrintOptions::on_importButton_clicked() { - QString filename = QFileDialog::getOpenFileName(this, tr("Import template file"), "", + QString pathUser = getPrintingTemplatePathUser(); + QString filename = QFileDialog::getOpenFileName(this, tr("Import template file"), pathUser, tr("HTML files") + " (*.html)"); if (filename.isEmpty()) return; QFileInfo fileInfo(filename); - QFile::copy(filename, getPrintingTemplatePathUser() + QDir::separator() + fileInfo.fileName()); + QFile::copy(filename, pathUser + QDir::separator() + fileInfo.fileName()); printOptions->p_template = fileInfo.fileName(); find_all_templates(); setup(); @@ -141,11 +142,14 @@ void PrintOptions::on_importButton_clicked() void PrintOptions::on_exportButton_clicked() { - QString filename = QFileDialog::getSaveFileName(this, tr("Export template files as"), "", + QString pathUser = getPrintingTemplatePathUser(); + QString filename = QFileDialog::getSaveFileName(this, tr("Export template files as"), pathUser, tr("HTML files") + " (*.html)"); if (filename.isEmpty()) return; - QFile::copy(getPrintingTemplatePathUser() + QDir::separator() + getSelectedTemplate(), filename); + QFile::copy(pathUser + QDir::separator() + getSelectedTemplate(), filename); + find_all_templates(); + setup(); } void PrintOptions::on_deleteButton_clicked() -- cgit v1.2.3-70-g09d2 From bc6146577d0c3524a02733d79811a6bc9310d839 Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Thu, 23 Nov 2017 02:54:27 +0200 Subject: printing: improve messaging in printoptions.cpp 1) on_deleteButton_clicked() show a proper message box with icon title and also quote the file name. 2) When exporting a file, make sure that the destination is not read-only even if the source is. 3) Do not allow editing of read-only templates (e.g. the bundled ones). Instruct the user to Export first. Signed-off-by: Lubomir I. Ivanov --- desktop-widgets/printoptions.cpp | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) (limited to 'desktop-widgets') diff --git a/desktop-widgets/printoptions.cpp b/desktop-widgets/printoptions.cpp index 34aac318f..7ed8ecb46 100644 --- a/desktop-widgets/printoptions.cpp +++ b/desktop-widgets/printoptions.cpp @@ -121,6 +121,19 @@ void PrintOptions::on_printTemplate_currentIndexChanged(int index) void PrintOptions::on_editButton_clicked() { + QString templateName = getSelectedTemplate(); + QFile f(getPrintingTemplatePathUser() + QDir::separator() + templateName); + if (!f.open(QFile::ReadWrite | QFile::Text)) { + QMessageBox msgBox(this); + msgBox.setWindowTitle(tr("Read-only template!")); + msgBox.setText(tr("The template '%1' is read-only and connot be edited.\n" + "Please export this template to a different file.").arg(templateName)); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.exec(); + return; + } else { + f.close(); + } TemplateEdit te(this, printOptions, templateOptions); te.exec(); setup(); @@ -148,6 +161,11 @@ void PrintOptions::on_exportButton_clicked() if (filename.isEmpty()) return; QFile::copy(pathUser + QDir::separator() + getSelectedTemplate(), filename); + QFile f(filename); + if (!f.open(QFile::ReadWrite | QFile::Text)) + f.setPermissions(QFileDevice::ReadUser | QFileDevice::ReadOwner | QFileDevice::WriteUser | QFileDevice::WriteOwner); + else + f.close(); find_all_templates(); setup(); } @@ -155,9 +173,9 @@ void PrintOptions::on_exportButton_clicked() void PrintOptions::on_deleteButton_clicked() { QString templateName = getSelectedTemplate(); - QMessageBox msgBox; - msgBox.setText(tr("This action cannot be undone!")); - msgBox.setInformativeText(tr("Delete template: %1?").arg(templateName)); + QMessageBox msgBox(this); + msgBox.setWindowTitle(tr("This action cannot be undone!")); + msgBox.setText(tr("Delete template '%1'?").arg(templateName)); msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); msgBox.setDefaultButton(QMessageBox::Cancel); if (msgBox.exec() == QMessageBox::Ok) { -- cgit v1.2.3-70-g09d2 From a2ec791f2fa9db99adbea0cd8820ed9c142f2349 Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Thu, 23 Nov 2017 16:40:05 +0200 Subject: printing: store the last import / export template Store the last template file name which the user imported / exported and then try to pre-select it in the combo box. Signed-off-by: Lubomir I. Ivanov --- desktop-widgets/printoptions.cpp | 7 ++++++- desktop-widgets/printoptions.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'desktop-widgets') diff --git a/desktop-widgets/printoptions.cpp b/desktop-widgets/printoptions.cpp index 7ed8ecb46..10d6d9c25 100644 --- a/desktop-widgets/printoptions.cpp +++ b/desktop-widgets/printoptions.cpp @@ -60,11 +60,13 @@ void PrintOptions::setupTemplates() int current_index = 0; ui.printTemplate->clear(); Q_FOREACH(const QString& theme, currList) { - if (theme == storedTemplate) // find the stored template in the list + // find the stored template in the list + if (theme == storedTemplate || theme == lastImportExportTemplate) current_index = currList.indexOf(theme); ui.printTemplate->addItem(theme.split('.')[0], theme); } ui.printTemplate->setCurrentIndex(current_index); + lastImportExportTemplate = ""; } // print type radio buttons @@ -149,6 +151,7 @@ void PrintOptions::on_importButton_clicked() QFileInfo fileInfo(filename); QFile::copy(filename, pathUser + QDir::separator() + fileInfo.fileName()); printOptions->p_template = fileInfo.fileName(); + lastImportExportTemplate = fileInfo.fileName(); find_all_templates(); setup(); } @@ -166,6 +169,8 @@ void PrintOptions::on_exportButton_clicked() f.setPermissions(QFileDevice::ReadUser | QFileDevice::ReadOwner | QFileDevice::WriteUser | QFileDevice::WriteOwner); else f.close(); + QFileInfo fileInfo(filename); + lastImportExportTemplate = fileInfo.fileName(); find_all_templates(); setup(); } diff --git a/desktop-widgets/printoptions.h b/desktop-widgets/printoptions.h index 9aaf8de70..1db0dfe61 100644 --- a/desktop-widgets/printoptions.h +++ b/desktop-widgets/printoptions.h @@ -72,6 +72,7 @@ private: struct print_options *printOptions; struct template_options *templateOptions; bool hasSetupSlots; + QString lastImportExportTemplate; void setupTemplates(); private -- cgit v1.2.3-70-g09d2 From c57df085a4dc9e4b679dfa5f2d834c3ecf5f8f63 Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Thu, 23 Nov 2017 16:42:54 +0200 Subject: printing: make sure that exported templates are .html Thus far the exported template did not had the .html extension. This patch makes sure that the extension is always added to the file if missing. Also handle the case where the user used ".htm" and replace that with ".html". Signed-off-by: Lubomir I. Ivanov --- desktop-widgets/printoptions.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'desktop-widgets') diff --git a/desktop-widgets/printoptions.cpp b/desktop-widgets/printoptions.cpp index 10d6d9c25..4b1deabca 100644 --- a/desktop-widgets/printoptions.cpp +++ b/desktop-widgets/printoptions.cpp @@ -163,6 +163,11 @@ void PrintOptions::on_exportButton_clicked() tr("HTML files") + " (*.html)"); if (filename.isEmpty()) return; + const QString ext(".html"); + if (filename.endsWith(".htm", Qt::CaseInsensitive)) + filename += "l"; + else if (!filename.endsWith(ext, Qt::CaseInsensitive)) + filename += ext; QFile::copy(pathUser + QDir::separator() + getSelectedTemplate(), filename); QFile f(filename); if (!f.open(QFile::ReadWrite | QFile::Text)) -- cgit v1.2.3-70-g09d2 From ba7f2a399b6c0df6805d25b37bcab266d2020acc Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Thu, 23 Nov 2017 18:03:46 +0200 Subject: printing: handle overwriting in import / export Show an error message if trying to: 1) Import over an existing read-only template with the same name 2) Export to a read-only file with the same name 3) Delete a read-only template Signed-off-by: Lubomir I. Ivanov --- desktop-widgets/printoptions.cpp | 46 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) (limited to 'desktop-widgets') diff --git a/desktop-widgets/printoptions.cpp b/desktop-widgets/printoptions.cpp index 4b1deabca..6e45bc5e1 100644 --- a/desktop-widgets/printoptions.cpp +++ b/desktop-widgets/printoptions.cpp @@ -149,7 +149,23 @@ void PrintOptions::on_importButton_clicked() if (filename.isEmpty()) return; QFileInfo fileInfo(filename); - QFile::copy(filename, pathUser + QDir::separator() + fileInfo.fileName()); + + const QString dest = pathUser + QDir::separator() + fileInfo.fileName(); + QFile f(dest); + if (!f.open(QFile::ReadWrite | QFile::Text)) { + QMessageBox msgBox(this); + msgBox.setWindowTitle(tr("Read-only template!")); + msgBox.setText(tr("The destination template '%1' is read-only and cannot be overwritten.").arg(fileInfo.fileName())); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.exec(); + return; + } else { + f.close(); + if (filename != dest) + f.remove(); + } + + QFile::copy(filename, dest); printOptions->p_template = fileInfo.fileName(); lastImportExportTemplate = fileInfo.fileName(); find_all_templates(); @@ -168,13 +184,28 @@ void PrintOptions::on_exportButton_clicked() filename += "l"; else if (!filename.endsWith(ext, Qt::CaseInsensitive)) filename += ext; - QFile::copy(pathUser + QDir::separator() + getSelectedTemplate(), filename); + QFileInfo fileInfo(filename); + const QString dest = pathUser + QDir::separator() + getSelectedTemplate(); + QFile f(filename); + if (!f.open(QFile::ReadWrite | QFile::Text)) { + QMessageBox msgBox(this); + msgBox.setWindowTitle(tr("Read-only template!")); + msgBox.setText(tr("The destination template '%1' is read-only and cannot be overwritten.").arg(fileInfo.fileName())); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.exec(); + return; + } else { + f.close(); + if (dest != filename) + f.remove(); + } + + QFile::copy(dest, filename); if (!f.open(QFile::ReadWrite | QFile::Text)) f.setPermissions(QFileDevice::ReadUser | QFileDevice::ReadOwner | QFileDevice::WriteUser | QFileDevice::WriteOwner); else f.close(); - QFileInfo fileInfo(filename); lastImportExportTemplate = fileInfo.fileName(); find_all_templates(); setup(); @@ -190,6 +221,15 @@ void PrintOptions::on_deleteButton_clicked() msgBox.setDefaultButton(QMessageBox::Cancel); if (msgBox.exec() == QMessageBox::Ok) { QFile f(getPrintingTemplatePathUser() + QDir::separator() + templateName); + if (!f.open(QFile::ReadWrite | QFile::Text)) { + msgBox.setWindowTitle(tr("Read-only template!")); + msgBox.setText(tr("The template '%1' is read-only and cannot be deleted.").arg(templateName)); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.exec(); + return; + } else { + f.close(); + } f.remove(); find_all_templates(); setup(); -- cgit v1.2.3-70-g09d2 From 712697e0c24e53d32ef6087c7f2b4e24fdf8a57c Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Thu, 23 Nov 2017 18:23:36 +0200 Subject: printing: detect a 'statistics' template when editing Prefix the path for 'statistics' templates when detecting if a template is read-only. Import / Export for statistic templates is not supported. So the user has to manually copy and chown a '/statistics' templates. Signed-off-by: Lubomir I. Ivanov --- desktop-widgets/printoptions.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'desktop-widgets') diff --git a/desktop-widgets/printoptions.cpp b/desktop-widgets/printoptions.cpp index 6e45bc5e1..cebb073a3 100644 --- a/desktop-widgets/printoptions.cpp +++ b/desktop-widgets/printoptions.cpp @@ -124,7 +124,8 @@ void PrintOptions::on_printTemplate_currentIndexChanged(int index) void PrintOptions::on_editButton_clicked() { QString templateName = getSelectedTemplate(); - QFile f(getPrintingTemplatePathUser() + QDir::separator() + templateName); + QString prefix = (printOptions->type == print_options::STATISTICS) ? "statistics/" : ""; + QFile f(getPrintingTemplatePathUser() + QDir::separator() + prefix + templateName); if (!f.open(QFile::ReadWrite | QFile::Text)) { QMessageBox msgBox(this); msgBox.setWindowTitle(tr("Read-only template!")); -- cgit v1.2.3-70-g09d2 From fc48cde77c98385c69bc4d951de6867cf2f652a0 Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Fri, 24 Nov 2017 20:55:16 +0200 Subject: printing: only load *.html files in the UI The function find_all_templates() thus far handled all files in the user template directory. This patch makes it so that only files with the .html extension are loaded. Also remove brackets for single lined `if` statement. Signed-off-by: Lubomir I. Ivanov --- desktop-widgets/templatelayout.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'desktop-widgets') diff --git a/desktop-widgets/templatelayout.cpp b/desktop-widgets/templatelayout.cpp index b53f55741..ab9b3f09a 100644 --- a/desktop-widgets/templatelayout.cpp +++ b/desktop-widgets/templatelayout.cpp @@ -20,23 +20,22 @@ int getTotalWork(print_options *printOptions) void find_all_templates() { + const QString ext(".html"); grantlee_templates.clear(); grantlee_statistics_templates.clear(); QDir dir(getPrintingTemplatePathUser()); QStringList list = dir.entryList(QDir::Files | QDir::NoDotAndDotDot); foreach (const QString& filename, list) { - if (filename.at(filename.size() - 1) != '~') { + if (filename.at(filename.size() - 1) != '~' && filename.endsWith(ext)) grantlee_templates.append(filename); - } } // find statistics templates dir.setPath(getPrintingTemplatePathUser() + QDir::separator() + "statistics"); list = dir.entryList(QDir::Files | QDir::NoDotAndDotDot); foreach (const QString& filename, list) { - if (filename.at(filename.size() - 1) != '~') { + if (filename.at(filename.size() - 1) != '~' && filename.endsWith(ext)) grantlee_statistics_templates.append(filename); - } } } -- cgit v1.2.3-70-g09d2 From 65f0600679a72d13b64b20c575c05b5313a80635 Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Fri, 24 Nov 2017 22:54:54 +0200 Subject: printing: update the coping of bundled templates This update includes: - Instead of copyPath() use a new specialized function: copy_bundled_templates() - The new function supports overwriting of templates in the user path, but only if a template file is read-only - If the file is RW create a backup of the file in the form of: -User.html - Collect backup files and store them in a QStringList which is then shown in a QMessageBox from MainWindow to notifying the user about the backup This change allows moving the maintenance of the bundled templates back to the application developers and contributors as currently the only one who can edit the templates in the user path was the user. Suggested-by: Dirk Hohndel Signed-off-by: Lubomir I. Ivanov --- desktop-widgets/mainwindow.cpp | 19 +++++++++++++++++-- desktop-widgets/templatelayout.cpp | 29 +++++++++++++++++++++++++++++ desktop-widgets/templatelayout.h | 2 ++ 3 files changed, 48 insertions(+), 2 deletions(-) (limited to 'desktop-widgets') diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp index 05314af1d..e5739bd47 100644 --- a/desktop-widgets/mainwindow.cpp +++ b/desktop-widgets/mainwindow.cpp @@ -256,8 +256,23 @@ MainWindow::MainWindow() : QMainWindow(), connect(geoLookup, SIGNAL(started()),information(), SLOT(disableGeoLookupEdition())); connect(geoLookup, SIGNAL(finished()), information(), SLOT(enableGeoLookupEdition())); #ifndef NO_PRINTING - // copy the bundled print templates to the user path; no overwriting occurs! - copyPath(getPrintingTemplatePathBundle(), getPrintingTemplatePathUser()); + // copy the bundled print templates to the user path + QStringList templateBackupList; + QString templatePathUser(getPrintingTemplatePathUser()); + copy_bundled_templates(getPrintingTemplatePathBundle(), templatePathUser, &templateBackupList); + if (templateBackupList.length()) { + QMessageBox msgBox(this); + templatePathUser.replace("\\", "/"); + templateBackupList.replaceInStrings(templatePathUser + "/", ""); + msgBox.setWindowTitle(tr("Template backup created")); + msgBox.setText(tr("The following backup printing templates were created:\n\n%1\n\n" + "Location:\n%2\n\n" + "Please note that as of this version of Subsurface the default templates\n" + "are read-only and should not be edited directly, since the application\n" + "can overwrite them on startup.").arg(templateBackupList.join("\n")).arg(templatePathUser)); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.exec(); + } set_bundled_templates_as_read_only(); find_all_templates(); #endif diff --git a/desktop-widgets/templatelayout.cpp b/desktop-widgets/templatelayout.cpp index ab9b3f09a..f3f3c8be2 100644 --- a/desktop-widgets/templatelayout.cpp +++ b/desktop-widgets/templatelayout.cpp @@ -62,6 +62,35 @@ void set_bundled_templates_as_read_only() QFile::setPermissions(pathUser + QDir::separator() + f, QFileDevice::ReadOwner | QFileDevice::ReadUser); } +void copy_bundled_templates(QString src, QString dst, QStringList *templateBackupList) +{ + QDir dir(src); + if (!dir.exists()) + return; + foreach (QString d, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { + QString dst_path = dst + QDir::separator() + d; + dir.mkpath(dst_path); + copy_bundled_templates(src + QDir::separator() + d, dst_path, templateBackupList); + } + foreach (QString f, dir.entryList(QDir::Files)) { + QFile fileSrc(src + QDir::separator() + f); + QFile fileDest(dst + QDir::separator() + f); + if (fileDest.exists()) { + // if open() fails the file is either locked or r/o. try to remove it and then overwrite + if (!fileDest.open(QFile::ReadWrite | QFile::Text)) { + fileDest.setPermissions(QFileDevice::WriteOwner | QFileDevice::WriteUser); + fileDest.remove(); + } else { // if the file is not read-only create a backup + fileDest.close(); + const QString targetFile = fileDest.fileName().replace(".html", "-User.html"); + fileDest.copy(targetFile); + *templateBackupList << targetFile; + } + } + fileSrc.copy(fileDest.fileName()); // in all cases copy the file + } +} + TemplateLayout::TemplateLayout(print_options *PrintOptions, template_options *templateOptions) : m_engine(NULL) { diff --git a/desktop-widgets/templatelayout.h b/desktop-widgets/templatelayout.h index 8ec4eadc7..cb60cc03d 100644 --- a/desktop-widgets/templatelayout.h +++ b/desktop-widgets/templatelayout.h @@ -2,6 +2,7 @@ #ifndef TEMPLATELAYOUT_H #define TEMPLATELAYOUT_H +#include #include #include "mainwindow.h" #include "printoptions.h" @@ -13,6 +14,7 @@ int getTotalWork(print_options *printOptions); void find_all_templates(); void set_bundled_templates_as_read_only(); +void copy_bundled_templates(QString src, QString dst, QStringList *templateBackupList); extern QList grantlee_templates, grantlee_statistics_templates; -- cgit v1.2.3-70-g09d2