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>