File indexing completed on 2024-06-23 05:03:09

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 to manage properties on objects
0008  *
0009  * @author Stephane MANKOWSKI
0010  */
0011 #include "skgpropertiesplugin.h"
0012 
0013 #include <qdir.h>
0014 #include <qdockwidget.h>
0015 
0016 #include <kaboutdata.h>
0017 #include <kactioncollection.h>
0018 #include <kpluginfactory.h>
0019 #include <kstandardaction.h>
0020 #include <ktoolbarpopupaction.h>
0021 
0022 #include "skgmainpanel.h"
0023 #include "skgpropertiesplugindockwidget.h"
0024 #include "skgtraces.h"
0025 #include "skgtransactionmng.h"
0026 
0027 /**
0028  * This plugin factory.
0029  */
0030 K_PLUGIN_CLASS_WITH_JSON(SKGPropertiesPlugin, "metadata.json")
0031 
0032 SKGPropertiesPlugin::SKGPropertiesPlugin(QWidget* iWidget, QObject* iParent, const QVariantList& /*iArg*/) :
0033     SKGInterfacePlugin(iParent),
0034     m_currentDocument(nullptr), m_dockWidget(nullptr), m_dockContent(nullptr), m_addPropertyMenu(nullptr)
0035 {
0036     Q_UNUSED(iWidget)
0037     SKGTRACEINFUNC(10)
0038 
0039     // Get list of bills
0040     m_billsProcess.setStandardOutputFile(QDir::tempPath() % "/skg_bills.csv");
0041     m_billsProcess.start(QStringLiteral("/bin/bash"), QStringList() << QStringLiteral("-c") << QStringLiteral("boobill bills  -q -f csv -v"));
0042 
0043     connect(&m_billsProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &SKGPropertiesPlugin::onBillsRetreived);
0044     connect(&m_billsProcess, static_cast<void (QProcess::*)(QProcess::ProcessError)>(&QProcess::errorOccurred), this, &SKGPropertiesPlugin::onBillsRetreived);
0045 }
0046 
0047 SKGPropertiesPlugin::~SKGPropertiesPlugin()
0048 {
0049     SKGTRACEINFUNC(10)
0050     m_currentDocument = nullptr;
0051     m_dockWidget = nullptr;
0052     m_dockContent = nullptr;
0053     m_addPropertyMenu = nullptr;
0054 
0055     if (m_billsProcess.state() == QProcess::Running) {
0056         m_billsProcess.kill();
0057     }
0058     if (m_billsProcess.state() == QProcess::Running) {
0059         m_billsProcess.kill();
0060         m_billsProcess.waitForFinished(-1);
0061     }
0062 }
0063 
0064 void SKGPropertiesPlugin::onBillsRetreived()
0065 {
0066     QFile file(QDir::tempPath() % "/skg_bills.csv");
0067     if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
0068         QTextStream stream(&file);
0069         stream.readLine();  // To avoid header id;date;format;label;idparent;price;currency;deadline;startdate;finishdate
0070         while (!stream.atEnd()) {
0071             // Read line
0072             QString line = stream.readLine().trimmed();
0073 
0074             m_bills.push_back(line);
0075         }
0076 
0077         // close file
0078         file.close();
0079     }
0080     file.remove();
0081 }
0082 
0083 bool SKGPropertiesPlugin::setupActions(SKGDocument* iDocument)
0084 {
0085     SKGTRACEINFUNC(10)
0086 
0087     m_currentDocument = iDocument;
0088 
0089     setComponentName(QStringLiteral("skg_properties"), title());
0090     setXMLFile(QStringLiteral("skg_properties.rc"));
0091 
0092     m_dockContent = new SKGPropertiesPluginDockWidget(SKGMainPanel::getMainPanel(), m_currentDocument);
0093     if (m_dockContent != nullptr) {
0094         connect(m_dockContent, &SKGPropertiesPluginDockWidget::selectionChanged, SKGMainPanel::getMainPanel(), &SKGMainPanel::refresh);
0095         m_dockWidget = new QDockWidget(SKGMainPanel::getMainPanel());
0096         if (m_dockWidget != nullptr) {
0097             m_dockWidget->setObjectName(QStringLiteral("skg_properties_docwidget"));
0098             m_dockWidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
0099             m_dockWidget->setWindowTitle(title());
0100             m_dockWidget->setWidget(m_dockContent);
0101 
0102             // add action to control hide / display of Bookmarks
0103             QAction* toggle = m_dockWidget->toggleViewAction();
0104             QAction* panelAction = actionCollection()->addAction(QStringLiteral("view_properties"));
0105             registerGlobalAction(QStringLiteral("view_properties"), panelAction);
0106             panelAction->setCheckable(true);
0107             panelAction->setChecked(toggle->isChecked());
0108             panelAction->setText(toggle->text());
0109             actionCollection()->setDefaultShortcut(panelAction, Qt::SHIFT + Qt::Key_F12);
0110             connect(panelAction, &QAction::triggered, toggle, &QAction::trigger);
0111             connect(toggle, &QAction::toggled, panelAction, &QAction::setChecked);
0112         }
0113     }
0114 
0115     // Menu
0116     auto actAddProperty = new KToolBarPopupAction(SKGServices::fromTheme(icon()), i18nc("Allows user to add a user defined property on an object", "Add property"), this);
0117     m_addPropertyMenu = actAddProperty->menu();
0118     connect(m_addPropertyMenu, &QMenu::aboutToShow, this, &SKGPropertiesPlugin::onShowAddPropertyMenu);
0119     actAddProperty->setStickyMenu(false);
0120     actAddProperty->setDelayed(false);
0121     registerGlobalAction(QStringLiteral("add_property"), actAddProperty, QStringList() << QStringLiteral("query:type='table' AND name NOT LIKE 'doctransaction%'"), 1, -1, 450);
0122     return true;
0123 }
0124 
0125 void SKGPropertiesPlugin::onAddProperty()
0126 {
0127     SKGTRACEINFUNC(10)
0128     SKGError err;
0129     // Scope for the transaction
0130     auto* act = qobject_cast<QAction*>(sender());
0131     if ((act != nullptr) && (m_currentDocument != nullptr)) {
0132         // Get parameters
0133         QStringList list = act->data().toStringList();
0134         const QString& name = list.at(0);
0135         const QString& value = list.at(1);
0136 
0137         // Create properties
0138         IFOK(err) {
0139             SKGObjectBase::SKGListSKGObjectBase selection = SKGMainPanel::getMainPanel()->getSelectedObjects();
0140             int nb = selection.count();
0141             SKGBEGINPROGRESSTRANSACTION(*m_currentDocument, i18nc("Create a user defined property", "Property creation"), err, nb)
0142             for (int i = 0; !err && i < nb; ++i) {
0143                 err = selection.at(i).setProperty(name, value);
0144                 IFOKDO(err, m_currentDocument->stepForward(i + 1))
0145             }
0146         }
0147     }
0148 
0149     // status bar
0150     IFOK(err) {
0151         err = SKGError(0, i18nc("The user defined property was successfully created", "Property created"));
0152     }
0153     SKGMainPanel::displayErrorMessage(err);
0154 }
0155 
0156 void SKGPropertiesPlugin::onDownloadAndAddBills()
0157 {
0158     SKGTRACEINFUNC(10)
0159     SKGError err;
0160     // Scope for the transaction
0161     auto* act = qobject_cast<QAction*>(sender());
0162     if ((act != nullptr) && (m_currentDocument != nullptr)) {
0163         // Get parameters
0164         QStringList list = act->data().toStringList();
0165         const QString& id = list.at(0);
0166         QString fileName = QDir::tempPath() % '/' % list.at(3) % '.' % list.at(2);
0167 
0168         // Create properties
0169         IFOK(err) {
0170             SKGObjectBase::SKGListSKGObjectBase selection = SKGMainPanel::getMainPanel()->getSelectedObjects();
0171             int nb = selection.count();
0172             SKGBEGINPROGRESSTRANSACTION(*m_currentDocument, i18nc("Create a user defined property", "Property creation"), err, 2 * nb)
0173             for (int i = 0; !err && i < nb; ++i) {
0174                 // Download the files
0175                 QFile::remove(fileName);
0176                 QString cmd = "boobill download " % id % " \"" % fileName % '"';
0177                 QProcess p;
0178                 p.start(QStringLiteral("/bin/bash"), QStringList() << QStringLiteral("-c") << cmd);
0179                 if (!p.waitForFinished(60000) || p.exitCode() != 0) {
0180                     err.setReturnCode(ERR_FAIL).setMessage(i18nc("Error message",  "The following command line failed with code %2:\n'%1'", cmd, p.exitCode()));
0181                 } else {
0182                     IFOKDO(err, m_currentDocument->stepForward(2 * i))
0183 
0184                     IFOKDO(err, selection.at(i).setProperty(i18nc("Noun", "Bill"), id, fileName))
0185                     QStringList importedBills = SKGServices::splitCSVLine(m_currentDocument->getParameter(QStringLiteral("SKG_IMPORTED_BILLS")));
0186                     importedBills.push_back(id);
0187                     IFOKDO(err, m_currentDocument->setParameter(QStringLiteral("SKG_IMPORTED_BILLS"), SKGServices::stringsToCsv(importedBills)))
0188                     IFOKDO(err, m_currentDocument->stepForward(2 * i + 1))
0189 
0190                     QFile::remove(fileName);
0191                 }
0192             }
0193         }
0194     }
0195 
0196     // status bar
0197     IFOK(err) {
0198         err = SKGError(0, i18nc("The user defined property was successfully created", "Property created"));
0199     }
0200     SKGMainPanel::displayErrorMessage(err);
0201 }
0202 
0203 
0204 void SKGPropertiesPlugin::onShowAddPropertyMenu()
0205 {
0206     if ((m_addPropertyMenu != nullptr) && (m_currentDocument != nullptr)) {
0207         m_addPropertyMenu->clear();
0208 
0209         // Get selection
0210         SKGObjectBase::SKGListSKGObjectBase sels = SKGMainPanel::getMainPanel()->getSelectedObjects();
0211         if (!sels.isEmpty()) {
0212             // Get the table of the selection
0213             QString table = sels.at(0).getRealTable();
0214 
0215             // Get list of more used properties for this table
0216             SKGStringListList listTmp;
0217             m_currentDocument->executeSelectSqliteOrder(
0218                 "SELECT t_name, t_value FROM (SELECT t_name, t_value, COUNT(1) AS nb FROM parameters WHERE (t_uuid_parent like '%-" % table % "' OR t_uuid_parent like '%-sub" % table % "') AND t_name NOT LIKE 'SKG_%' AND b_blob IS NULL GROUP BY t_name, t_value) ORDER BY nb DESC LIMIT 7",
0219                 listTmp);
0220 
0221             // Create actions
0222             int nb = listTmp.count();
0223             QIcon iconp = SKGServices::fromTheme(icon());
0224             if (nb > 1) {
0225                 for (int i = 1; i < nb; ++i) {
0226                     // Should the string below be translated ??? It contains no word…
0227                     QAction* act = m_addPropertyMenu->addAction(iconp, i18nc("Add a property (attribute=value)", "Add %1=%2", listTmp.at(i).at(0), listTmp.at(i).at(1)));
0228                     if (act != nullptr) {
0229                         act->setData(listTmp.at(i));
0230                         connect(act, &QAction::triggered, this, &SKGPropertiesPlugin::onAddProperty);
0231                     }
0232                 }
0233             } else {
0234                 QAction* act = m_addPropertyMenu->addAction(iconp, i18nc("Help", "No property found. You must create a property from the dock first."));
0235                 act->setEnabled(false);
0236             }
0237 
0238             // Check if the sub process is still running
0239             if (m_billsProcess.state() == QProcess::Running) {
0240                 // Create separator
0241                 {
0242                     QAction* act = m_addPropertyMenu->addAction(QLatin1String(""));
0243                     act->setSeparator(true);
0244                 }
0245 
0246                 // Add download on going
0247                 QAction* act = m_addPropertyMenu->addAction(i18nc("Message", "Download list of available bills on going…"));
0248                 if (act != nullptr) {
0249                     act->setEnabled(false);
0250                 }
0251 
0252             } else {
0253                 // Check if some bills can be downloaded
0254                 int nb2 = m_bills.count();
0255                 if (nb2 != 0) {
0256                     // Create separator
0257                     {
0258                         QAction* act = m_addPropertyMenu->addAction(QLatin1String(""));
0259                         act->setSeparator(true);
0260                     }
0261 
0262                     // Create action
0263                     QStringList importedBills = SKGServices::splitCSVLine(m_currentDocument->getParameter(QStringLiteral("SKG_IMPORTED_BILLS")));
0264 
0265                     QMenu* menuMore = nullptr;
0266                     QIcon icond = SKGServices::fromTheme(icon(), QStringList() << QStringLiteral("download"));
0267                     QSet<QString> backendDone;
0268                     for (int j = 1; j < nb2; ++j) {
0269                         // id;date;format;label;idparent;price;currency;deadline;startdate;finishdate
0270                         QStringList fields = SKGServices::splitCSVLine(m_bills.at(j));
0271                         if (fields.count() > 3 && !importedBills.contains(fields.at(0))) {
0272                             QStringList ids = SKGServices::splitCSVLine(fields.at(0), '@');
0273                             if (ids.count() == 2) {
0274                                 const QString& backend = ids.at(1);
0275 
0276                                 // Selection of the menu where the item must be added
0277                                 QMenu* menu;
0278                                 if (!backendDone.contains(backend)) {
0279                                     // This item must be added in root menu
0280                                     menu = m_addPropertyMenu;
0281                                     backendDone.insert(backend);
0282                                 } else {
0283                                     // This item must be added in "More…" menu
0284                                     if (menuMore == nullptr) {
0285                                         menuMore = new QMenu(i18nc("Noun", "More…"), m_addPropertyMenu);
0286                                     }
0287                                     menu = menuMore;
0288                                 }
0289 
0290                                 // Should the string below be translated ??? It contains no word…
0291                                 QAction* act = menu->addAction(icond, i18nc("Add a property (attribute=value)", "Download and add %1 (%2)", fields[3] % '.' % fields[2], fields[0]));
0292                                 if (act != nullptr) {
0293                                     act->setToolTip(fields[0]);
0294                                     act->setData(fields);
0295                                     connect(act, &QAction::triggered, this, &SKGPropertiesPlugin::onDownloadAndAddBills);
0296                                 }
0297                             }
0298                         }
0299                     }
0300 
0301                     // Add "More…" menu
0302                     if (menuMore != nullptr) {
0303                         m_addPropertyMenu->addMenu(menuMore);
0304                     }
0305                 }
0306             }
0307         }
0308     }
0309 }
0310 
0311 void SKGPropertiesPlugin::refresh()
0312 {
0313     SKGTRACEINFUNC(10)
0314     if (m_dockContent != nullptr) {
0315         m_dockContent->refresh();
0316     }
0317 }
0318 
0319 QDockWidget* SKGPropertiesPlugin::getDockWidget()
0320 {
0321     return m_dockWidget;
0322 }
0323 
0324 QString SKGPropertiesPlugin::title() const
0325 {
0326     return i18nc("Noun, an item's properties", "Properties");
0327 }
0328 
0329 QString SKGPropertiesPlugin::icon() const
0330 {
0331     return QStringLiteral("tag");
0332 }
0333 
0334 int SKGPropertiesPlugin::getOrder() const
0335 {
0336     return 6;
0337 }
0338 
0339 QStringList SKGPropertiesPlugin::tips() const
0340 {
0341     QStringList output;
0342     output.push_back(i18nc("Description of a tip", "<p>… you can manage properties on all objects.</p>"));
0343     output.push_back(i18nc("Description of a tip", "<p>… you can add files or Internet links as property.</p>"));
0344     output.push_back(i18nc("Description of a tip", "<p>… you can automatically download and add bills as properties by using %1.</p>", "weboob"));
0345     return output;
0346 }
0347 
0348 #include <skgpropertiesplugin.moc>