File indexing completed on 2025-02-16 04:40:51

0001 /***************************************************************************
0002  *   SPDX-License-Identifier: GPL-2.0-or-later
0003  *                                                                         *
0004  *   SPDX-FileCopyrightText: 2004-2023 Thomas Fischer <fischer@unix-ag.uni-kl.de>
0005  *                                                                         *
0006  *   This program is free software; you can redistribute it and/or modify  *
0007  *   it under the terms of the GNU General Public License as published by  *
0008  *   the Free Software Foundation; either version 2 of the License, or     *
0009  *   (at your option) any later version.                                   *
0010  *                                                                         *
0011  *   This program is distributed in the hope that it will be useful,       *
0012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0014  *   GNU General Public License for more details.                          *
0015  *                                                                         *
0016  *   You should have received a copy of the GNU General Public License     *
0017  *   along with this program; if not, see <https://www.gnu.org/licenses/>. *
0018  ***************************************************************************/
0019 
0020 #include "onlinesearchdoi.h"
0021 
0022 #ifdef HAVE_QTWIDGETS
0023 #include <QLabel>
0024 #include <QLineEdit>
0025 #include <QGridLayout>
0026 #endif // HAVE_QTWIDGETS
0027 #include <QNetworkRequest>
0028 #include <QNetworkReply>
0029 #include <QRegularExpression>
0030 
0031 #ifdef HAVE_KF
0032 #include <KConfigGroup>
0033 #include <KLocalizedString>
0034 #endif // HAVE_KF
0035 
0036 #include <KBibTeX>
0037 #include <FileImporterBibTeX>
0038 #include "internalnetworkaccessmanager.h"
0039 #include "onlinesearchabstract_p.h"
0040 #include "logging_networking.h"
0041 
0042 #ifdef HAVE_QTWIDGETS
0043 class OnlineSearchDOI::Form : public OnlineSearchAbstract::Form
0044 {
0045     Q_OBJECT
0046 
0047 private:
0048     QString configGroupName;
0049 
0050     void loadState() {
0051         KConfigGroup configGroup(d->config, configGroupName);
0052         lineEditDoiNumber->setText(configGroup.readEntry(QStringLiteral("doiNumber"), QString()));
0053     }
0054 
0055 public:
0056     QLineEdit *lineEditDoiNumber;
0057 
0058     Form(QWidget *widget)
0059             : OnlineSearchAbstract::Form(widget), configGroupName(QStringLiteral("Search Engine DOI")) {
0060         QGridLayout *layout = new QGridLayout(this);
0061         layout->setContentsMargins(0, 0, 0, 0);
0062 
0063         QLabel *label = new QLabel(i18n("DOI:"), this);
0064         layout->addWidget(label, 0, 0, 1, 1);
0065         lineEditDoiNumber = new QLineEdit(this);
0066         layout->addWidget(lineEditDoiNumber, 0, 1, 1, 1);
0067         lineEditDoiNumber->setClearButtonEnabled(true);
0068         connect(lineEditDoiNumber, &QLineEdit::returnPressed, this, &OnlineSearchDOI::Form::returnPressed);
0069 
0070         layout->setRowStretch(1, 100);
0071         lineEditDoiNumber->setFocus(Qt::TabFocusReason);
0072 
0073         loadState();
0074     }
0075 
0076     bool readyToStart() const override {
0077         return !lineEditDoiNumber->text().isEmpty();
0078     }
0079 
0080     void copyFromEntry(const Entry &entry) override {
0081         lineEditDoiNumber->setText(PlainTextValue::text(entry[Entry::ftDOI]));
0082     }
0083 
0084     void saveState() {
0085         KConfigGroup configGroup(d->config, configGroupName);
0086         configGroup.writeEntry(QStringLiteral("doiNumber"), lineEditDoiNumber->text());
0087         d->config->sync();
0088     }
0089 };
0090 #endif // HAVE_QTWIDGETS
0091 
0092 
0093 class OnlineSearchDOI::OnlineSearchDOIPrivate
0094 {
0095 public:
0096 #ifdef HAVE_QTWIDGETS
0097     OnlineSearchDOI::Form *form;
0098 #endif // HAVE_QTWIDGETS
0099 
0100     OnlineSearchDOIPrivate(OnlineSearchDOI *parent)
0101 #ifdef HAVE_QTWIDGETS
0102             : form(nullptr)
0103 #endif // HAVE_QTWIDGETS
0104     {
0105         Q_UNUSED(parent)
0106     }
0107 
0108 #ifdef HAVE_QTWIDGETS
0109     QUrl buildQueryUrl() {
0110         if (form == nullptr) {
0111             qCWarning(LOG_KBIBTEX_NETWORKING) << "Cannot build query url if no form is specified";
0112             return QUrl();
0113         }
0114 
0115         return QUrl(QStringLiteral("https://dx.doi.org/") + form->lineEditDoiNumber->text());
0116     }
0117 #endif // HAVE_QTWIDGETS
0118 
0119     QUrl buildQueryUrl(const QMap<QueryKey, QString> &query, int numResults) {
0120         Q_UNUSED(numResults)
0121 
0122         const QRegularExpressionMatch doiRegExpMatch = KBibTeX::doiRegExp.match(query[QueryKey::FreeText]);
0123         if (doiRegExpMatch.hasMatch()) {
0124             return QUrl(QStringLiteral("https://dx.doi.org/") + doiRegExpMatch.captured(QStringLiteral("doi")));
0125         }
0126 
0127         return QUrl();
0128     }
0129 };
0130 
0131 
0132 OnlineSearchDOI::OnlineSearchDOI(QObject *parent)
0133         : OnlineSearchAbstract(parent), d(new OnlineSearchDOIPrivate(this))
0134 {
0135     // TODO
0136 }
0137 
0138 OnlineSearchDOI::~OnlineSearchDOI()
0139 {
0140     delete d;
0141 }
0142 
0143 #ifdef HAVE_QTWIDGETS
0144 void OnlineSearchDOI::startSearchFromForm()
0145 {
0146     m_hasBeenCanceled = false;
0147     Q_EMIT progress(curStep = 0, numSteps = 1);
0148 
0149     const QUrl url = d->buildQueryUrl();
0150     if (url.isValid()) {
0151         QNetworkRequest request(url);
0152         request.setRawHeader(QByteArray("Accept"), QByteArray("text/bibliography; style=bibtex"));
0153         QNetworkReply *reply = InternalNetworkAccessManager::instance().get(request);
0154         InternalNetworkAccessManager::instance().setNetworkReplyTimeout(reply);
0155         connect(reply, &QNetworkReply::finished, this, &OnlineSearchDOI::downloadDone);
0156 
0157         d->form->saveState();
0158     } else
0159         delayedStoppedSearch(resultNoError);
0160 
0161     refreshBusyProperty();
0162 }
0163 #endif // HAVE_QTWIDGETS
0164 
0165 void OnlineSearchDOI::startSearch(const QMap<QueryKey, QString> &query, int numResults)
0166 {
0167     m_hasBeenCanceled = false;
0168     Q_EMIT progress(curStep = 0, numSteps = 1);
0169 
0170     const QUrl url = d->buildQueryUrl(query, numResults);
0171     if (url.isValid()) {
0172         QNetworkRequest request(url);
0173         request.setRawHeader(QByteArray("Accept"), QByteArray("text/bibliography; style=bibtex"));
0174         QNetworkReply *reply = InternalNetworkAccessManager::instance().get(request);
0175         InternalNetworkAccessManager::instance().setNetworkReplyTimeout(reply);
0176         connect(reply, &QNetworkReply::finished, this, &OnlineSearchDOI::downloadDone);
0177 
0178         refreshBusyProperty();
0179     } else
0180         delayedStoppedSearch(resultNoError);
0181 }
0182 
0183 QString OnlineSearchDOI::label() const
0184 {
0185     return i18n("DOI");
0186 }
0187 
0188 #ifdef HAVE_QTWIDGETS
0189 OnlineSearchAbstract::Form *OnlineSearchDOI::customWidget(QWidget *parent)
0190 {
0191     if (d->form == nullptr)
0192         d->form = new OnlineSearchDOI::Form(parent);
0193     return d->form;
0194 }
0195 #endif // HAVE_QTWIDGETS
0196 
0197 QUrl OnlineSearchDOI::homepage() const
0198 {
0199     return QUrl(QStringLiteral("https://dx.doi.org/"));
0200 }
0201 
0202 void OnlineSearchDOI::downloadDone()
0203 {
0204     Q_EMIT progress(++curStep, numSteps);
0205     QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
0206 
0207     QUrl redirUrl;
0208     if (handleErrors(reply, redirUrl)) {
0209         if (redirUrl.isValid()) {
0210             /// redirection to another url
0211             ++numSteps;
0212 
0213             QNetworkRequest request(redirUrl);
0214             request.setRawHeader(QByteArray("Accept"), QByteArray("text/bibliography; style=bibtex"));
0215             QNetworkReply *newReply = InternalNetworkAccessManager::instance().get(request);
0216             InternalNetworkAccessManager::instance().setNetworkReplyTimeout(newReply);
0217             connect(newReply, &QNetworkReply::finished, this, &OnlineSearchDOI::downloadDone);
0218         } else {  /// ensure proper treatment of UTF-8 characters
0219             const QString bibTeXcode = QString::fromUtf8(reply->readAll().constData());
0220 
0221             if (!bibTeXcode.isEmpty()) {
0222                 FileImporterBibTeX importer(this);
0223                 File *bibtexFile = importer.fromString(bibTeXcode);
0224 
0225                 bool hasEntries = false;
0226                 if (bibtexFile != nullptr) {
0227                     for (const auto &element : const_cast<const File &>(*bibtexFile)) {
0228                         QSharedPointer<Entry> entry = element.dynamicCast<Entry>();
0229                         hasEntries |= publishEntry(entry);
0230                     }
0231 
0232                     stopSearch(resultNoError);
0233 
0234                     delete bibtexFile;
0235                 } else {
0236                     qCWarning(LOG_KBIBTEX_NETWORKING) << "No valid BibTeX file results returned on request on" << InternalNetworkAccessManager::removeApiKey(reply->url()).toDisplayString();
0237                     stopSearch(resultUnspecifiedError);
0238                 }
0239             } else {
0240                 /// returned file is empty
0241                 stopSearch(resultNoError);
0242             }
0243         }
0244     }
0245 
0246     refreshBusyProperty();
0247 }
0248 
0249 #include "onlinesearchdoi.moc"