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