File indexing completed on 2024-05-12 05:13:02
0001 /* 0002 This file is part of Akregator. 0003 0004 SPDX-FileCopyrightText: 2005 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net> 0005 SPDX-FileCopyrightText: 2005 Frank Osterfeld <osterfeld@kde.org> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later WITH Qt-Commercial-exception-1.0 0008 */ 0009 #include "storage.h" 0010 0011 #include "mk4.h" 0012 0013 #include <QMap> 0014 #include <QString> 0015 #include <QStringList> 0016 #include <QTimer> 0017 0018 #include <QDateTime> 0019 #include <QDir> 0020 #include <QStandardPaths> 0021 #include <chrono> 0022 0023 using namespace std::chrono_literals; 0024 0025 class Akregator::Backend::Storage::StoragePrivate 0026 { 0027 public: 0028 StoragePrivate() 0029 : purl("url") 0030 , pFeedList("feedList") 0031 , punread("unread") 0032 , ptotalCount("totalCount") 0033 , plastFetch("lastFetch") 0034 { 0035 } 0036 0037 c4_Storage *storage; 0038 Akregator::Backend::Storage *q; 0039 c4_View archiveView; 0040 bool autoCommit = false; 0041 bool modified = false; 0042 mutable QMap<QString, Akregator::Backend::FeedStorage *> feeds; 0043 QStringList feedURLs; 0044 c4_StringProp purl, pFeedList; 0045 c4_IntProp punread, ptotalCount, plastFetch; 0046 QString archivePath; 0047 0048 c4_Storage *feedListStorage; 0049 c4_View feedListView; 0050 0051 Akregator::Backend::FeedStorage *createFeedStorage(const QString &url); 0052 }; 0053 0054 Akregator::Backend::Storage::Storage() 0055 : d(new StoragePrivate) 0056 { 0057 d->q = this; 0058 setArchivePath(QString()); 0059 } 0060 0061 Akregator::Backend::FeedStorage *Akregator::Backend::Storage::StoragePrivate::createFeedStorage(const QString &url) 0062 { 0063 if (!feeds.contains(url)) { 0064 auto fs = new Akregator::Backend::FeedStorage(url, q); 0065 feeds[url] = fs; 0066 c4_Row findrow; 0067 purl(findrow) = url.toLatin1().constData(); 0068 int findidx = archiveView.Find(findrow); 0069 if (findidx == -1) { 0070 punread(findrow) = 0; 0071 ptotalCount(findrow) = 0; 0072 plastFetch(findrow) = 0; 0073 archiveView.Add(findrow); 0074 modified = true; 0075 } 0076 } 0077 return feeds[url]; 0078 } 0079 0080 Akregator::Backend::FeedStorage *Akregator::Backend::Storage::archiveFor(const QString &url) 0081 { 0082 return d->createFeedStorage(url); 0083 } 0084 0085 const Akregator::Backend::FeedStorage *Akregator::Backend::Storage::archiveFor(const QString &url) const 0086 { 0087 return d->createFeedStorage(url); 0088 } 0089 0090 void Akregator::Backend::Storage::setArchivePath(const QString &archivePath) 0091 { 0092 if (archivePath.isNull()) { // if isNull, reset to default 0093 d->archivePath = defaultArchivePath(); 0094 } else { 0095 d->archivePath = archivePath; 0096 } 0097 } 0098 0099 QString Akregator::Backend::Storage::archivePath() const 0100 { 0101 return d->archivePath; 0102 } 0103 0104 QString Akregator::Backend::Storage::defaultArchivePath() 0105 { 0106 const QString ret = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/akregator/Archive"); 0107 QDir().mkpath(ret); 0108 return ret; 0109 } 0110 0111 Akregator::Backend::Storage::~Storage() 0112 { 0113 close(); 0114 } 0115 0116 bool Akregator::Backend::Storage::open(bool autoCommit) 0117 { 0118 QString filePath = d->archivePath + QLatin1StringView("/archiveindex.mk4"); 0119 d->storage = new c4_Storage(filePath.toLocal8Bit().constData(), true); 0120 d->archiveView = d->storage->GetAs("archive[url:S,unread:I,totalCount:I,lastFetch:I]"); 0121 c4_View hash = d->storage->GetAs("archiveHash[_H:I,_R:I]"); 0122 d->archiveView = d->archiveView.Hash(hash, 1); // hash on url 0123 d->autoCommit = autoCommit; 0124 0125 filePath = d->archivePath + QLatin1StringView("/feedlistbackup.mk4"); 0126 d->feedListStorage = new c4_Storage(filePath.toLocal8Bit().constData(), true); 0127 d->feedListView = d->feedListStorage->GetAs("archive[feedList:S,tagSet:S]"); 0128 return true; 0129 } 0130 0131 bool Akregator::Backend::Storage::autoCommit() const 0132 { 0133 return d->autoCommit; 0134 } 0135 0136 void Akregator::Backend::Storage::close() 0137 { 0138 QMap<QString, FeedStorage *>::Iterator it; 0139 QMap<QString, FeedStorage *>::Iterator end(d->feeds.end()); 0140 for (it = d->feeds.begin(); it != end; ++it) { 0141 it.value()->close(); 0142 delete it.value(); 0143 } 0144 if (d->autoCommit) { 0145 d->storage->Commit(); 0146 } 0147 0148 delete d->storage; 0149 d->storage = nullptr; 0150 0151 d->feedListStorage->Commit(); 0152 delete d->feedListStorage; 0153 d->feedListStorage = nullptr; 0154 } 0155 0156 bool Akregator::Backend::Storage::commit() 0157 { 0158 QMap<QString, FeedStorage *>::Iterator it; 0159 QMap<QString, FeedStorage *>::Iterator end(d->feeds.end()); 0160 for (it = d->feeds.begin(); it != end; ++it) { 0161 it.value()->commit(); 0162 } 0163 0164 if (d->storage) { 0165 d->storage->Commit(); 0166 return true; 0167 } 0168 0169 return false; 0170 } 0171 0172 bool Akregator::Backend::Storage::rollback() 0173 { 0174 QMap<QString, FeedStorage *>::Iterator it; 0175 QMap<QString, FeedStorage *>::Iterator end(d->feeds.end()); 0176 for (it = d->feeds.begin(); it != end; ++it) { 0177 it.value()->rollback(); 0178 } 0179 0180 if (d->storage) { 0181 d->storage->Rollback(); 0182 return true; 0183 } 0184 return false; 0185 } 0186 0187 int Akregator::Backend::Storage::unreadFor(const QString &url) const 0188 { 0189 c4_Row findrow; 0190 d->purl(findrow) = url.toLatin1().constData(); 0191 int findidx = d->archiveView.Find(findrow); 0192 0193 return findidx != -1 ? d->punread(d->archiveView.GetAt(findidx)) : 0; 0194 } 0195 0196 void Akregator::Backend::Storage::setUnreadFor(const QString &url, int unread) 0197 { 0198 c4_Row findrow; 0199 d->purl(findrow) = url.toLatin1().constData(); 0200 int findidx = d->archiveView.Find(findrow); 0201 if (findidx == -1) { 0202 return; 0203 } 0204 findrow = d->archiveView.GetAt(findidx); 0205 d->punread(findrow) = unread; 0206 d->archiveView.SetAt(findidx, findrow); 0207 markDirty(); 0208 } 0209 0210 int Akregator::Backend::Storage::totalCountFor(const QString &url) const 0211 { 0212 c4_Row findrow; 0213 d->purl(findrow) = url.toLatin1().constData(); 0214 int findidx = d->archiveView.Find(findrow); 0215 0216 return findidx != -1 ? d->ptotalCount(d->archiveView.GetAt(findidx)) : 0; 0217 } 0218 0219 void Akregator::Backend::Storage::setTotalCountFor(const QString &url, int total) 0220 { 0221 c4_Row findrow; 0222 d->purl(findrow) = url.toLatin1().constData(); 0223 int findidx = d->archiveView.Find(findrow); 0224 if (findidx == -1) { 0225 return; 0226 } 0227 findrow = d->archiveView.GetAt(findidx); 0228 d->ptotalCount(findrow) = total; 0229 d->archiveView.SetAt(findidx, findrow); 0230 markDirty(); 0231 } 0232 0233 QDateTime Akregator::Backend::Storage::lastFetchFor(const QString &url) const 0234 { 0235 c4_Row findrow; 0236 d->purl(findrow) = url.toLatin1().constData(); 0237 int findidx = d->archiveView.Find(findrow); 0238 0239 return findidx != -1 ? QDateTime::fromSecsSinceEpoch(d->plastFetch(d->archiveView.GetAt(findidx))) : QDateTime(); 0240 } 0241 0242 void Akregator::Backend::Storage::setLastFetchFor(const QString &url, const QDateTime &lastFetch) 0243 { 0244 c4_Row findrow; 0245 d->purl(findrow) = url.toLatin1().constData(); 0246 int findidx = d->archiveView.Find(findrow); 0247 if (findidx == -1) { 0248 return; 0249 } 0250 findrow = d->archiveView.GetAt(findidx); 0251 d->plastFetch(findrow) = lastFetch.toSecsSinceEpoch(); 0252 d->archiveView.SetAt(findidx, findrow); 0253 markDirty(); 0254 } 0255 0256 void Akregator::Backend::Storage::markDirty() 0257 { 0258 if (!d->modified) { 0259 d->modified = true; 0260 // commit changes after 3 seconds 0261 QTimer::singleShot(3s, this, &Storage::slotCommit); 0262 } 0263 } 0264 0265 void Akregator::Backend::Storage::slotCommit() 0266 { 0267 if (d->modified) { 0268 commit(); 0269 } 0270 d->modified = false; 0271 } 0272 0273 QStringList Akregator::Backend::Storage::feeds() const 0274 { 0275 // TODO: cache list 0276 QStringList list; 0277 const int size = d->archiveView.GetSize(); 0278 list.reserve(size); 0279 for (int i = 0; i < size; ++i) { 0280 list += QString::fromLatin1(QByteArray(d->purl(d->archiveView.GetAt(i)))); 0281 } 0282 // fill with urls 0283 return list; 0284 } 0285 0286 void Akregator::Backend::Storage::storeFeedList(const QString &opmlStr) 0287 { 0288 if (d->feedListView.GetSize() == 0) { 0289 c4_Row row; 0290 d->pFeedList(row) = !opmlStr.isEmpty() ? opmlStr.toUtf8().data() : ""; 0291 d->feedListView.Add(row); 0292 } else { 0293 c4_Row row = d->feedListView.GetAt(0); 0294 d->pFeedList(row) = !opmlStr.isEmpty() ? opmlStr.toUtf8().data() : ""; 0295 d->feedListView.SetAt(0, row); 0296 } 0297 markDirty(); 0298 } 0299 0300 QString Akregator::Backend::Storage::restoreFeedList() const 0301 { 0302 if (d->feedListView.GetSize() == 0) { 0303 return {}; 0304 } 0305 0306 c4_Row row = d->feedListView.GetAt(0); 0307 return QString::fromUtf8(QByteArray(d->pFeedList(row))); 0308 } 0309 0310 #include "moc_storage.cpp"