MythTV master
rsseditor.cpp
Go to the documentation of this file.
1// Qt
2#include <QChar> // Fix Qt6 GCC SFINAE warning
3#include <QDateTime>
4#include <QDomDocument>
5#include <QImageReader>
6
7// MythTV headers
25
26// RSS headers
27#include "rsseditor.h"
28
29#define LOC QString("RSSEditor: ")
30#define LOC_WARN QString("RSSEditor, Warning: ")
31#define LOC_ERR QString("RSSEditor, Error: ")
32
33namespace
34{
35 const QString CEID_NEWIMAGE = "image";
36
38 {
39 QStringList ret;
40
41 QList<QByteArray> exts = QImageReader::supportedImageFormats();
42 for (const auto & ext : std::as_const(exts))
43 ret.append(QString("*.").append(ext));
44
45 return ret;
46 }
47}
48
50{
51 if (m_manager)
52 {
53 m_manager->disconnect();
54 m_manager->deleteLater();
55 m_manager = nullptr;
56 }
57}
58
60{
61 // Load the theme for this screen
62 bool foundtheme =
63 LoadWindowFromXML("netvision-ui.xml", "rsseditpopup", this);
64
65 if (!foundtheme)
66 return false;
67
68 bool err = false;
69 UIUtilE::Assign(this, m_urlEdit, "url", &err);
70 UIUtilE::Assign(this, m_titleEdit, "title", &err);
71 UIUtilE::Assign(this, m_descEdit, "description", &err);
72 UIUtilE::Assign(this, m_authorEdit, "author", &err);
73 UIUtilE::Assign(this, m_download, "download", &err);
74 UIUtilE::Assign(this, m_okButton, "ok", &err);
75 UIUtilE::Assign(this, m_cancelButton, "cancel", &err);
76 UIUtilE::Assign(this, m_thumbButton, "preview_browse", &err);
77 UIUtilE::Assign(this, m_thumbImage, "preview", &err);
78
79 if (err)
80 {
81 LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'rsseditpopup'");
82 return false;
83 }
84
88
93
94 if (m_editing)
95 {
97
102
103 QString thumb = m_site->GetImage();
104 if (!thumb.isEmpty())
105 {
108 }
109
110 if (m_site->GetDownload())
112 }
113
115
116 return true;
117}
118
119bool RSSEditPopup::keyPressEvent(QKeyEvent *event)
120{
121 if (GetFocusWidget()->keyPressEvent(event))
122 return true;
123
124 QStringList actions;
125 bool handled = GetMythMainWindow()->TranslateKeyPress("Internet Video",
126 event, actions);
127
128 if (!handled && MythScreenType::keyPressEvent(event))
129 handled = true;
130
131 return handled;
132}
133
135{
136 if (m_editing)
137 {
138 QString title = m_titleEdit->GetText();
139 QString desc = m_descEdit->GetText();
140 QString author = m_authorEdit->GetText();
141 QString link = m_urlEdit->GetText();
142 QString filename = m_thumbImage->GetFilename();
143 bool download = m_download->GetCheckState() == MythUIStateType::Full;
144
146
147 std::shared_ptr<MythSortHelper>sh = getMythSortHelper();
148 RSSSite site(title, sh->doTitle(title), filename, VIDEO_PODCAST,
149 desc, link, author, download, MythDate::current());
150 if (insertInDB(&site))
151 emit Saving();
152 Close();
153 }
154 else
155 {
156 m_manager = new QNetworkAccessManager();
157 QUrl qurl(m_urlEdit->GetText());
158
159 m_reply = m_manager->get(QNetworkRequest(qurl));
160
161 connect(m_manager, &QNetworkAccessManager::finished, this,
163 }
164}
165
166QUrl RSSEditPopup::redirectUrl(const QUrl& possibleRedirectUrl,
167 const QUrl& oldRedirectUrl)
168{
169 QUrl redirectUrl;
170 if(!possibleRedirectUrl.isEmpty() && possibleRedirectUrl != oldRedirectUrl)
171 redirectUrl = possibleRedirectUrl;
172 return redirectUrl;
173}
174
175void RSSEditPopup::SlotCheckRedirect(QNetworkReply* reply)
176{
177 QVariant possibleRedirectUrl =
178 reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
179
180 QUrl urlRedirectedTo;
181 urlRedirectedTo = redirectUrl(
182 possibleRedirectUrl.toUrl(), urlRedirectedTo);
183
184 if (!urlRedirectedTo.isEmpty())
185 {
186 m_urlEdit->SetText(urlRedirectedTo.toString());
187 m_manager->get(QNetworkRequest(urlRedirectedTo));
188 }
189 else
190 {
191// urlRedirectedTo.clear();
192 SlotSave(reply);
193 }
194 reply->deleteLater();
195}
196
197void RSSEditPopup::SlotSave(QNetworkReply* reply)
198{
199 QDomDocument document;
200#if QT_VERSION < QT_VERSION_CHECK(6,5,0)
201 document.setContent(reply->read(reply->bytesAvailable()), true);
202#else
203 document.setContent(reply->read(reply->bytesAvailable()),
204 QDomDocument::ParseOption::UseNamespaceProcessing);
205#endif
206
207 QString text = document.toString();
208
209 QString title = m_titleEdit->GetText();
210 QString description = m_descEdit->GetText();
211 QString author = m_authorEdit->GetText();
212 QString file = m_thumbImage->GetFilename();
213
214 LOG(VB_GENERAL, LOG_DEBUG, QString("Text to Parse: %1").arg(text));
215
216 QDomElement root = document.documentElement();
217 QDomElement channel = root.firstChildElement ("channel");
218 if (!channel.isNull())
219 {
221 if (title.isEmpty())
222 title = channel.firstChildElement("title").text().trimmed();
223 if (description.isEmpty())
224 description = channel.firstChildElement("description").text();
225 if (author.isEmpty())
226 author = Parse::GetAuthor(channel);
227 if (author.isEmpty())
228 author = channel.firstChildElement("managingEditor").text();
229 if (author.isEmpty())
230 author = channel.firstChildElement("webMaster").text();
231
232 QString thumbnailURL =
233 channel.firstChildElement("image").attribute("url");
234 if (thumbnailURL.isEmpty())
235 {
236 QDomElement thumbElem = channel.firstChildElement("image");
237 if (!thumbElem.isNull())
238 thumbnailURL = thumbElem.firstChildElement("url").text();
239 }
240 if (thumbnailURL.isEmpty())
241 {
242 QDomNodeList nodes = channel.elementsByTagNameNS(
243 "http://www.itunes.com/dtds/podcast-1.0.dtd",
244 "image");
245 if (nodes.size())
246 {
247 thumbnailURL =
248 nodes.at(0).toElement().attributeNode("href").value();
249 if (thumbnailURL.isEmpty())
250 thumbnailURL = nodes.at(0).toElement().text();
251 }
252 }
253
254 bool download = m_download->GetCheckState() == MythUIStateType::Full;
255 QString filename("");
256
257 if (!file.isEmpty())
258 filename = file;
259 else if (!thumbnailURL.isEmpty())
260 filename = thumbnailURL;
261
262 QString link = m_urlEdit->GetText();
263
264 std::shared_ptr<MythSortHelper>sh = getMythSortHelper();
265 RSSSite site(title, sh->doTitle(title), filename, VIDEO_PODCAST, description, link,
266 author, download, MythDate::current());
267 if (insertInDB(&site))
268 emit Saving();
269 }
270
271 Close();
272}
273
275{
276 SelectImagePopup(GetConfDir() + "/MythNetvision" + "/sitecovers", *this,
278}
279
281 QObject &inst, const QString &returnEvent)
282{
283 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
284
285 auto *fb = new MythUIFileBrowser(popupStack, prefix);
286 fb->SetNameFilter(GetSupportedImageExtensionFilter());
287 if (fb->Create())
288 {
289 fb->SetReturnEvent(&inst, returnEvent);
290 popupStack->AddScreen(fb);
291 }
292 else
293 {
294 delete fb;
295 }
296}
297
298void RSSEditPopup::customEvent(QEvent *levent)
299{
300 if (levent->type() == DialogCompletionEvent::kEventType)
301 {
302 auto *dce = dynamic_cast<DialogCompletionEvent*>(levent);
303 if ((dce != nullptr) && (dce->GetId() == CEID_NEWIMAGE))
304 {
305 m_thumbImage->SetFilename(dce->GetResultText());
308 }
309 }
310}
311
313{
314 QMutexLocker locker(&m_lock);
315
316 if (m_changed)
317 emit ItemsChanged();
318}
319
321{
322 QMutexLocker locker(&m_lock);
323
324 // Load the theme for this screen
325 bool foundtheme = LoadWindowFromXML("netvision-ui.xml", "rsseditor", this);
326
327 if (!foundtheme)
328 return false;
329
330 bool err = false;
331 UIUtilE::Assign(this, m_sites, "sites", &err);
332 UIUtilE::Assign(this, m_new, "new", &err);
333 UIUtilE::Assign(this, m_delete, "delete", &err);
334 UIUtilE::Assign(this, m_edit, "edit", &err);
335
336 UIUtilW::Assign(this, m_image, "preview");
337 UIUtilW::Assign(this, m_title, "title");
338 UIUtilW::Assign(this, m_desc, "description");
339 UIUtilW::Assign(this, m_url, "url");
340 UIUtilW::Assign(this, m_author, "author");
341
342 if (err)
343 {
344 LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'rsseditor'");
345 return false;
346 }
347
350
357
360
362
363 LoadData();
364
365 if (m_sites->GetCount() == 0)
367 else
369
370 return true;
371}
372
374{
375 qDeleteAll(m_siteList);
378 if (m_sites->GetCount() == 0)
379 {
380 m_edit->SetVisible(false);
381 m_delete->SetVisible(false);
382 m_sites->SetVisible(false);
383 }
384 else
385 {
386 m_edit->SetVisible(true);
387 m_delete->SetVisible(true);
388 m_sites->SetVisible(true);
389 }
390}
391
392bool RSSEditor::keyPressEvent(QKeyEvent *event)
393{
394 if (GetFocusWidget()->keyPressEvent(event))
395 return true;
396
397 QStringList actions;
398 bool handled = GetMythMainWindow()->TranslateKeyPress("Internet Video",
399 event, actions);
400
401 for (int i = 0; i < actions.size() && !handled; i++)
402 {
403 const QString& action = actions[i];
404 handled = true;
405
406 if (action == "DELETE" && GetFocusWidget() == m_sites)
407 {
409 }
410 else if (action == "EDIT" && GetFocusWidget() == m_sites)
411 {
412 SlotEditSite();
413 }
414 else
415 {
416 handled = false;
417 }
418 }
419
420
421 if (!handled && MythScreenType::keyPressEvent(event))
422 handled = true;
423
424 return handled;
425}
426
428{
429 QMutexLocker locker(&m_lock);
430
431 m_sites->Reset();
432
433 for (const auto & site : std::as_const(m_siteList))
434 {
435 auto *item = new MythUIButtonListItem(m_sites, site->GetTitle());
436 item->SetText(site->GetTitle(), "title");
437 item->SetText(site->GetDescription(), "description");
438 item->SetText(site->GetURL(), "url");
439 item->SetText(site->GetAuthor(), "author");
440 item->SetData(QVariant::fromValue(site));
441 item->SetImage(site->GetImage());
442 }
443}
444
446{
447 auto *site = m_sites->GetItemCurrent()->GetData().value<RSSSite*>();
448 if (site)
449 {
450 if (m_image)
451 {
452 const QString& thumb = site->GetImage();
453
454 m_image->Reset();
455
456 if (!thumb.isEmpty())
457 {
458 m_image->SetFilename(thumb);
459 m_image->Load();
460 }
461 }
462 if (m_title)
463 m_title->SetText(site->GetTitle());
464 if (m_desc)
465 m_desc->SetText(site->GetDescription());
466 if (m_url)
467 m_url->SetText(site->GetURL());
468 if (m_author)
469 m_author->SetText(site->GetAuthor());
470 }
471}
472
474{
475 QMutexLocker locker(&m_lock);
476
477 QString message =
478 tr("Are you sure you want to unsubscribe from this feed?");
479
480 MythScreenStack *m_popupStack =
481 GetMythMainWindow()->GetStack("popup stack");
482
483 auto *confirmdialog = new MythConfirmationDialog(m_popupStack,message);
484
485 if (confirmdialog->Create())
486 {
487 m_popupStack->AddScreen(confirmdialog);
488
489 connect(confirmdialog, &MythConfirmationDialog::haveResult,
491 }
492 else
493 {
494 delete confirmdialog;
495 }
496}
497
499{
500 QMutexLocker locker(&m_lock);
501
503
504 auto *site = m_sites->GetItemCurrent()->GetData().value<RSSSite*>();
505 if (site)
506 {
507 auto *rsseditpopup =
508 new RSSEditPopup(site->GetURL(), true, mainStack, "rsseditpopup");
509
510 if (rsseditpopup->Create())
511 {
512 connect(rsseditpopup, &RSSEditPopup::Saving, this, &RSSEditor::ListChanged);
513
514 mainStack->AddScreen(rsseditpopup);
515 }
516 else
517 {
518 delete rsseditpopup;
519 }
520 }
521}
522
524{
525 QMutexLocker locker(&m_lock);
526
528
529 auto *rsseditpopup = new RSSEditPopup("", false, mainStack, "rsseditpopup");
530
531 if (rsseditpopup->Create())
532 {
533 connect(rsseditpopup, &RSSEditPopup::Saving, this, &RSSEditor::ListChanged);
534
535 mainStack->AddScreen(rsseditpopup);
536 }
537 else
538 {
539 delete rsseditpopup;
540 }
541}
542
543void RSSEditor::DoDeleteSite(bool remove)
544{
545 QMutexLocker locker(&m_lock);
546
547 if (!remove)
548 return;
549
550 auto *site = m_sites->GetItemCurrent()->GetData().value<RSSSite*>();
551
552 if (removeFromDB(site))
553 ListChanged();
554}
555
557{
558 m_changed = true;
559 LoadData();
560}
561
562#include "moc_rsseditor.cpp"
Event dispatched from MythUI modal dialogs to a listening class containing a result of some form.
Definition: mythdialogbox.h:40
static const Type kEventType
Definition: mythdialogbox.h:55
Dialog asking for user confirmation.
MythScreenStack * GetMainStack()
bool TranslateKeyPress(const QString &Context, QKeyEvent *Event, QStringList &Actions, bool AllowJumps=true)
Get a list of actions for a keypress in the given context.
MythScreenStack * GetStack(const QString &Stackname)
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
void BuildFocusList(void)
MythUIType * GetFocusWidget(void) const
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
bool SetFocusWidget(MythUIType *widget=nullptr)
virtual void Close()
MythUIButtonListItem * GetItemCurrent() const
void Reset() override
Reset the widget to it's original state, should not reset changes made by the theme.
void itemClicked(MythUIButtonListItem *item)
void itemSelected(MythUIButtonListItem *item)
void Clicked()
void SetCheckState(MythUIStateType::StateType state)
MythUIStateType::StateType GetCheckState() const
bool Load(bool allowLoadInBackground=true, bool forceStat=false)
Load the image(s), wraps ImageLoader::LoadImage()
void SetFilename(const QString &filename)
Must be followed by a call to Load() to load the image.
QString GetFilename(void) const
Definition: mythuiimage.h:109
void Reset(void) override
Reset the image back to the default defined in the theme.
QString GetText(void) const
void SetText(const QString &text, bool moveCursor=true)
void SetMaxLength(int length)
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:115
virtual void SetVisible(bool visible)
void Show(void)
static QString GetAuthor(const QDomElement &parent)
Definition: rssparse.cpp:975
Site name, URL and icon edit screen.
Definition: rsseditor.h:27
void ParseAndSave(void)
Definition: rsseditor.cpp:134
static QUrl redirectUrl(const QUrl &possibleRedirectUrl, const QUrl &oldRedirectUrl)
Definition: rsseditor.cpp:166
RSSSite * m_site
Definition: rsseditor.h:51
MythUIButton * m_thumbButton
Definition: rsseditor.h:56
QNetworkReply * m_reply
Definition: rsseditor.h:68
QNetworkAccessManager * m_manager
Definition: rsseditor.h:67
~RSSEditPopup() override
Definition: rsseditor.cpp:49
bool Create(void) override
Definition: rsseditor.cpp:59
MythUIButton * m_cancelButton
Definition: rsseditor.h:63
void customEvent(QEvent *levent) override
Definition: rsseditor.cpp:298
QString m_urlText
Definition: rsseditor.h:52
void Saving(void)
MythUITextEdit * m_descEdit
Definition: rsseditor.h:59
MythUICheckBox * m_download
Definition: rsseditor.h:65
static void SelectImagePopup(const QString &prefix, QObject &inst, const QString &returnEvent)
Definition: rsseditor.cpp:280
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
Definition: rsseditor.cpp:119
bool m_editing
Definition: rsseditor.h:53
void SlotCheckRedirect(QNetworkReply *reply)
Definition: rsseditor.cpp:175
void SlotSave(QNetworkReply *reply)
Definition: rsseditor.cpp:197
MythUIImage * m_thumbImage
Definition: rsseditor.h:55
MythUIButton * m_okButton
Definition: rsseditor.h:62
void DoFileBrowser(void)
Definition: rsseditor.cpp:274
MythUITextEdit * m_urlEdit
Definition: rsseditor.h:57
MythUITextEdit * m_titleEdit
Definition: rsseditor.h:58
MythUITextEdit * m_authorEdit
Definition: rsseditor.h:60
void LoadData(void)
Definition: rsseditor.cpp:373
RSSSite::rssList m_siteList
Definition: rsseditor.h:103
bool Create(void) override
Definition: rsseditor.cpp:320
MythUIImage * m_image
Definition: rsseditor.h:109
void fillRSSButtonList()
Definition: rsseditor.cpp:427
MythUIButtonList * m_sites
Definition: rsseditor.h:104
MythUIButton * m_new
Definition: rsseditor.h:105
void SlotItemChanged()
Definition: rsseditor.cpp:445
MythUIButton * m_edit
Definition: rsseditor.h:107
QRecursiveMutex m_lock
Definition: rsseditor.h:100
bool m_changed
Definition: rsseditor.h:101
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
Definition: rsseditor.cpp:392
MythUIText * m_desc
Definition: rsseditor.h:112
MythUIButton * m_delete
Definition: rsseditor.h:106
void SlotEditSite(void)
Definition: rsseditor.cpp:498
void SlotDeleteSite(void)
Definition: rsseditor.cpp:473
MythUIText * m_author
Definition: rsseditor.h:113
void ItemsChanged(void)
void DoDeleteSite(bool remove)
Definition: rsseditor.cpp:543
MythUIText * m_url
Definition: rsseditor.h:111
~RSSEditor() override
Definition: rsseditor.cpp:312
MythUIText * m_title
Definition: rsseditor.h:110
void SlotNewSite(void)
Definition: rsseditor.cpp:523
void ListChanged(void)
Definition: rsseditor.cpp:556
const QString & GetImage() const
Definition: rssmanager.h:57
const QString & GetAuthor() const
Definition: rssmanager.h:61
const QString & GetTitle() const
Definition: rssmanager.h:55
const bool & GetDownload() const
Definition: rssmanager.h:62
const QString & GetDescription() const
Definition: rssmanager.h:59
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
QString GetConfDir(void)
Definition: mythdirs.cpp:282
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythMainWindow * GetMythMainWindow(void)
std::shared_ptr< MythSortHelper > getMythSortHelper(void)
Get a pointer to the MythSortHelper singleton.
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
QStringList GetSupportedImageExtensionFilter()
Definition: rsseditor.cpp:37
bool removeFromDB(RSSSite *site)
Definition: netutils.cpp:686
RSSSite * findByURL(const QString &url, ArticleType type)
Definition: netutils.cpp:542
RSSSite::rssList findAllDBRSS()
Definition: netutils.cpp:610
bool insertInDB(RSSSite *site)
Definition: netutils.cpp:643
@ VIDEO_PODCAST
Definition: rssparse.h:22
static bool Assign(ContainerType *container, UIType *&item, const QString &name, bool *err=nullptr)
Definition: mythuiutils.h:27