diff --git a/src/core/databasefactory.h b/src/core/databasefactory.h index db4d754c8..b3efcbab3 100644 --- a/src/core/databasefactory.h +++ b/src/core/databasefactory.h @@ -5,17 +5,6 @@ #include #include -// TODO: přidat podporu pro mysql -// nemužu mit stejny SQL kod pro mysql a sqlite -// ale musim docilit aby oba kody SQL delaly tabulky -// se stejnymi atributy - stejnymi nazvy sloupcu -// PROBLEMY se mysql: nutno pridat AUTO_INCREMENT u int primary keyů -// taky bacha na nazvy sloupců, třeba -// key a read sou klicovy slouva a fejlne to tak -// taky bacha ze typ TEXT nemuze bejt dost dobre -// pouzitej v UNIQUE CHECK, misto toho se da pouzit treba -// VARCHAR (100) - class DatabaseFactory : public QObject { Q_OBJECT diff --git a/src/core/feedsmodelstandardfeed.cpp b/src/core/feedsmodelstandardfeed.cpp index d78430af8..ff720b7e9 100755 --- a/src/core/feedsmodelstandardfeed.cpp +++ b/src/core/feedsmodelstandardfeed.cpp @@ -13,6 +13,11 @@ #include #include +#include +#include +#include +#include + FeedsModelStandardFeed::FeedsModelStandardFeed(FeedsModelRootItem *parent_item) : FeedsModelFeed(parent_item), @@ -44,15 +49,117 @@ FeedsModelStandardFeed *FeedsModelStandardFeed::loadFromRecord(const QSqlRecord return feed; } -FeedsModelStandardFeed *FeedsModelStandardFeed::guessFeed(const QString &url, - const QString &username, - const QString &password) { - // TODO: http://www.google.com/s2/favicons?domain=root.cz - // ZISKAT ikonu (napsat taky aby se dala ikona pro - // dane url ziskavat taky samostatne - // pak ziskat informace o kanalu +QPair FeedsModelStandardFeed::guessFeed(const QString &url, + const QString &username, + const QString &password) { + QPair result; result.first = NULL; - return NULL; + // Try to obtain icon. + QIcon icon_data; + + if (NetworkFactory::downloadIcon(url, + 5000, + icon_data) == QNetworkReply::NoError) { + // Icon for feed was downloaded and is stored now in _icon_data. + result.first = new FeedsModelStandardFeed(); + result.first->setIcon(icon_data); + } + + QByteArray feed_contents; + if ((result.second = NetworkFactory::downloadFeedFile(url, + Settings::instance()->value(APP_CFG_FEEDS, "feed_update_timeout", DOWNLOAD_TIMEOUT).toInt(), + feed_contents, + true, + username, + password)) == QNetworkReply::NoError) { + // Feed XML was obtained, now we need to try to guess + // its encoding before we can read further data. + + QXmlStreamReader xml_stream_reader(feed_contents); + QString xml_schema_encoding; + QString xml_contents_encoded; + + // We have several chances to read the XML version directly + // from XML declaration. + for (int i = 0; i < 2 && !xml_stream_reader.atEnd(); i++) { + if ((xml_schema_encoding = xml_stream_reader.documentEncoding().toString()).isEmpty()) { + xml_stream_reader.readNext(); + } + else { + break; + } + } + + QTextCodec *custom_codec = QTextCodec::codecForName(xml_schema_encoding.toLocal8Bit()); + + if (custom_codec != NULL) { + // Feed encoding was probably guessed. + xml_contents_encoded = custom_codec->toUnicode(feed_contents); + result.first->setEncoding(xml_schema_encoding); + } + else { + // Feed encoding probably not guessed, set it as + // default. + xml_contents_encoded = feed_contents; + result.first->setEncoding(DEFAULT_FEED_ENCODING); + } + + // Feed XML was obtained, guess it now. + QDomDocument xml_document; + + if (!xml_document.setContent(xml_contents_encoded)) { + // XML is invalid, exit. + return result; + } + + QDomElement root_element = xml_document.documentElement(); + QString root_tag_name = root_element.tagName(); + + if (root_tag_name == "rdf:RDF") { + if (result.first == NULL) { + result.first = new FeedsModelStandardFeed(); + } + + // We found RDF feed. + QDomElement channel_element = root_element.namedItem("channel").toElement(); + + result.first->setType(StandardRdf); + result.first->setTitle(channel_element.namedItem("title").toElement().text()); + result.first->setDescription(channel_element.namedItem("description").toElement().text()); + } + else if (root_tag_name == "rss") { + if (result.first == NULL) { + result.first = new FeedsModelStandardFeed(); + } + + // We found RSS 0.91/0.92/0.93/2.0/2.0.1 feed. + QString rss_type = root_element.attribute("version", "2.0"); + + if (rss_type == "0.91" || rss_type == "0.92" || rss_type == "0.93") { + result.first->setType(StandardRss0X); + } + else { + result.first->setType(StandardRss2X); + } + + QDomElement channel_element = root_element.namedItem("channel").toElement(); + + result.first->setTitle(channel_element.namedItem("title").toElement().text()); + result.first->setDescription(channel_element.namedItem("description").toElement().text()); + } + else if (root_tag_name == "feed") { + if (result.first == NULL) { + result.first = new FeedsModelStandardFeed(); + } + + // We found ATOM feed. + result.first->setType(StandardAtom10); + result.first->setTitle(root_element.namedItem("title").toElement().text()); + result.first->setDescription(root_element.namedItem("subtitle").toElement().text()); + } + } + + return result; } QVariant FeedsModelStandardFeed::data(int column, int role) const { diff --git a/src/core/feedsmodelstandardfeed.h b/src/core/feedsmodelstandardfeed.h index 0aade987d..dfca7609e 100644 --- a/src/core/feedsmodelstandardfeed.h +++ b/src/core/feedsmodelstandardfeed.h @@ -5,6 +5,8 @@ #include #include +#include +#include class Message; @@ -83,10 +85,12 @@ class FeedsModelStandardFeed : public FeedsModelFeed { // Tries to guess feed hidden under given URL // and uses given credentials. - // Returns NULL if something failed. - static FeedsModelStandardFeed *guessFeed(const QString &url, - const QString &username, - const QString &password); + // Returns pointer to guessed feed (if at least partially + // guessed) and retrieved error/status code from network layer + // or NULL feed. + static QPair guessFeed(const QString &url, + const QString &username, + const QString &password); protected: // Persistently stores given messages into the database diff --git a/src/core/networkfactory.cpp b/src/core/networkfactory.cpp index 7b456d0cc..74067201b 100644 --- a/src/core/networkfactory.cpp +++ b/src/core/networkfactory.cpp @@ -5,11 +5,31 @@ #include #include +#include NetworkFactory::NetworkFactory() { } +QNetworkReply::NetworkError NetworkFactory::downloadIcon(const QString &url, + int timeout, + QIcon &output) { + QString google_s2_with_url = QString("http://www.google.com/s2/favicons?domain=%1").arg(Qt::escape(url)); + QByteArray icon_data; + + QNetworkReply::NetworkError network_result = downloadFeedFile(google_s2_with_url, + timeout, + icon_data); + + if (network_result == QNetworkReply::NoError) { + QPixmap icon_pixmap; + icon_pixmap.loadFromData(icon_data); + output = QIcon(icon_pixmap); + } + + return network_result; +} + QNetworkReply::NetworkError NetworkFactory::downloadFeedFile(const QString &url, int timeout, QByteArray &output, diff --git a/src/core/networkfactory.h b/src/core/networkfactory.h index e3fcaf648..f2d371233 100644 --- a/src/core/networkfactory.h +++ b/src/core/networkfactory.h @@ -12,6 +12,12 @@ class NetworkFactory { explicit NetworkFactory(); public: + // Performs SYNCHRONOUS download if favicon for the site, + // given URL belongs to. + static QNetworkReply::NetworkError downloadIcon(const QString &url, + int timeout, + QIcon &output); + // Performs SYNCHRONOUS download of file with given URL // and given timeout. static QNetworkReply::NetworkError downloadFeedFile(const QString &url, diff --git a/src/gui/formstandardfeeddetails.cpp b/src/gui/formstandardfeeddetails.cpp index d7a9d1b92..57c4edbfe 100644 --- a/src/gui/formstandardfeeddetails.cpp +++ b/src/gui/formstandardfeeddetails.cpp @@ -16,6 +16,8 @@ #include #include #include +#include +#include FormStandardFeedDetails::FormStandardFeedDetails(FeedsModel *model, QWidget *parent) @@ -240,6 +242,25 @@ void FormStandardFeedDetails::apply() { } } +void FormStandardFeedDetails::guessFeed() { + QPair result = FeedsModelStandardFeed::guessFeed(m_ui->m_txtUrl->lineEdit()->text(), + m_ui->m_txtUsername->lineEdit()->text(), + m_ui->m_txtPassword->lineEdit()->text()); + + if (result.first != NULL) { + // Icon was perhaps guessed. + m_ui->m_btnIcon->setIcon(result.first->icon()); + + m_ui->m_txtTitle->lineEdit()->setText(result.first->title()); + m_ui->m_txtDescription->lineEdit()->setText(result.first->description()); + m_ui->m_cmbType->setCurrentIndex(m_ui->m_cmbType->findData(QVariant::fromValue((int) result.first->type()))); + m_ui->m_cmbEncoding->setCurrentIndex(m_ui->m_cmbEncoding->findData(result.first->encoding(), Qt::DisplayRole)); + } + else { + // No feed guessed, even no icon available. + } +} + void FormStandardFeedDetails::createConnections() { // General connections. connect(m_ui->m_buttonBox, SIGNAL(accepted()), @@ -258,6 +279,8 @@ void FormStandardFeedDetails::createConnections() { this, SLOT(onAuthenticationSwitched())); connect(m_ui->m_cmbAutoUpdateType, SIGNAL(currentIndexChanged(int)), this, SLOT(onAutoUpdateTypeChanged(int))); + connect(m_btnLoadDataFromInternet, SIGNAL(clicked()), + this, SLOT(guessFeed())); // Icon connections. connect(m_actionLoadIconFromFile, SIGNAL(triggered()), this, SLOT(onLoadIconFromFile())); diff --git a/src/gui/formstandardfeeddetails.h b/src/gui/formstandardfeeddetails.h index f577643a3..53700e607 100644 --- a/src/gui/formstandardfeeddetails.h +++ b/src/gui/formstandardfeeddetails.h @@ -30,6 +30,7 @@ class FormStandardFeedDetails : public QDialog { protected slots: // Applies changes. void apply(); + void guessFeed(); // Trigerred when title/description/url/username/password changes. void onTitleChanged(const QString &new_title);