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