30 , mBaseUrl( feedUrl.toString() )
33 , mSettingsKey( keyForFeed( mBaseUrl ) )
38 QUrlQuery query( feedUrl );
45 if ( feedLanguage.isEmpty() )
47 feedLanguage =
QgsSettings().
value( QStringLiteral(
"locale/userLocale" ), QStringLiteral(
"en_US" ) ).toString().left( 2 );
49 if ( !feedLanguage.isEmpty() && feedLanguage != QStringLiteral(
"C" ) )
50 query.addQueryItem( QStringLiteral(
"lang" ), feedLanguage );
56 if ( latOk && longOk )
59 if ( feedUrl.isLocalFile() )
61 query.addQueryItem( QStringLiteral(
"lat" ), QString::number(
static_cast< int >( feedLat ) ) );
62 query.addQueryItem( QStringLiteral(
"lon" ), QString::number(
static_cast< int >( feedLong ) ) );
72 if ( feedUrl.isLocalFile() )
74 if ( !query.toString().isEmpty() )
75 mFeedUrl = QUrl( mFeedUrl.toString() +
'_' + query.toString() );
79 mFeedUrl.setQuery( query );
91 const int beforeSize = mEntries.size();
92 mEntries.erase( std::remove_if( mEntries.begin(), mEntries.end(),
93 [key, &dismissed](
const Entry & entry )
95 if ( entry.key == key )
101 } ), mEntries.end() );
102 if ( beforeSize == mEntries.size() )
108 if ( !dismissed.imageUrl.isEmpty() )
111 const QString imagePath = QStringLiteral(
"%1/%2.png" ).arg( previewDir ).arg( key );
112 if ( QFile::exists( imagePath ) )
114 QFile::remove( imagePath );
118 if ( !mBlockSignals )
119 emit entryDismissed( dismissed );
124 const QList< QgsNewsFeedParser::Entry >
entries = mEntries;
138 QNetworkRequest req( mFeedUrl );
141 mFetchStartTime = QDateTime::currentDateTimeUtc().toSecsSinceEpoch();
148 QNetworkReply *reply = task->
reply();
155 if ( reply->error() != QNetworkReply::NoError )
157 QgsMessageLog::logMessage( tr(
"News feed request failed [error: %1]" ).arg( reply->errorString() ) );
162 QMetaObject::invokeMethod(
this,
"onFetch", Qt::QueuedConnection, Q_ARG( QString, task->
contentAsString() ) );
168 void QgsNewsFeedParser::onFetch(
const QString &content )
174 const QVariantList
entries = json.toList();
175 QList< QgsNewsFeedParser::Entry > newEntries;
176 newEntries.reserve(
entries.size() );
177 for (
const QVariant &e :
entries )
180 const QVariantMap entryMap = e.toMap();
181 newEntry.key = entryMap.value( QStringLiteral(
"pk" ) ).toInt();
182 newEntry.title = entryMap.value( QStringLiteral(
"title" ) ).toString();
183 newEntry.imageUrl = entryMap.value( QStringLiteral(
"image" ) ).toString();
184 newEntry.content = entryMap.value( QStringLiteral(
"content" ) ).toString();
185 newEntry.link = entryMap.value( QStringLiteral(
"url" ) ).toString();
186 newEntry.sticky = entryMap.value( QStringLiteral(
"sticky" ) ).toBool();
188 const uint expiry = entryMap.value( QStringLiteral(
"publish_to" ) ).toUInt( &ok );
190 newEntry.expiry.setSecsSinceEpoch( expiry );
191 newEntries.append( newEntry );
193 if ( !newEntry.imageUrl.isEmpty() )
194 fetchImageForEntry( newEntry );
196 mEntries.append( newEntry );
197 storeEntryInSettings( newEntry );
204 void QgsNewsFeedParser::readStoredEntries()
210 std::sort( existing.begin(), existing.end(), [](
const QString & a,
const QString & b )
212 return a.toInt() < b.toInt();
214 mEntries.reserve( existing.size() );
215 for (
const QString &entry : existing )
217 const Entry e = readEntryFromSettings( entry.toInt() );
218 if ( !e.expiry.isValid() || e.expiry > QDateTime::currentDateTime() )
219 mEntries.append( e );
223 mBlockSignals =
true;
225 mBlockSignals =
false;
232 const QString baseSettingsKey = QStringLiteral(
"%1/%2" ).arg( mSettingsKey ).arg( key );
237 entry.title = settings.
value( QStringLiteral(
"title" ) ).toString();
238 entry.imageUrl = settings.
value( QStringLiteral(
"imageUrl" ) ).toString();
239 entry.content = settings.
value( QStringLiteral(
"content" ) ).toString();
240 entry.link = settings.
value( QStringLiteral(
"link" ) ).toString();
241 entry.sticky = settings.
value( QStringLiteral(
"sticky" ) ).toBool();
242 entry.expiry = settings.
value( QStringLiteral(
"expiry" ) ).toDateTime();
243 if ( !entry.imageUrl.isEmpty() )
246 const QString imagePath = QStringLiteral(
"%1/%2.png" ).arg( previewDir ).arg( entry.key );
247 if ( QFile::exists( imagePath ) )
249 const QImage img( imagePath );
250 entry.image = QPixmap::fromImage( img );
254 fetchImageForEntry( entry );
262 const QString baseSettingsKey = QStringLiteral(
"%1/%2" ).arg( mSettingsKey ).arg( entry.
key );
269 if ( entry.
expiry.isValid() )
279 auto findIter = std::find_if( mEntries.begin(), mEntries.end(), [entry](
const QgsNewsFeedParser::Entry & candidate )
281 return candidate.key == entry.key;
283 if ( findIter != mEntries.end() )
285 const int entryIndex =
static_cast< int >( std::distance( mEntries.begin(), findIter ) );
287 QImage img = QImage::fromData( fetcher->
reply()->readAll() );
289 QSize size = img.size();
291 if ( size.width() > 250 )
293 size.setHeight(
static_cast< int >( size.height() *
static_cast< double >( 250 ) / size.width() ) );
294 size.setWidth( 250 );
297 if ( size.height() > 177 )
299 size.setWidth(
static_cast< int >( size.width() *
static_cast< double >( 177 ) / size.height() ) );
300 size.setHeight( 177 );
304 img = img.scaled( size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation );
307 QImage previewImage( size, QImage::Format_ARGB32 );
308 previewImage.fill( Qt::transparent );
309 QPainter previewPainter( &previewImage );
310 previewPainter.setRenderHint( QPainter::Antialiasing,
true );
311 previewPainter.setRenderHint( QPainter::SmoothPixmapTransform,
true );
312 previewPainter.setPen( Qt::NoPen );
313 previewPainter.setBrush( Qt::black );
314 previewPainter.drawRoundedRect( 0, 0, size.width(), size.height(), 8, 8 );
315 previewPainter.setCompositionMode( QPainter::CompositionMode_SourceIn );
316 previewPainter.drawImage( 0, 0, img );
317 previewPainter.end();
321 QDir().mkdir( previewDir );
322 const QString imagePath = QStringLiteral(
"%1/%2.png" ).arg( previewDir ).arg( entry.
key );
323 previewImage.save( imagePath );
325 mEntries[ entryIndex ].image = QPixmap::fromImage( previewImage );
328 fetcher->deleteLater();
335 static QRegularExpression sRegexp( QStringLiteral(
"[^a-zA-Z0-9]" ) );
336 QString res = baseUrl;
337 res = res.replace( sRegexp, QString() );
338 return QStringLiteral(
"NewsFeed/%1" ).arg( res );