File indexing completed on 2024-05-26 05:10:26
0001 /*************************************************************************** 0002 * SPDX-FileCopyrightText: 2022 S. MANKOWSKI stephane@mankowski.fr 0003 * SPDX-FileCopyrightText: 2022 G. DE BURE support@mankowski.fr 0004 * SPDX-License-Identifier: GPL-3.0-or-later 0005 ***************************************************************************/ 0006 /** @file 0007 * A plugin for monthly report. 0008 * 0009 * @author Stephane MANKOWSKI 0010 */ 0011 #include "skgmonthlypluginwidget.h" 0012 0013 #include <kaboutdata.h> 0014 #include <kzip.h> 0015 0016 #include <kns3/qtquickdialogwrapper.h> 0017 #include <qdesktopservices.h> 0018 #include <qdir.h> 0019 #include <qdom.h> 0020 #include <qfile.h> 0021 #include <qmenu.h> 0022 #include <qtextstream.h> 0023 #ifdef SKG_WEBENGINE 0024 #include <qwebenginepage.h> 0025 #endif 0026 #ifdef SKG_WEBKIT 0027 #include <qwebframe.h> 0028 #endif 0029 #include <qdiriterator.h> 0030 #include <qstandardpaths.h> 0031 #include <qvalidator.h> 0032 0033 #include "skgmainpanel.h" 0034 #include "skgreport.h" 0035 #include "skgtraces.h" 0036 #include "skgtransactionmng.h" 0037 0038 SKGMonthlyPluginWidget::SKGMonthlyPluginWidget(QWidget* iParent, SKGDocument* iDocument) 0039 : SKGTabPage(iParent, iDocument), m_upload(nullptr) 0040 { 0041 SKGTRACEINFUNC(1) 0042 if (iDocument == nullptr) { 0043 return; 0044 } 0045 0046 ui.setupUi(this); 0047 ui.kMonth->setMode(SKGSimplePeriodEdit::PREVIOUS_PERIODS | SKGSimplePeriodEdit::PREVIOUS_MONTHS); 0048 ui.kDeleteTemplate->hide(); 0049 0050 ui.kRefresh->setIcon(SKGServices::fromTheme(QStringLiteral("view-refresh"))); 0051 ui.kGetNewHotStuff->setIcon(SKGServices::fromTheme(QStringLiteral("get-hot-new-stuff"))); 0052 ui.kDeleteTemplate->setIcon(SKGServices::fromTheme(QStringLiteral("edit-delete"))); 0053 0054 auto newValidator = new QRegularExpressionValidator(QRegularExpression(QStringLiteral("^[\\w\\s]+$")), this); 0055 ui.kTemplate->setValidator(newValidator); 0056 0057 connect(getDocument(), &SKGDocument::tableModified, this, &SKGMonthlyPluginWidget::dataModified, Qt::QueuedConnection); 0058 connect(ui.kMonth, static_cast<void (SKGComboBox::*)(const QString&)>(&SKGComboBox::currentTextChanged), this, &SKGMonthlyPluginWidget::onPeriodChanged, Qt::QueuedConnection); 0059 0060 QStringList overlays; 0061 overlays.push_back(QStringLiteral("list-add")); 0062 m_upload = new QAction(SKGServices::fromTheme(QStringLiteral("get-hot-new-stuff"), overlays), i18n("Upload"), this); 0063 connect(m_upload, &QAction::triggered, this, &SKGMonthlyPluginWidget::onPutNewHotStuff); 0064 0065 auto menu = new QMenu(this); 0066 menu->addAction(m_upload); 0067 ui.kGetNewHotStuff->setMenu(menu); 0068 0069 connect(ui.kDeleteTemplate, &QToolButton::clicked, this, &SKGMonthlyPluginWidget::onDeleteTemplate); 0070 connect(ui.kTemplate, static_cast<void (SKGComboBox::*)(const QString&)>(&SKGComboBox::returnPressed), this, &SKGMonthlyPluginWidget::onAddTemplate); 0071 connect(ui.kTemplate, &SKGComboBox::editTextChanged, this, &SKGMonthlyPluginWidget::onTemplateChanged); 0072 connect(ui.kGetNewHotStuff, &QToolButton::clicked, this, &SKGMonthlyPluginWidget::onGetNewHotStuff); 0073 connect(ui.kRefresh, &QToolButton::clicked, this, &SKGMonthlyPluginWidget::onPeriodChanged); 0074 0075 // Refresh 0076 fillTemplateList(); 0077 dataModified(QLatin1String(""), 0); 0078 } 0079 0080 SKGMonthlyPluginWidget::~SKGMonthlyPluginWidget() 0081 { 0082 SKGTRACEINFUNC(1) 0083 } 0084 0085 QString SKGMonthlyPluginWidget::getState() 0086 { 0087 SKGTRACEINFUNC(10) 0088 QDomDocument doc(QStringLiteral("SKGML")); 0089 QDomElement root = doc.createElement(QStringLiteral("parameters")); 0090 doc.appendChild(root); 0091 0092 // Get state 0093 root.setAttribute(QStringLiteral("month"), ui.kMonth->text()); 0094 root.setAttribute(QStringLiteral("template"), ui.kTemplate->text()); 0095 root.setAttribute(QStringLiteral("web"), ui.kWebView->getState()); 0096 0097 return doc.toString(); 0098 } 0099 0100 void SKGMonthlyPluginWidget::setState(const QString& iState) 0101 { 0102 SKGTRACEINFUNC(10) 0103 QDomDocument doc(QStringLiteral("SKGML")); 0104 doc.setContent(iState); 0105 QDomElement root = doc.documentElement(); 0106 0107 // Set state 0108 QString webS = root.attribute(QStringLiteral("web")); 0109 if (!webS.isEmpty()) { 0110 ui.kWebView->setState(webS); 0111 } 0112 0113 QString templat = root.attribute(QStringLiteral("template")); 0114 if (!templat.isEmpty()) { 0115 bool p = ui.kTemplate->blockSignals(true); 0116 ui.kTemplate->setText(templat); 0117 ui.kTemplate->blockSignals(p); 0118 onTemplateChanged(); 0119 } 0120 0121 QString month = root.attribute(QStringLiteral("month")); 0122 if (!month.isEmpty()) { 0123 ui.kMonth->setText(month); 0124 } 0125 onPeriodChanged(); 0126 } 0127 0128 QString SKGMonthlyPluginWidget::getDefaultStateAttribute() 0129 { 0130 return QStringLiteral("SKGMONTHLY_DEFAULT_PARAMETERS"); 0131 } 0132 0133 QWidget* SKGMonthlyPluginWidget::mainWidget() 0134 { 0135 return ui.kWebView; 0136 } 0137 0138 void SKGMonthlyPluginWidget::fillTemplateList() 0139 { 0140 disconnect(ui.kTemplate, static_cast<void (SKGComboBox::*)(const QString&)>(&SKGComboBox::currentTextChanged), this, &SKGMonthlyPluginWidget::onPeriodChanged); 0141 // Get previous selected item 0142 QString current = ui.kTemplate->text(); 0143 0144 // Fill 0145 ui.kTemplate->clear(); 0146 const auto dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, KAboutData::applicationData().componentName() % "/html", QStandardPaths::LocateDirectory); 0147 for (const auto& dir : dirs) { 0148 QDirIterator it(dir, QStringList() << QStringLiteral("*.txt")); 0149 while (it.hasNext()) { 0150 QString file = it.next(); 0151 0152 QFileInfo f(file); 0153 QString file2 = f.completeBaseName(); 0154 if (!ui.kTemplate->contains(file2) && file2 != QStringLiteral("main")) { 0155 ui.kTemplate->addItem(file2, file); 0156 } 0157 } 0158 } 0159 0160 // Set previous selected itemData 0161 if (!current.isEmpty() && ui.kTemplate->contains(current)) { 0162 ui.kTemplate->setCurrentItem(current); 0163 } 0164 connect(ui.kTemplate, static_cast<void (SKGComboBox::*)(const QString&)>(&SKGComboBox::currentTextChanged), this, &SKGMonthlyPluginWidget::onPeriodChanged, Qt::QueuedConnection); 0165 } 0166 0167 void SKGMonthlyPluginWidget::onAddTemplate() 0168 { 0169 QString templat = ui.kTemplate->text().trimmed(); 0170 QString templateDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + KAboutData::applicationData().componentName(); 0171 QString templatFileName = templateDir % "/html/" % templat % ".txt"; 0172 QStringList templates; 0173 const auto dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, KAboutData::applicationData().componentName() % "/html", QStandardPaths::LocateDirectory); 0174 for (const auto& dir : dirs) { 0175 QDirIterator it(dir, QStringList() << QStringLiteral("*.txt")); 0176 while (it.hasNext()) { 0177 templates.append(it.next()); 0178 } 0179 } 0180 0181 if (!templat.isEmpty() && (!templates.contains(templatFileName) || QFileInfo(templatFileName).isWritable())) { 0182 SKGError err; 0183 if (!templates.contains(templatFileName)) { 0184 // Create the new template 0185 QString source = QStandardPaths::locate(QStandardPaths::GenericDataLocation, KAboutData::applicationData().componentName() % "/html/tutorial.txt"); 0186 QDir(templateDir).mkpath(QStringLiteral("html")); 0187 if (SKGServices::upload(QUrl::fromLocalFile(source), QUrl::fromLocalFile(templatFileName))) { 0188 err.setReturnCode(ERR_FAIL).setMessage(i18nc("An error message", "Impossible to copy file from '%1' to '%2'", source, templatFileName)); 0189 } else { 0190 fillTemplateList(); 0191 } 0192 } 0193 0194 // Open the created or already existing file 0195 QDesktopServices::openUrl(QUrl::fromLocalFile(templatFileName)); 0196 0197 onTemplateChanged(); 0198 0199 // Display error 0200 SKGMainPanel::displayErrorMessage(err); 0201 } 0202 } 0203 0204 void SKGMonthlyPluginWidget::onDeleteTemplate() 0205 { 0206 QString templat = ui.kTemplate->text().trimmed(); 0207 QString templatFileName = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + KAboutData::applicationData().componentName() % "/html/" % templat % ".txt"; 0208 if (!templat.isEmpty()) { 0209 // This is a new source 0210 SKGError err; 0211 0212 // Delete the file 0213 QFile file(templatFileName); 0214 if (!file.remove()) { 0215 err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message", "Deletion of '%1' failed", templatFileName)); 0216 } 0217 0218 IFOK(err) ui.kTemplate->removeItem(ui.kTemplate->findText(templat)); 0219 0220 // Display error 0221 SKGMainPanel::displayErrorMessage(err); 0222 } 0223 } 0224 0225 void SKGMonthlyPluginWidget::onTemplateChanged() 0226 { 0227 QString templat = ui.kTemplate->text().trimmed(); 0228 QString templatFileName = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + KAboutData::applicationData().componentName() % "/html/" % templat % ".txt"; 0229 bool local = !templat.isEmpty() && QFileInfo(templatFileName).isWritable(); 0230 ui.kDeleteTemplate->setVisible(local); 0231 m_upload->setEnabled(local); 0232 } 0233 0234 void SKGMonthlyPluginWidget::dataModified(const QString& iTableName, int iIdTransaction) 0235 { 0236 SKGTRACEINFUNC(1) 0237 Q_UNUSED(iIdTransaction) 0238 0239 QSqlDatabase* db = getDocument()->getMainDatabase(); 0240 setEnabled(db != nullptr); 0241 // TODO(Stephane MANKOWSKI): v_operation_display must be generic 0242 if (db != nullptr && (iTableName == QStringLiteral("v_operation_display") || iTableName.isEmpty())) { 0243 // Fill combo 0244 0245 QDate date = QDate::currentDate(); 0246 QStringList list; 0247 // TODO(Stephane MANKOWSKI): v_operation_display must be generic 0248 getDocument()->getDistinctValues(QStringLiteral("v_operation_display"), QStringLiteral("MIN(d_DATEMONTH)"), QStringLiteral("d_date<=CURRENT_DATE"), list); 0249 if (!list.isEmpty()) { 0250 if (!list[0].isEmpty()) { 0251 date = SKGServices::periodToDate(list[0]); 0252 } 0253 } 0254 0255 ui.kMonth->setFirstDate(date); 0256 ui.kRefresh->setEnabled(!list.isEmpty()); 0257 } 0258 } 0259 0260 QString SKGMonthlyPluginWidget::getPeriod() 0261 { 0262 return ui.kMonth->period(); 0263 } 0264 0265 void SKGMonthlyPluginWidget::onPeriodChanged() 0266 { 0267 SKGTRACEINFUNC(1) 0268 QString month = getPeriod(); 0269 if (!month.isEmpty()) { 0270 // Display report 0271 QString htmlReport = getDocument()->getParameter("SKG_MONTHLY_REPORT_" % month); 0272 if (htmlReport.isEmpty() || sender() == ui.kRefresh || sender() == ui.kTemplate) { 0273 SKGError err; 0274 SKGBEGINLIGHTTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Compute monthly report for '%1'", month), err) 0275 htmlReport = getReport(); 0276 err = getDocument()->setParameter("SKG_MONTHLY_REPORT_" % month, htmlReport); 0277 } 0278 0279 // Display html report 0280 #ifdef SKG_WEBENGINE 0281 ui.kWebView->page()->setHtml(htmlReport, QUrl("file://")); 0282 #endif 0283 #ifdef SKG_WEBKIT 0284 ui.kWebView->setHtml(htmlReport); 0285 ui.kWebView->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); 0286 #endif 0287 #if !defined(SKG_WEBENGINE) && !defined(SKG_WEBKIT) 0288 ui.kWebView->setHtml(htmlReport); 0289 #endif 0290 } 0291 } 0292 0293 void SKGMonthlyPluginWidget::onGetNewHotStuff() 0294 { 0295 QPointer<KNS3::QtQuickDialogWrapper> dialog = new KNS3::QtQuickDialogWrapper(KAboutData::applicationData().componentName() % "_monthly.knsrc", this); 0296 dialog->exec(); 0297 0298 fillTemplateList(); 0299 } 0300 0301 void SKGMonthlyPluginWidget::onPutNewHotStuff() 0302 { 0303 QString templat = ui.kTemplate->text().trimmed(); 0304 0305 // Create zip file 0306 QString templatFileName = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + KAboutData::applicationData().componentName() % "/html/" % templat % ".txt"; 0307 QString templatHFileName = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + KAboutData::applicationData().componentName() % "/html/" % templat % ".html"; 0308 QString zipFileName = QDir::tempPath() % "/" % templat % ".zip"; 0309 KZip zip(zipFileName); 0310 if (zip.open(QIODevice::WriteOnly)) { 0311 zip.addLocalFile(templatFileName, templat % ".txt"); 0312 if (QFile(templatHFileName).exists()) { 0313 zip.addLocalFile(templatHFileName, templat % ".html"); 0314 } 0315 zip.close(); 0316 0317 // Create screen shots 0318 QString preview1 = QDir::tempPath() % "/" % templat % "_preview1.png"; 0319 QString preview2 = QDir::tempPath() % "/" % templat % "_preview2.png"; 0320 QString preview3 = QDir::tempPath() % "/" % templat % "_preview3.png"; 0321 #ifdef SKG_WEBENGINE 0322 // TODO(SMI): QWebEngine 0323 #endif 0324 #ifdef SKG_WEBKIT 0325 bool previous = ui.kWebView->blockSignals(true); 0326 QWebFrame* frame = ui.kWebView->page()->mainFrame(); 0327 frame->setScrollBarValue(Qt::Vertical, frame->scrollBarMaximum(Qt::Vertical)); 0328 ui.kWebView->exportInFile(preview2); 0329 0330 frame->setScrollBarValue(Qt::Vertical, frame->scrollBarMaximum(Qt::Vertical) / 2); 0331 ui.kWebView->exportInFile(preview3); 0332 0333 frame->setScrollBarValue(Qt::Vertical, 0); 0334 ui.kWebView->exportInFile(preview1); 0335 ui.kWebView->blockSignals(previous); 0336 #endif 0337 #if !defined(SKG_WEBENGINE) && !defined(SKG_WEBKIT) 0338 // TODO(SMI): QTextBrowser 0339 #endif 0340 // Open dialog 0341 SKGMainPanel::getMainPanel()->displayMessage(i18nc("Upload message", 0342 "The package is ready. You can find it here %1 and 3 screen capture there %2, %3 , %4. You can now upload it manually.", 0343 zipFileName, preview1, preview2, preview3 0344 ) 0345 ); 0346 } 0347 } 0348 0349 QString SKGMonthlyPluginWidget::getReport() 0350 { 0351 QString html; 0352 SKGError err; 0353 SKGTRACEINFUNCRC(10, err) 0354 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); 0355 0356 // Get period 0357 if (!getPeriod().isEmpty()) { 0358 SKGReport* rep = getDocument()->getReport(); 0359 if (rep != nullptr) { 0360 rep->setPeriod(getPeriod()); 0361 0362 // Enrich with tips of the day 0363 rep->setTipsOfDay(SKGMainPanel::getMainPanel()->getTipsOfDay()); 0364 0365 err = SKGReport::getReportFromTemplate(rep, ui.kTemplate->itemData(ui.kTemplate->currentIndex()).toString(), html); 0366 0367 delete rep; 0368 } 0369 } 0370 QApplication::restoreOverrideCursor(); 0371 0372 // status bar 0373 IFKO(err) html += err.getFullMessageWithHistorical(); 0374 return html; 0375 } 0376 0377