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