File indexing completed on 2024-05-12 05:10:11

0001 /***************************************************************************
0002     Copyright (C) 2011 Robby Stephenson <robby@periapsis.org>
0003  ***************************************************************************/
0004 
0005 /***************************************************************************
0006  *                                                                         *
0007  *   This program is free software; you can redistribute it and/or         *
0008  *   modify it under the terms of the GNU General Public License as        *
0009  *   published by the Free Software Foundation; either version 2 of        *
0010  *   the License or (at your option) version 3 or any later version        *
0011  *   accepted by the membership of KDE e.V. (or its successor approved     *
0012  *   by the membership of KDE e.V.), which shall act as a proxy            *
0013  *   defined in Section 14 of version 3 of the license.                    *
0014  *                                                                         *
0015  *   This program is distributed in the hope that it will be useful,       *
0016  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0017  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0018  *   GNU General Public License for more details.                          *
0019  *                                                                         *
0020  *   You should have received a copy of the GNU General Public License     *
0021  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
0022  *                                                                         *
0023  ***************************************************************************/
0024 
0025 #include "goodreadsimporter.h"
0026 #include "xslthandler.h"
0027 #include "tellicoimporter.h"
0028 #include "../core/filehandler.h"
0029 #include "../utils/datafileregistry.h"
0030 #include "../utils/string_utils.h"
0031 #include "../tellico_debug.h"
0032 
0033 #include <KConfigGroup>
0034 #include <KLocalizedString>
0035 
0036 #include <QLineEdit>
0037 #include <QVBoxLayout>
0038 #include <QFormLayout>
0039 #include <QGroupBox>
0040 #include <QDomDocument>
0041 #include <QRegularExpression>
0042 #include <QUrlQuery>
0043 #include <QFile>
0044 
0045 namespace {
0046   static const char* GOODREADS_LIST_URL = "https://www.goodreads.com/review/list.xml";
0047   static const char* GOODREADS_USER_URL = "https://www.goodreads.com/user/show.xml";
0048   static const char* GOODREADS_API_KEY = "23626735f5a498f2f2c003300549c0b73f5caa9e87e9faca81eac394eaa5a0d66b3a6d0f563182f29efa";
0049 }
0050 
0051 using Tellico::Import::GoodreadsImporter;
0052 
0053 GoodreadsImporter::GoodreadsImporter() : Import::Importer(), m_widget(nullptr), m_userEdit(nullptr) {
0054   QString xsltFile = DataFileRegistry::self()->locate(QStringLiteral("goodreads2tellico.xsl"));
0055   if(!xsltFile.isEmpty()) {
0056     m_xsltURL = QUrl::fromLocalFile(xsltFile);
0057   } else {
0058     myWarning() << "unable to find goodreads2tellico.xsl!";
0059   }
0060 }
0061 
0062 void GoodreadsImporter::setConfig(KSharedConfig::Ptr config_) {
0063   m_config = config_;
0064 }
0065 
0066 bool GoodreadsImporter::canImport(int type) const {
0067   return type == Data::Collection::Book;
0068 }
0069 
0070 Tellico::Data::CollPtr GoodreadsImporter::collection() {
0071   if(m_coll) {
0072     return m_coll;
0073   }
0074 
0075   if(!m_config) {
0076     m_config = KSharedConfig::openConfig();
0077   }
0078 
0079   KConfigGroup cg(m_config, QStringLiteral("ImportOptions - Goodreads"));
0080   m_user = cg.readEntry("User ID");
0081   m_key = cg.readEntry("Developer Key");
0082   if(m_key.isEmpty()) {
0083     m_key = Tellico::reverseObfuscate(GOODREADS_API_KEY);
0084   }
0085 
0086   if(m_xsltURL.isEmpty() || !m_xsltURL.isValid()) {
0087     setStatusMessage(i18n("A valid XSLT file is needed to import the file."));
0088     return Data::CollPtr();
0089   }
0090 
0091   if(m_widget) {
0092     m_user = m_userEdit->text().trimmed();
0093   } else if(m_user.isEmpty()) {
0094     myWarning() << "no widget and no user!";
0095     return Data::CollPtr();
0096   }
0097 
0098   // if the user is not all digits, assume it's a user name and
0099   // convert it to a user id
0100   static const QRegularExpression digitsRx(QLatin1String("^\\d+$"));
0101   if(!digitsRx.match(m_user).hasMatch()) {
0102     m_user = idFromName(m_user);
0103   }
0104   if(m_user.isEmpty()) {
0105     setStatusMessage(i18n("A valid user ID must be entered."));
0106     return Data::CollPtr();
0107   }
0108 
0109   XSLTHandler handler(m_xsltURL);
0110   if(!handler.isValid()) {
0111     setStatusMessage(i18n("Tellico encountered an error in XSLT processing."));
0112     return Data::CollPtr();
0113   }
0114 
0115   const QString text = this->text();
0116 #if 0
0117   myWarning() << "Remove output debug from goodreadsimporter.cpp";
0118   QFile f(QLatin1String("/tmp/goodreads.xml"));
0119   if(f.open(QIODevice::WriteOnly)) {
0120     QTextStream t(&f);
0121     t.setCodec("UTF-8");
0122     t << text;
0123   }
0124   f.close();
0125 #endif
0126 
0127   QString str = handler.applyStylesheet(text);
0128 //  myDebug() << str;
0129 
0130   Import::TellicoImporter imp(str);
0131   imp.setOptions(imp.options() ^ Import::ImportShowImageErrors);
0132   m_coll = imp.collection();
0133   setStatusMessage(imp.statusMessage());
0134 
0135   cg.writeEntry("User ID", m_user);
0136   cg.writeEntry("Developer Key", m_key);
0137 
0138   return m_coll;
0139 }
0140 
0141 QWidget* GoodreadsImporter::widget(QWidget* parent_) {
0142   if(m_widget) {
0143     return m_widget;
0144   }
0145   m_widget = new QWidget(parent_);
0146   QVBoxLayout* l = new QVBoxLayout(m_widget);
0147 
0148   QGroupBox* gbox = new QGroupBox(i18n("Goodreads Options"), m_widget);
0149   QFormLayout* lay = new QFormLayout(gbox);
0150 
0151   m_userEdit = new QLineEdit(gbox);
0152   m_userEdit->setText(m_user);
0153 
0154   lay->addRow(i18n("User ID:"), m_userEdit);
0155 
0156   l->addWidget(gbox);
0157   l->addStretch(1);
0158 
0159   return m_widget;
0160 }
0161 
0162 QString GoodreadsImporter::text() const {
0163   QUrl u(QString::fromLatin1(GOODREADS_LIST_URL));
0164   QUrlQuery q;
0165   q.addQueryItem(QStringLiteral("v"), QStringLiteral("2"));
0166   q.addQueryItem(QStringLiteral("id"), m_user);
0167   q.addQueryItem(QStringLiteral("key"), m_key);
0168   u.setQuery(q);
0169 //  myDebug() << u;
0170   return FileHandler::readTextFile(u, true /* quiet */, true);
0171 }
0172 
0173 QString GoodreadsImporter::idFromName(const QString& name_) const {
0174   QUrl u(QString::fromLatin1(GOODREADS_USER_URL));
0175   QUrlQuery q;
0176   q.addQueryItem(QStringLiteral("username"), name_);
0177   q.addQueryItem(QStringLiteral("key"), m_key);
0178   u.setQuery(q);
0179 //  myDebug() << u;
0180 
0181   QDomDocument dom = FileHandler::readXMLDocument(u, false /* process namespace */, true /* quiet */);
0182   return dom.documentElement().namedItem(QStringLiteral("user"))
0183                               .namedItem(QStringLiteral("id"))
0184                               .toElement()
0185                               .text()
0186                               .trimmed();
0187 }