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 ret.reserve(exts.size());
43 for (const auto & ext : std::as_const(exts))
44 ret.append(QString("*.").append(ext));
45
46 return ret;
47 }
48}
49
51{
52 if (m_manager)
53 {
54 m_manager->disconnect();
55 m_manager->deleteLater();
56 m_manager = nullptr;
57 }
58}
59
61{
62 // Load the theme for this screen
63 bool foundtheme =
64 LoadWindowFromXML("netvision-ui.xml", "rsseditpopup", this);
65
66 if (!foundtheme)
67 return false;
68
69 bool err = false;
70 UIUtilE::Assign(this, m_urlEdit, "url", &err);
71 UIUtilE::Assign(this, m_titleEdit, "title", &err);
72 UIUtilE::Assign(this, m_descEdit, "description", &err);
73 UIUtilE::Assign(this, m_authorEdit, "author", &err);
74 UIUtilE::Assign(this, m_download, "download", &err);
75 UIUtilE::Assign(this, m_okButton, "ok", &err);
76 UIUtilE::Assign(this, m_cancelButton, "cancel", &err);
77 UIUtilE::Assign(this, m_thumbButton, "preview_browse", &err);
78 UIUtilE::Assign(this, m_thumbImage, "preview", &err);
79
80 if (err)
81 {
82 LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'rsseditpopup'");
83 return false;
84 }
85
89
94
95 if (m_editing)
96 {
98
103
104 QString thumb = m_site->GetImage();
105 if (!thumb.isEmpty())
106 {
109 }
110
111 if (m_site->GetDownload())
113 }
114
116
117 return true;
118}
119
120bool RSSEditPopup::keyPressEvent(QKeyEvent *event)
121{
122 if (GetFocusWidget()->keyPressEvent(event))
123 return true;
124
125 QStringList actions;
126 bool handled = GetMythMainWindow()->TranslateKeyPress("Internet Video",
127 event, actions);
128
129 if (!handled && MythScreenType::keyPressEvent(event))
130 handled = true;
131
132 return handled;
133}
134
136{
137 if (m_editing)
138 {
139 QString title = m_titleEdit->GetText();
140 QString desc = m_descEdit->GetText();
141 QString author = m_authorEdit->GetText();
142 QString link = m_urlEdit->GetText();
143 QString filename = m_thumbImage->GetFilename();
144 bool download = m_download->GetCheckState() == MythUIStateType::Full;
145
147
148 std::shared_ptr<MythSortHelper>sh = getMythSortHelper();
149 RSSSite site(title, sh->doTitle(title), filename, VIDEO_PODCAST,
150 desc, link, author, download, MythDate::current());
151 if (insertInDB(&site))
152 emit Saving();
153 Close();
154 }
155 else
156 {
157 m_manager = new QNetworkAccessManager();
158 QUrl qurl(m_urlEdit->GetText());
159
160 m_reply = m_manager->get(QNetworkRequest(qurl));
161
162 connect(m_manager, &QNetworkAccessManager::finished, this,
164 }
165}
166
167QUrl RSSEditPopup::redirectUrl(const QUrl& possibleRedirectUrl,
168 const QUrl& oldRedirectUrl)
169{
170 QUrl redirectUrl;
171 if(!possibleRedirectUrl.isEmpty() && possibleRedirectUrl != oldRedirectUrl)
172 redirectUrl = possibleRedirectUrl;
173 return redirectUrl;
174}
175
176void RSSEditPopup::SlotCheckRedirect(QNetworkReply* reply)
177{
178 QVariant possibleRedirectUrl =
179 reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
180
181 QUrl urlRedirectedTo;
182 urlRedirectedTo = redirectUrl(
183 possibleRedirectUrl.toUrl(), urlRedirectedTo);
184
185 if (!urlRedirectedTo.isEmpty())
186 {
187 m_urlEdit->SetText(urlRedirectedTo.toString());
188 m_manager->get(QNetworkRequest(urlRedirectedTo));
189 }
190 else
191 {
192// urlRedirectedTo.clear();
193 SlotSave(reply);
194 }
195 reply->deleteLater();
196}
197
198void RSSEditPopup::SlotSave(QNetworkReply* reply)
199{
200 QDomDocument document;
201#if QT_VERSION < QT_VERSION_CHECK(6,5,0)
202 document.setContent(reply->read(reply->bytesAvailable()), true);
203#else
204 document.setContent(reply->read(reply->bytesAvailable()),
205 QDomDocument::ParseOption::UseNamespaceProcessing);
206#endif
207
208 QString text = document.toString();
209
210 QString title = m_titleEdit->GetText();
211 QString description = m_descEdit->GetText();
212 QString author = m_authorEdit->GetText();
213 QString file = m_thumbImage->GetFilename();
214
215 LOG(VB_GENERAL, LOG_DEBUG, QString("Text to Parse: %1").arg(text));
216
217 QDomElement root = document.documentElement();
218 QDomElement channel = root.firstChildElement ("channel");
219 if (!channel.isNull())
220 {
222 if (title.isEmpty())
223 title = channel.firstChildElement("title").text().trimmed();
224 if (description.isEmpty())
225 description = channel.firstChildElement("description").text();
226 if (author.isEmpty())
227 author = Parse::GetAuthor(channel);
228 if (author.isEmpty())
229 author = channel.firstChildElement("managingEditor").text();
230 if (author.isEmpty())
231 author = channel.firstChildElement("webMaster").text();
232
233 QString thumbnailURL =
234 channel.firstChildElement("image").attribute("url");
235 if (thumbnailURL.isEmpty())
236 {
237 QDomElement thumbElem = channel.firstChildElement("image");
238 if (!thumbElem.isNull())
239 thumbnailURL = thumbElem.firstChildElement("url").text();
240 }
241 if (thumbnailURL.isEmpty())
242 {
243 QDomNodeList nodes = channel.elementsByTagNameNS(
244 "http://www.itunes.com/dtds/podcast-1.0.dtd",
245 "image");
246 if (nodes.size())
247 {
248 thumbnailURL =
249 nodes.at(0).toElement().attributeNode("href").value();
250 if (thumbnailURL.isEmpty())
251 thumbnailURL = nodes.at(0).toElement().text();
252 }
253 }
254
255 bool download = m_download->GetCheckState() == MythUIStateType::Full;
256 QString filename("");
257
258 if (!file.isEmpty())
259 filename = file;
260 else if (!thumbnailURL.isEmpty())
261 filename = thumbnailURL;
262
263 QString link = m_urlEdit->GetText();
264
265 std::shared_ptr<MythSortHelper>sh = getMythSortHelper();
266 RSSSite site(title, sh->doTitle(title), filename, VIDEO_PODCAST, description, link,
267 author, download, MythDate::current());
268 if (insertInDB(&site))
269 emit Saving();
270 }
271
272 Close();
273}
274
276{
277 SelectImagePopup(GetConfDir() + "/MythNetvision" + "/sitecovers", *this,
279}
280
282 QObject &inst, const QString &returnEvent)
283{
284 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
285
286 auto *fb = new MythUIFileBrowser(popupStack, prefix);
287 fb->SetNameFilter(GetSupportedImageExtensionFilter());
288 if (fb->Create())
289 {
290 fb->SetReturnEvent(&inst, returnEvent);
291 popupStack->AddScreen(fb);
292 }
293 else
294 {
295 delete fb;
296 }
297}
298
299void RSSEditPopup::customEvent(QEvent *levent)
300{
301 if (levent->type() == DialogCompletionEvent::kEventType)
302 {
303 auto *dce = dynamic_cast<DialogCompletionEvent*>(levent);
304 if ((dce != nullptr) && (dce->GetId() == CEID_NEWIMAGE))
305 {
306 m_thumbImage->SetFilename(dce->GetResultText());
309 }
310 }
311}
312
314{
315 QMutexLocker locker(&m_lock);
316
317 if (m_changed)
318 emit ItemsChanged();
319}
320
322{
323 QMutexLocker locker(&m_lock);
324
325 // Load the theme for this screen
326 bool foundtheme = LoadWindowFromXML("netvision-ui.xml", "rsseditor", this);
327
328 if (!foundtheme)
329 return false;
330
331 bool err = false;
332 UIUtilE::Assign(this, m_sites, "sites", &err);
333 UIUtilE::Assign(this, m_new, "new", &err);
334 UIUtilE::Assign(this, m_delete, "delete", &err);
335 UIUtilE::Assign(this, m_edit, "edit", &err);
336
337 UIUtilW::Assign(this, m_image, "preview");
338 UIUtilW::Assign(this, m_title, "title");
339 UIUtilW::Assign(this, m_desc, "description");
340 UIUtilW::Assign(this, m_url, "url");
341 UIUtilW::Assign(this, m_author, "author");
342
343 if (err)
344 {
345 LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'rsseditor'");
346 return false;
347 }
348
351
358
361
363
364 LoadData();
365
366 if (m_sites->GetCount() == 0)
368 else
370
371 return true;
372}
373
375{
376 qDeleteAll(m_siteList);
379 if (m_sites->GetCount() == 0)
380 {
381 m_edit->SetVisible(false);
382 m_delete->SetVisible(false);
383 m_sites->SetVisible(false);
384 }
385 else
386 {
387 m_edit->SetVisible(true);
388 m_delete->SetVisible(true);
389 m_sites->SetVisible(true);
390 }
391}
392
393bool RSSEditor::keyPressEvent(QKeyEvent *event)
394{
395 if (GetFocusWidget()->keyPressEvent(event))
396 return true;
397
398 QStringList actions;
399 bool handled = GetMythMainWindow()->TranslateKeyPress("Internet Video",
400 event, actions);
401
402 for (int i = 0; i < actions.size() && !handled; i++)
403 {
404 const QString& action = actions[i];
405 handled = true;
406
407 if (action == "DELETE" && GetFocusWidget() == m_sites)
408 {
410 }
411 else if (action == "EDIT" && GetFocusWidget() == m_sites)
412 {
413 SlotEditSite();
414 }
415 else
416 {
417 handled = false;
418 }
419 }
420
421
422 if (!handled && MythScreenType::keyPressEvent(event))
423 handled = true;
424
425 return handled;
426}
427
429{
430 QMutexLocker locker(&m_lock);
431
432 m_sites->Reset();
433
434 for (const auto & site : std::as_const(m_siteList))
435 {
436 auto *item = new MythUIButtonListItem(m_sites, site->GetTitle());
437 item->SetText(site->GetTitle(), "title");
438 item->SetText(site->GetDescription(), "description");
439 item->SetText(site->GetURL(), "url");
440 item->SetText(site->GetAuthor(), "author");
441 item->SetData(QVariant::fromValue(site));
442 item->SetImage(site->GetImage());
443 }
444}
445
447{
448 auto *site = m_sites->GetItemCurrent()->GetData().value<RSSSite*>();
449 if (site)
450 {
451 if (m_image)
452 {
453 const QString& thumb = site->GetImage();
454
455 m_image->Reset();
456
457 if (!thumb.isEmpty())
458 {
459 m_image->SetFilename(thumb);
460 m_image->Load();
461 }
462 }
463 if (m_title)
464 m_title->SetText(site->GetTitle());
465 if (m_desc)
466 m_desc->SetText(site->GetDescription());
467 if (m_url)
468 m_url->SetText(site->GetURL());
469 if (m_author)
470 m_author->SetText(site->GetAuthor());
471 }
472}
473
475{
476 QMutexLocker locker(&m_lock);
477
478 QString message =
479 tr("Are you sure you want to unsubscribe from this feed?");
480
481 MythScreenStack *m_popupStack =
482 GetMythMainWindow()->GetStack("popup stack");
483
484 auto *confirmdialog = new MythConfirmationDialog(m_popupStack,message);
485
486 if (confirmdialog->Create())
487 {
488 m_popupStack->AddScreen(confirmdialog);
489
490 connect(confirmdialog, &MythConfirmationDialog::haveResult,
492 }
493 else
494 {
495 delete confirmdialog;
496 }
497}
498
500{
501 QMutexLocker locker(&m_lock);
502
504
505 auto *site = m_sites->GetItemCurrent()->GetData().value<RSSSite*>();
506 if (site)
507 {
508 auto *rsseditpopup =
509 new RSSEditPopup(site->GetURL(), true, mainStack, "rsseditpopup");
510
511 if (rsseditpopup->Create())
512 {
513 connect(rsseditpopup, &RSSEditPopup::Saving, this, &RSSEditor::ListChanged);
514
515 mainStack->AddScreen(rsseditpopup);
516 }
517 else
518 {
519 delete rsseditpopup;
520 }
521 }
522}
523
525{
526 QMutexLocker locker(&m_lock);
527
529
530 auto *rsseditpopup = new RSSEditPopup("", false, mainStack, "rsseditpopup");
531
532 if (rsseditpopup->Create())
533 {
534 connect(rsseditpopup, &RSSEditPopup::Saving, this, &RSSEditor::ListChanged);
535
536 mainStack->AddScreen(rsseditpopup);
537 }
538 else
539 {
540 delete rsseditpopup;
541 }
542}
543
544void RSSEditor::DoDeleteSite(bool remove)
545{
546 QMutexLocker locker(&m_lock);
547
548 if (!remove)
549 return;
550
551 auto *site = m_sites->GetItemCurrent()->GetData().value<RSSSite*>();
552
553 if (removeFromDB(site))
554 ListChanged();
555}
556
558{
559 m_changed = true;
560 LoadData();
561}
562
563#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:988
Site name, URL and icon edit screen.
Definition: rsseditor.h:27
void ParseAndSave(void)
Definition: rsseditor.cpp:135
static QUrl redirectUrl(const QUrl &possibleRedirectUrl, const QUrl &oldRedirectUrl)
Definition: rsseditor.cpp:167
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:50
bool Create(void) override
Definition: rsseditor.cpp:60
MythUIButton * m_cancelButton
Definition: rsseditor.h:63
void customEvent(QEvent *levent) override
Definition: rsseditor.cpp:299
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:281
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
Definition: rsseditor.cpp:120
bool m_editing
Definition: rsseditor.h:53
void SlotCheckRedirect(QNetworkReply *reply)
Definition: rsseditor.cpp:176
void SlotSave(QNetworkReply *reply)
Definition: rsseditor.cpp:198
MythUIImage * m_thumbImage
Definition: rsseditor.h:55
MythUIButton * m_okButton
Definition: rsseditor.h:62
void DoFileBrowser(void)
Definition: rsseditor.cpp:275
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:374
RSSSite::rssList m_siteList
Definition: rsseditor.h:103
bool Create(void) override
Definition: rsseditor.cpp:321
MythUIImage * m_image
Definition: rsseditor.h:109
void fillRSSButtonList()
Definition: rsseditor.cpp:428
MythUIButtonList * m_sites
Definition: rsseditor.h:104
MythUIButton * m_new
Definition: rsseditor.h:105
void SlotItemChanged()
Definition: rsseditor.cpp:446
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:393
MythUIText * m_desc
Definition: rsseditor.h:112
MythUIButton * m_delete
Definition: rsseditor.h:106
void SlotEditSite(void)
Definition: rsseditor.cpp:499
void SlotDeleteSite(void)
Definition: rsseditor.cpp:474
MythUIText * m_author
Definition: rsseditor.h:113
void ItemsChanged(void)
void DoDeleteSite(bool remove)
Definition: rsseditor.cpp:544
MythUIText * m_url
Definition: rsseditor.h:111
~RSSEditor() override
Definition: rsseditor.cpp:313
MythUIText * m_title
Definition: rsseditor.h:110
void SlotNewSite(void)
Definition: rsseditor.cpp:524
void ListChanged(void)
Definition: rsseditor.cpp:557
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