File indexing completed on 2024-05-19 05:07:26
0001 /* 0002 SPDX-FileCopyrightText: 2013-2018 Christian Dávid <christian-david@web.de> 0003 SPDX-FileCopyrightText: 2019-2022 Thomas Baumgart <tbaumgart@kde.org> 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "onlinejobadministration.h" 0008 0009 // ---------------------------------------------------------------------------- 0010 // Std Includes 0011 0012 #include <memory> 0013 0014 // ---------------------------------------------------------------------------- 0015 // QT Includes 0016 0017 #include <QDebug> 0018 #include <QJsonArray> 0019 #include <QList> 0020 #include <QPluginLoader> 0021 #include <QXmlStreamReader> 0022 0023 // ---------------------------------------------------------------------------- 0024 // KDE Includes 0025 #include <KJsonUtils> 0026 #include <KPluginFactory> 0027 #include <KPluginMetaData> 0028 0029 // ---------------------------------------------------------------------------- 0030 // Project Includes 0031 0032 #include "mymoneyaccount.h" 0033 #include "mymoneyfile.h" 0034 #include "mymoneykeyvaluecontainer.h" 0035 #include "onlinejobsmodel.h" 0036 #include "onlinepluginextended.h" 0037 #include "onlinetasks/unavailabletask/tasks/unavailabletask.h" 0038 #include "onlinetasks/interfaces/tasks/credittransfer.h" 0039 #include "tasks/onlinetask.h" 0040 0041 onlineJobAdministration::onlineJobAdministration(QObject *parent) 0042 : QObject(parent) 0043 , m_onlinePlugins(nullptr) 0044 , m_inRegistration(false) 0045 { 0046 } 0047 0048 onlineJobAdministration::~onlineJobAdministration() 0049 { 0050 clearCaches(); 0051 } 0052 0053 onlineJobAdministration* onlineJobAdministration::instance() 0054 { 0055 static onlineJobAdministration m_instance; 0056 return &m_instance; 0057 } 0058 0059 void onlineJobAdministration::clearCaches() 0060 { 0061 qDeleteAll(m_onlineTasks); 0062 m_onlineTasks.clear(); 0063 qDeleteAll(m_onlineTaskConverter); 0064 m_onlineTaskConverter.clear(); 0065 } 0066 0067 KMyMoneyPlugin::OnlinePluginExtended* onlineJobAdministration::getOnlinePlugin(const QString& accountId) const 0068 { 0069 MyMoneyAccount acc = MyMoneyFile::instance()->account(accountId); 0070 0071 QMap<QString, KMyMoneyPlugin::OnlinePluginExtended*>::const_iterator it_p; 0072 it_p = m_onlinePlugins->constFind(acc.onlineBankingSettings().value("provider").toLower()); 0073 0074 if (it_p != m_onlinePlugins->constEnd()) { 0075 // plugin found, use it 0076 return *it_p; 0077 } 0078 return 0; 0079 } 0080 0081 void onlineJobAdministration::setOnlinePlugins(QMap<QString, KMyMoneyPlugin::OnlinePluginExtended*>& plugins) 0082 { 0083 m_onlinePlugins = &plugins; 0084 updateActions(); 0085 } 0086 0087 void onlineJobAdministration::updateActions() 0088 { 0089 Q_EMIT canSendAnyTaskChanged(canSendAnyTask()); 0090 Q_EMIT canSendCreditTransferChanged(canSendCreditTransfer()); 0091 } 0092 0093 QStringList onlineJobAdministration::availableOnlineTasks() 0094 { 0095 const auto plugins = KPluginMetaData::findPlugins("kmymoney_plugins/onlinetasks", [](const KPluginMetaData& data) { 0096 return !(data.rawData()["KMyMoney"].toObject()["OnlineTask"].isNull()); 0097 }); 0098 0099 QStringList list; 0100 for (const KPluginMetaData& plugin : qAsConst(plugins)) { 0101 QJsonValue array = plugin.rawData()["KMyMoney"].toObject()["OnlineTask"].toObject()["Iids"]; 0102 if (array.isArray()) 0103 list.append(array.toVariant().toStringList()); 0104 } 0105 return list; 0106 } 0107 0108 /** 0109 * @internal The real work is done here. 0110 */ 0111 bool onlineJobAdministration::isJobSupported(const QString& accountId, const QString& name) const 0112 { 0113 if (!m_onlinePlugins) 0114 return false; 0115 for (KMyMoneyPlugin::OnlinePluginExtended* plugin : qAsConst(*m_onlinePlugins)) { 0116 if (plugin->availableJobs(accountId).contains(name)) 0117 return true; 0118 } 0119 return false; 0120 } 0121 0122 bool onlineJobAdministration::isJobSupported(const QString& accountId, const QStringList& names) const 0123 { 0124 for (const auto& name : names) { 0125 if (isJobSupported(accountId, name)) 0126 return true; 0127 } 0128 return false; 0129 } 0130 0131 bool onlineJobAdministration::isAnyJobSupported(const QString& accountId) const 0132 { 0133 if (accountId.isEmpty()) 0134 return false; 0135 0136 if (!m_onlinePlugins) 0137 return false; 0138 0139 for (KMyMoneyPlugin::OnlinePluginExtended* plugin : qAsConst(*m_onlinePlugins)) { 0140 if (!(plugin->availableJobs(accountId).isEmpty())) 0141 return true; 0142 } 0143 return false; 0144 } 0145 0146 onlineJob onlineJobAdministration::createOnlineJob(const QString& name, const QString& id) const 0147 { 0148 return (onlineJob(createOnlineTask(name), id)); 0149 } 0150 0151 onlineTask* onlineJobAdministration::createOnlineTask(const QString& name) const 0152 { 0153 const onlineTask* task = rootOnlineTask(name); 0154 if (task) 0155 return task->clone(); 0156 return nullptr; 0157 } 0158 0159 onlineTask* onlineJobAdministration::createOnlineTaskByXml(QXmlStreamReader* reader, const QString& iid) const 0160 { 0161 onlineTask* task = rootOnlineTask(iid); 0162 if (task) { 0163 return task->createFromXml(reader); 0164 } 0165 qWarning("In the file is a onlineTask for which I could not find the plugin ('%s')", qPrintable(iid)); 0166 // the taskTemplate object is only used to get access 0167 // to the createFromXml() method which returns 0168 // a pointer to the actual newly created object. 0169 unavailableTask taskTemplate(QString{}); 0170 task = taskTemplate.createFromXml(reader); 0171 return task; 0172 } 0173 0174 /** 0175 * @internal Using KPluginFactory to create the plugins seemed to be good idea. The drawback is that it does not support to create non QObjects directly. 0176 * This made this function way longer than needed and adds many checks. 0177 * 0178 * @fixme Delete created tasks 0179 */ 0180 onlineTask* onlineJobAdministration::rootOnlineTask(const QString& name) const 0181 { 0182 auto plugins = KPluginMetaData::findPlugins("kmymoney_plugins/onlinetasks", [&name](const KPluginMetaData& data) { 0183 QJsonValue array = data.rawData()["KMyMoney"].toObject()["OnlineTask"].toObject()["Iids"]; 0184 if (array.isArray()) 0185 return (array.toVariant().toStringList().contains(name)); 0186 return false; 0187 }); 0188 0189 if (plugins.isEmpty()) 0190 return nullptr; 0191 0192 if (plugins.length() != 1) 0193 qWarning() << "Multiple plugins which offer the online task \"" << name << "\" were found. Loading a random one."; 0194 0195 auto pluginResult = KPluginFactory::instantiatePlugin<KMyMoneyPlugin::onlineTaskFactory>(plugins.first(), onlineJobAdministration::instance()); 0196 if (!pluginResult) { 0197 qWarning() << "Could not load plugin for online task " << name << ", file name " << plugins.first().fileName() << "."; 0198 return nullptr; 0199 } 0200 auto taskFactory = pluginResult.plugin; 0201 0202 // Finally create task 0203 onlineTask* task = taskFactory->createOnlineTask(name); 0204 if (task) 0205 // Add to our cache as this is still used in several places 0206 onlineJobAdministration::instance()->registerOnlineTask(taskFactory->createOnlineTask(name)); 0207 0208 return task; 0209 } 0210 0211 onlineTaskConverter::convertType onlineJobAdministration::canConvert(const QString& originalTaskIid, const QString& convertTaskIid) const 0212 { 0213 return canConvert(originalTaskIid, QStringList(convertTaskIid)); 0214 } 0215 0216 onlineTaskConverter::convertType onlineJobAdministration::canConvert(const QString& originalTaskIid, const QStringList& convertTaskIids) const 0217 { 0218 Q_ASSERT(false); 0219 //! @todo Make alive 0220 onlineTaskConverter::convertType bestConvertType = onlineTaskConverter::convertImpossible; 0221 #if 0 0222 for (QString destinationName : destinationNames) { 0223 onlineTask::convertType type = canConvert(original, destinationName); 0224 if (type == onlineTask::convertionLossy) 0225 bestConvertType = onlineTask::convertionLossy; 0226 else if (type == onlineTask::convertionLoseless) 0227 return onlineTask::convertionLoseless; 0228 } 0229 #else 0230 Q_UNUSED(originalTaskIid); 0231 Q_UNUSED(convertTaskIids); 0232 #endif 0233 return bestConvertType; 0234 } 0235 0236 /** 0237 * @todo if more than one converter offers the convert, use best 0238 */ 0239 onlineJob onlineJobAdministration::convert(const onlineJob& original, const QString& convertTaskIid, onlineTaskConverter::convertType& convertType, QString& userInformation, const QString& onlineJobId) const 0240 { 0241 onlineJob newJob; 0242 0243 const QList<onlineTaskConverter*> converterList = m_onlineTaskConverter.values(convertTaskIid); 0244 for (const onlineTaskConverter* converter : converterList) { 0245 if (converter->convertibleTasks().contains(original.taskIid())) { 0246 onlineTask* task = converter->convert(*original.task(), convertType, userInformation); 0247 Q_ASSERT_X(convertType != onlineTaskConverter::convertImpossible || task != 0, qPrintable("converter for " + converter->convertedTask()), "Converter returned convertType 'impossible' but return was not null_ptr."); 0248 if (task != 0) { 0249 newJob = onlineJob(task, onlineJobId); 0250 break; 0251 } 0252 } 0253 } 0254 0255 return newJob; 0256 } 0257 0258 onlineJob onlineJobAdministration::convertBest(const onlineJob& original, const QStringList& convertTaskIids, onlineTaskConverter::convertType& convertType, QString& userInformation) const 0259 { 0260 return convertBest(original, convertTaskIids, convertType, userInformation, original.id()); 0261 } 0262 0263 onlineJob onlineJobAdministration::convertBest(const onlineJob& original, const QStringList& convertTaskIids, onlineTaskConverter::convertType& bestConvertType, QString& bestUserInformation, const QString& onlineJobId) const 0264 { 0265 onlineJob bestConvert; 0266 bestConvertType = onlineTaskConverter::convertImpossible; 0267 bestUserInformation = QString(); 0268 0269 for (const auto& taskIid : convertTaskIids) { 0270 // Try convert 0271 onlineTaskConverter::convertType convertType = onlineTaskConverter::convertImpossible; 0272 QString userInformation; 0273 onlineJob convertJob = convert(original, taskIid, convertType, userInformation, onlineJobId); 0274 0275 // Check if it was successful 0276 if (bestConvertType < convertType) { 0277 bestConvert = convertJob; 0278 bestUserInformation = userInformation; 0279 bestConvertType = convertType; 0280 if (convertType == onlineTaskConverter::convertionLoseless) 0281 break; 0282 } 0283 } 0284 0285 return bestConvert; 0286 } 0287 0288 void onlineJobAdministration::registerAllOnlineTasks() 0289 { 0290 // avoid recursive entrance 0291 if (m_inRegistration) 0292 return; 0293 0294 m_inRegistration = true; 0295 const QStringList availableTasks = availableOnlineTasks(); 0296 for (const auto& name : availableTasks) { 0297 onlineTask* const task = rootOnlineTask(name); 0298 Q_UNUSED(task); 0299 } 0300 m_inRegistration = false; 0301 } 0302 0303 void onlineJobAdministration::registerOnlineTask(onlineTask *const task) 0304 { 0305 if (Q_UNLIKELY(task == 0)) 0306 return; 0307 0308 const bool sendAnyTask = canSendAnyTask(); 0309 const bool sendCreditTransfer = canSendCreditTransfer(); 0310 0311 m_onlineTasks.insert(task->taskName(), task); 0312 0313 if (sendAnyTask != canSendAnyTask()) 0314 Q_EMIT canSendAnyTaskChanged(!sendAnyTask); 0315 if (sendCreditTransfer != canSendCreditTransfer()) 0316 Q_EMIT canSendCreditTransferChanged(!sendCreditTransfer); 0317 } 0318 0319 void onlineJobAdministration::registerOnlineTaskConverter(onlineTaskConverter* const converter) 0320 { 0321 if (Q_UNLIKELY(converter == 0)) 0322 return; 0323 0324 m_onlineTaskConverter.insert(converter->convertedTask(), converter); 0325 qDebug() << "onlineTaskConverter available" << converter->convertedTask() << converter->convertibleTasks(); 0326 } 0327 0328 onlineJobAdministration::onlineJobEditOffers onlineJobAdministration::onlineJobEdits() 0329 { 0330 auto plugins = KPluginMetaData::findPlugins("kmymoney_plugins/onlinetasks", [](const KPluginMetaData& data) { 0331 return !(data.rawData()["KMyMoney"].toObject()["OnlineTask"].toObject()["Editors"].isNull()); 0332 }); 0333 0334 onlineJobAdministration::onlineJobEditOffers list; 0335 list.reserve(plugins.size()); 0336 for (const KPluginMetaData& data : qAsConst(plugins)) { 0337 QJsonArray editorsArray = data.rawData()["KMyMoney"].toObject()["OnlineTask"].toObject()["Editors"].toArray(); 0338 for (const QJsonValue& entry : qAsConst(editorsArray)) { 0339 if (!entry.toObject()["OnlineTaskIds"].isNull()) { 0340 list.append(onlineJobAdministration::onlineJobEditOffer{data.fileName(), KJsonUtils::readTranslatedString(entry.toObject(), "Name")}); 0341 } 0342 } 0343 } 0344 return list; 0345 } 0346 0347 IonlineTaskSettings::ptr onlineJobAdministration::taskSettings(const QString& taskName, const QString& accountId) const 0348 { 0349 KMyMoneyPlugin::OnlinePluginExtended* plugin = getOnlinePlugin(accountId); 0350 if (plugin != 0) 0351 return (plugin->settings(accountId, taskName)); 0352 return IonlineTaskSettings::ptr(); 0353 } 0354 0355 bool onlineJobAdministration::canSendAnyTask() 0356 { 0357 if (!m_onlinePlugins) 0358 return false; 0359 0360 if (m_onlineTasks.isEmpty()) { 0361 registerAllOnlineTasks(); 0362 } 0363 0364 // Check if any plugin supports a loaded online task 0365 /// @todo optimize this loop to move the accounts to the outer loop 0366 for (KMyMoneyPlugin::OnlinePluginExtended* plugin : qAsConst(*m_onlinePlugins)) { 0367 QList<MyMoneyAccount> accounts; 0368 MyMoneyFile::instance()->accountList(accounts, QStringList(), true); 0369 for (const auto& account : qAsConst(accounts)) { 0370 if (account.hasOnlineMapping()) { 0371 const auto availableJobs = plugin->availableJobs(account.id()); 0372 for (const auto& onlineTaskIid : qAsConst(availableJobs)) { 0373 if (m_onlineTasks.contains(onlineTaskIid)) { 0374 return true; 0375 } 0376 } 0377 } 0378 } 0379 } 0380 return false; 0381 } 0382 0383 bool onlineJobAdministration::canSendCreditTransfer() 0384 { 0385 if (!m_onlinePlugins) 0386 return false; 0387 0388 if (m_onlineTasks.isEmpty()) { 0389 registerAllOnlineTasks(); 0390 } 0391 0392 QList<MyMoneyAccount> accounts; 0393 MyMoneyFile::instance()->accountList(accounts, QStringList(), true); 0394 for (const auto& account : qAsConst(accounts)) { 0395 if (account.hasOnlineMapping()) { 0396 for (const onlineTask* task : qAsConst(m_onlineTasks)) { 0397 // Check if a online task has the correct type 0398 if (dynamic_cast<const creditTransfer*>(task) != 0) { 0399 for (KMyMoneyPlugin::OnlinePluginExtended* plugin : qAsConst(*m_onlinePlugins)) { 0400 if (plugin->availableJobs(account.id()).contains(task->taskName())) 0401 return true; 0402 } 0403 } 0404 } 0405 } 0406 } 0407 return false; 0408 } 0409 0410 bool onlineJobAdministration::canEditOnlineJob(const QString& jobId) 0411 { 0412 const auto idx = MyMoneyFile::instance()->onlineJobsModel()->indexById(jobId); 0413 if (idx.isValid()) { 0414 const auto taskIid = idx.data(eMyMoney::Model::OnlineJobTaskIidRole).toString(); 0415 return (!taskIid.isEmpty() && m_onlineTasks.contains(taskIid)); 0416 } 0417 return false; 0418 } 0419 0420 void onlineJobAdministration::updateOnlineTaskProperties() 0421 { 0422 Q_EMIT canSendAnyTaskChanged(canSendAnyTask()); 0423 Q_EMIT canSendCreditTransferChanged(canSendCreditTransfer()); 0424 }