File indexing completed on 2025-02-09 05:59:19
0001 /* 0002 SPDX-FileCopyrightText: 2019 Thomas Baumgart tbaumgart @kde.org 0003 SPDX-FileCopyrightText: 2024 Ralf Habacker ralf.habacker @freenet.de 0004 0005 This file is part of libalkimia. 0006 0007 SPDX-License-Identifier: LGPL-2.1-or-later 0008 */ 0009 0010 #include "alkonlinequotesprofile_p.h" 0011 0012 #include "alkutils.h" 0013 0014 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) 0015 #include <QDebug> 0016 #include <QRegularExpression> 0017 #else 0018 #include <KConfig> 0019 #include <KDebug> 0020 #endif 0021 0022 // define static members 0023 QString AlkOnlineQuotesProfile::Private::m_financeQuoteScriptPath; 0024 QStringList AlkOnlineQuotesProfile::Private::m_financeQuoteSources; 0025 0026 bool AlkOnlineQuotesProfile::Private::setupFinanceQuoteScriptPath() 0027 { 0028 if (m_financeQuoteScriptPath.isEmpty()) { 0029 m_financeQuoteScriptPath = AlkUtils::locateDataFile("misc/financequote.pl"); 0030 } 0031 return !m_financeQuoteScriptPath.isEmpty(); 0032 } 0033 0034 AlkOnlineQuotesProfile::Private::Private(AlkOnlineQuotesProfile *p) 0035 : m_p(p) 0036 , m_profileManager(0) 0037 , m_engine(new AlkNewStuffEngine) 0038 , m_config(0) 0039 , m_type(Type::Undefined) 0040 { 0041 #ifdef ENABLE_FINANCEQUOTE 0042 setupFinanceQuoteScriptPath(); 0043 #endif 0044 #if QT_VERSION < QT_VERSION_CHECK(5,0,0) 0045 connect(m_engine, SIGNAL(updatesAvailable(AlkNewStuffEntryList)), this, 0046 SLOT(slotUpdatesAvailable(AlkNewStuffEntryList))); 0047 #else 0048 connect(m_engine, &AlkNewStuffEngine::updatesAvailable, this, 0049 &AlkOnlineQuotesProfile::Private::slotUpdatesAvailable); 0050 #endif 0051 } 0052 0053 AlkOnlineQuotesProfile::Private::~Private() 0054 { 0055 delete m_engine; 0056 } 0057 0058 QString AlkOnlineQuotesProfile::Private::GHNSId(const QString &name) const 0059 { 0060 for (const AlkNewStuffEntry &entry : m_engine->installedEntries()) { 0061 if (entry.name == name) 0062 return entry.id; 0063 } 0064 return QString(); 0065 } 0066 0067 QString AlkOnlineQuotesProfile::Private::GHNSFilePath(const QString &name) const 0068 { 0069 for (const AlkNewStuffEntry &entry : m_engine->installedEntries()) { 0070 if (entry.name == name) 0071 return entry.installedFiles.size() > 0 ? entry.installedFiles[0] : QString(); 0072 } 0073 return QString(); 0074 } 0075 0076 const QStringList AlkOnlineQuotesProfile::Private::quoteSourcesNative() 0077 { 0078 auto kconfig = KSharedConfig::openConfig(m_kconfigFile, KConfig::SimpleConfig); 0079 QStringList groups = kconfig->groupList(); 0080 0081 QStringList::Iterator it; 0082 #if QT_VERSION < QT_VERSION_CHECK(5,0,0) 0083 QRegExp onlineQuoteSource(QString("^Online-Quote-Source-(.*)$")); 0084 0085 // get rid of all 'non online quote source' entries 0086 for (it = groups.begin(); it != groups.end(); it = groups.erase(it)) { 0087 if (onlineQuoteSource.indexIn(*it) >= 0) { 0088 // Insert the name part 0089 it = groups.insert(it, onlineQuoteSource.cap(1)); 0090 ++it; 0091 } 0092 } 0093 #else 0094 QRegularExpression onlineQuoteSource(QLatin1String("^Online-Quote-Source-(.*)$")); 0095 0096 // get rid of all 'non online quote source' entries 0097 for (it = groups.begin(); it != groups.end(); it = groups.erase(it)) { 0098 const auto match = onlineQuoteSource.match(*it); 0099 if (match.hasMatch()) { 0100 // Insert the name part 0101 it = groups.insert(it, match.captured(1)); 0102 ++it; 0103 } 0104 } 0105 #endif 0106 // Set up each of the default sources. These are done piecemeal so that 0107 // when we add a new source, it's automatically picked up. And any changes 0108 // are also picked up. 0109 QMap<QString, AlkOnlineQuoteSource> defaults = defaultQuoteSources(); 0110 QMap<QString, AlkOnlineQuoteSource>::iterator it_source = defaults.begin(); 0111 while (it_source != defaults.end()) { 0112 if (!groups.contains((*it_source).name())) { 0113 groups += (*it_source).name(); 0114 (*it_source).write(); 0115 kconfig->sync(); 0116 } 0117 ++it_source; 0118 } 0119 0120 return groups; 0121 } 0122 0123 #ifdef ENABLE_FINANCEQUOTE 0124 const QStringList AlkOnlineQuotesProfile::Private::quoteSourcesFinanceQuote() 0125 { 0126 if (m_financeQuoteSources.empty()) { // run the process one time only 0127 // since this is a static function it can be called without constructing an object 0128 // so we need to make sure that m_financeQuoteScriptPath is properly initialized 0129 if (setupFinanceQuoteScriptPath()) { 0130 AlkFinanceQuoteProcess testList; 0131 testList.testLaunch(m_financeQuoteScriptPath); 0132 while (!testList.isFinished()) { 0133 qApp->processEvents(); 0134 } 0135 0136 if (testList.exitCode() == 0) { 0137 AlkFinanceQuoteProcess getList; 0138 getList.launch(m_financeQuoteScriptPath); 0139 while (!getList.isFinished()) { 0140 qApp->processEvents(); 0141 } 0142 m_financeQuoteSources = getList.getSourceList(); 0143 } 0144 } 0145 } 0146 return m_financeQuoteSources; 0147 } 0148 #endif 0149 0150 const QStringList AlkOnlineQuotesProfile::Private::quoteSourcesSkrooge() 0151 { 0152 return quoteSourcesGHNS(); 0153 } 0154 0155 const QStringList AlkOnlineQuotesProfile::Private::quoteSourcesGHNS() 0156 { 0157 QStringList sources; 0158 QStringList files = AlkUtils::getDataFiles(m_GHNSFilePath, QStringList() << QStringLiteral("*.txt")); 0159 0160 // add installed remote sources 0161 for (const AlkNewStuffEntry &entry : m_engine->installedEntries()) { 0162 AlkOnlineQuoteSource source(entry.name, m_p); 0163 if (entry.installedFiles.size() > 0) 0164 files.removeAll(entry.installedFiles[0]); 0165 if (source.isEmpty()) { 0166 qDebug() << "skipping" << entry.name; 0167 continue; 0168 } 0169 if (!sources.contains(entry.id)) { 0170 qDebug() << "adding quote source" << entry.name; 0171 sources.push_back(entry.name); 0172 } 0173 } 0174 0175 // add unpublished remote sources 0176 for (const QString &file : files) { 0177 QFileInfo f(file); 0178 QString name = f.completeBaseName(); 0179 AlkOnlineQuoteSource source(name, m_p); 0180 if (source.isEmpty()) { 0181 qDebug() << "skipping" << name; 0182 continue; 0183 } 0184 if (!sources.contains(name)) { 0185 qDebug() << "adding quote source" << name; 0186 sources.push_back(name); 0187 } 0188 } 0189 return sources; 0190 } 0191 0192 const AlkOnlineQuotesProfile::Map AlkOnlineQuotesProfile::Private::defaultQuoteSources() 0193 { 0194 QMap<QString, AlkOnlineQuoteSource> result; 0195 0196 // Use fx-rate.net as the standard currency exchange rate source until 0197 // we have the capability to use more than one source. Use a neutral 0198 // name for the source. 0199 0200 switch (m_p->type()) { 0201 case AlkOnlineQuotesProfile::Type::None: 0202 case AlkOnlineQuotesProfile::Type::Alkimia4: 0203 case AlkOnlineQuotesProfile::Type::Alkimia5: { 0204 AlkOnlineQuoteSource source(AlkOnlineQuoteSource::defaultCurrencyQuoteSource("Alkimia Currency")); 0205 source.setProfile(m_p); 0206 result[source.name()] = source; 0207 #if defined(BUILD_WITH_WEBKIT) || defined(BUILD_WITH_WEBENGINE) 0208 AlkOnlineQuoteSource source2(AlkOnlineQuoteSource::defaultCurrencyQuoteSource("Alkimia Currency.webkit")); 0209 source2.setProfile(m_p); 0210 result[source2.name()] = source2; 0211 #endif 0212 break; 0213 } 0214 default: 0215 break; 0216 } 0217 return result; 0218 } 0219 0220 QString AlkOnlineQuotesProfile::Private::dataRootPath() 0221 { 0222 return QLibraryInfo::location(QLibraryInfo::PrefixPath) + "/share"; 0223 } 0224 0225 QString AlkOnlineQuotesProfile::Private::homeRootPath() 0226 { 0227 if (m_type == Type::KMyMoney5 || m_type == Type::Alkimia5 || m_type == Type::Skrooge5) 0228 return QDir::homePath(); 0229 else if (m_type == Type::KMyMoney4 || m_type == Type::Alkimia4 || m_type == Type::Skrooge4) { 0230 #ifdef Q_OS_WIN 0231 return qgetenv("APPDATA"); 0232 #else 0233 return QDir::homePath(); 0234 #endif 0235 } else { 0236 return QString(); 0237 } 0238 } 0239 0240 QString AlkOnlineQuotesProfile::Private::configPath() 0241 { 0242 #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) 0243 if (m_type == Type::KMyMoney5) 0244 return QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); 0245 else if(m_type == Type::Alkimia5 || m_type == Type::Skrooge5) 0246 return QString("%1/.config").arg(homeRootPath()); 0247 else 0248 #endif 0249 if (m_type == Type::KMyMoney4 || m_type == Type::Alkimia4 || m_type == Type::Skrooge4) 0250 return QString("%1/.kde4/share/config").arg(homeRootPath()); 0251 return 0252 QString(); 0253 } 0254 0255 QString AlkOnlineQuotesProfile::Private::dataReadPath() 0256 { 0257 if (m_type == Type::KMyMoney5 || m_type == Type::Alkimia5 || m_type == Type::Skrooge5) 0258 return dataRootPath(); 0259 else if (m_type == Type::KMyMoney4 || m_type == Type::Alkimia4 || m_type == Type::Skrooge4) 0260 return QString("%1/kde4/apps").arg(dataRootPath()); 0261 return 0262 QString(); 0263 } 0264 0265 QString AlkOnlineQuotesProfile::Private::dataWritePath() 0266 { 0267 if (m_type == Type::KMyMoney5 || m_type == Type::Alkimia5 || m_type == Type::Skrooge5) 0268 return QString("%1/.local/share").arg(homeRootPath()); 0269 else if (m_type == Type::KMyMoney4 || m_type == Type::Alkimia4 || m_type == Type::Skrooge4) 0270 return QString("%1/.kde4/share/apps").arg(homeRootPath()); 0271 return 0272 QString(); 0273 } 0274 0275 void AlkOnlineQuotesProfile::Private::slotUpdatesAvailable(const AlkNewStuffEntryList &updates) 0276 { 0277 for (auto &entry : updates) { 0278 qDebug() << "update available in profile" << m_p->name() << "for" 0279 << entry.name << entry.version << entry.id << entry.category 0280 << entry.providerId; 0281 Q_EMIT m_p->updateAvailable(m_p->name(), entry.name); 0282 } 0283 }