diff options
Diffstat (limited to 'src/fetch/crossreffetcher.cpp')
-rw-r--r-- | src/fetch/crossreffetcher.cpp | 392 |
1 files changed, 392 insertions, 0 deletions
diff --git a/src/fetch/crossreffetcher.cpp b/src/fetch/crossreffetcher.cpp new file mode 100644 index 0000000..8c5d303 --- /dev/null +++ b/src/fetch/crossreffetcher.cpp @@ -0,0 +1,392 @@ +/*************************************************************************** + copyright : (C) 2007 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#include "crossreffetcher.h" +#include "messagehandler.h" +#include "../translators/xslthandler.h" +#include "../translators/tellicoimporter.h" +#include "../tellico_kernel.h" +#include "../tellico_utils.h" +#include "../collection.h" +#include "../entry.h" +#include "../core/netaccess.h" +#include "../imagefactory.h" +#include "../tellico_debug.h" + +#include <klocale.h> +#include <kstandarddirs.h> +#include <kconfig.h> +#include <klineedit.h> +#include <kactivelabel.h> + +#include <qlabel.h> +#include <qwhatsthis.h> +#include <qlayout.h> +#include <qfile.h> + +// #define CROSSREF_TEST + +#define CROSSREF_USE_UNIXREF + +namespace { + static const char* CROSSREF_BASE_URL = "http://www.crossref.org/openurl/?url_ver=Z39.88-2004&noredirect=true"; +} + +using Tellico::Fetch::CrossRefFetcher; + +CrossRefFetcher::CrossRefFetcher(QObject* parent_) + : Fetcher(parent_), m_xsltHandler(0), m_job(0), m_started(false) { +} + +CrossRefFetcher::~CrossRefFetcher() { + delete m_xsltHandler; + m_xsltHandler = 0; +} + +QString CrossRefFetcher::defaultName() { + return QString::fromLatin1("CrossRef"); +} + +QString CrossRefFetcher::source() const { + return m_name.isEmpty() ? defaultName() : m_name; +} + +bool CrossRefFetcher::canFetch(int type) const { + return type == Data::Collection::Bibtex; +} + +void CrossRefFetcher::readConfigHook(const KConfigGroup& config_) { + QString s = config_.readEntry("User"); + if(!s.isEmpty()) { + m_user = s; + } + s = config_.readEntry("Password"); + if(!s.isEmpty()) { + m_password = s; + } +} + +void CrossRefFetcher::search(FetchKey key_, const QString& value_) { + m_key = key_; + m_value = value_.stripWhiteSpace(); + m_started = true; + + if(m_user.isEmpty() || m_password.isEmpty()) { + message(i18n("%1 requires a username and password.").arg(source()), MessageHandler::Warning); + stop(); + return; + } + + if(!canFetch(Kernel::self()->collectionType())) { + message(i18n("%1 does not allow searching for this collection type.").arg(source()), MessageHandler::Warning); + stop(); + return; + } + + m_data.truncate(0); + +// myDebug() << "CrossRefFetcher::search() - value = " << value_ << endl; + + KURL u = searchURL(m_key, m_value); + if(u.isEmpty()) { + stop(); + return; + } + + m_job = KIO::get(u, false, false); + connect(m_job, SIGNAL(data(KIO::Job*, const QByteArray&)), + SLOT(slotData(KIO::Job*, const QByteArray&))); + connect(m_job, SIGNAL(result(KIO::Job*)), + SLOT(slotComplete(KIO::Job*))); +} + +void CrossRefFetcher::stop() { + if(!m_started) { + return; + } +// myDebug() << "CrossRefFetcher::stop()" << endl; + if(m_job) { + m_job->kill(); + m_job = 0; + } + m_data.truncate(0); + m_started = false; + emit signalDone(this); +} + +void CrossRefFetcher::slotData(KIO::Job*, const QByteArray& data_) { + QDataStream stream(m_data, IO_WriteOnly | IO_Append); + stream.writeRawBytes(data_.data(), data_.size()); +} + +void CrossRefFetcher::slotComplete(KIO::Job* job_) { +// myDebug() << "CrossRefFetcher::slotComplete()" << endl; + // since the fetch is done, don't worry about holding the job pointer + m_job = 0; + + if(job_->error()) { + job_->showErrorDialog(Kernel::self()->widget()); + stop(); + return; + } + + if(m_data.isEmpty()) { + myDebug() << "CrossRefFetcher::slotComplete() - no data" << endl; + stop(); + return; + } + +#if 0 + kdWarning() << "Remove debug from crossreffetcher.cpp" << endl; + QFile f(QString::fromLatin1("/tmp/test.xml")); + if(f.open(IO_WriteOnly)) { + QTextStream t(&f); + t.setEncoding(QTextStream::UnicodeUTF8); + t << QCString(m_data, m_data.size()+1); + } + f.close(); +#endif + + if(!m_xsltHandler) { + initXSLTHandler(); + if(!m_xsltHandler) { // probably an error somewhere in the stylesheet loading + stop(); + return; + } + } + + // assume result is always utf-8 + QString str = m_xsltHandler->applyStylesheet(QString::fromUtf8(m_data, m_data.size())); + Import::TellicoImporter imp(str); + Data::CollPtr coll = imp.collection(); + + if(!coll) { + myDebug() << "CrossRefFetcher::slotComplete() - no valid result" << endl; + stop(); + return; + } + + Data::EntryVec entries = coll->entries(); + for(Data::EntryVec::Iterator entry = entries.begin(); entry != entries.end(); ++entry) { + if(!m_started) { + // might get aborted + break; + } + QString desc = entry->field(QString::fromLatin1("author")) + + QChar('/') + entry->field(QString::fromLatin1("publisher")); + if(!entry->field(QString::fromLatin1("year")).isEmpty()) { + desc += QChar('/') + entry->field(QString::fromLatin1("year")); + } + + SearchResult* r = new SearchResult(this, entry->title(), desc, entry->field(QString::fromLatin1("isbn"))); + m_entries.insert(r->uid, Data::EntryPtr(entry)); + emit signalResultFound(r); + } + + stop(); // required +} + +Tellico::Data::EntryPtr CrossRefFetcher::fetchEntry(uint uid_) { + Data::EntryPtr entry = m_entries[uid_]; + // if URL but no cover image, fetch it + if(!entry->field(QString::fromLatin1("url")).isEmpty()) { + Data::CollPtr coll = entry->collection(); + Data::FieldPtr field = coll->fieldByName(QString::fromLatin1("cover")); + if(!field && !coll->imageFields().isEmpty()) { + field = coll->imageFields().front(); + } else if(!field) { + field = new Data::Field(QString::fromLatin1("cover"), i18n("Front Cover"), Data::Field::Image); + coll->addField(field); + } + if(entry->field(field).isEmpty()) { + QPixmap pix = NetAccess::filePreview(entry->field(QString::fromLatin1("url"))); + if(!pix.isNull()) { + QString id = ImageFactory::addImage(pix, QString::fromLatin1("PNG")); + if(!id.isEmpty()) { + entry->setField(field, id); + } + } + } + } + return entry; +} + +void CrossRefFetcher::initXSLTHandler() { +#ifdef CROSSREF_USE_UNIXREF + QString xsltfile = locate("appdata", QString::fromLatin1("unixref2tellico.xsl")); +#else + QString xsltfile = locate("appdata", QString::fromLatin1("crossref2tellico.xsl")); +#endif + if(xsltfile.isEmpty()) { + kdWarning() << "CrossRefFetcher::initXSLTHandler() - can not locate xslt file." << endl; + return; + } + + KURL u; + u.setPath(xsltfile); + + delete m_xsltHandler; + m_xsltHandler = new XSLTHandler(u); + if(!m_xsltHandler->isValid()) { + kdWarning() << "CrossRefFetcher::initXSLTHandler() - error in crossref2tellico.xsl." << endl; + delete m_xsltHandler; + m_xsltHandler = 0; + return; + } +} + +KURL CrossRefFetcher::searchURL(FetchKey key_, const QString& value_) const { + KURL u(QString::fromLatin1(CROSSREF_BASE_URL)); +#ifdef CROSSREF_USE_UNIXREF + u.addQueryItem(QString::fromLatin1("format"), QString::fromLatin1("unixref")); +#endif + u.addQueryItem(QString::fromLatin1("req_dat"), QString::fromLatin1("ourl_%1:%2").arg(m_user, m_password)); + + switch(key_) { + case DOI: + u.addQueryItem(QString::fromLatin1("rft_id"), QString::fromLatin1("info:doi/%1").arg(value_)); + break; + + default: + kdWarning() << "CrossRefFetcher::search() - key not recognized: " << m_key << endl; + return KURL(); + } + +#ifdef CROSSREF_TEST + u = KURL::fromPathOrURL(QString::fromLatin1("/home/robby/crossref.xml")); +#endif + myDebug() << "CrossRefFetcher::search() - url: " << u.url() << endl; + return u; +} + +void CrossRefFetcher::updateEntry(Data::EntryPtr entry_) { + QString doi = entry_->field(QString::fromLatin1("doi")); + if(!doi.isEmpty()) { + search(Fetch::DOI, doi); + return; + } + +#if 0 + // optimistically try searching for title and rely on Collection::sameEntry() to figure things out + QString t = entry_->field(QString::fromLatin1("title")); + if(!t.isEmpty()) { + m_limit = 10; // raise limit so more possibility of match + search(Fetch::Title, t); + return; + } +#endif + + myDebug() << "CrossRefFetcher::updateEntry() - insufficient info to search" << endl; + emit signalDone(this); // always need to emit this if not continuing with the search +} + +void CrossRefFetcher::updateEntrySynchronous(Data::EntryPtr entry) { + if(!entry) { + return; + } + if(m_user.isEmpty() || m_password.isEmpty()) { + myDebug() << "CrossRefFetcher::updateEntrySynchronous() - username and password is required" << endl; + return; + } + QString doi = entry->field(QString::fromLatin1("doi")); + if(doi.isEmpty()) { + return; + } + + KURL u = searchURL(DOI, doi); + QString xml = FileHandler::readTextFile(u, true, true); + if(xml.isEmpty()) { + return; + } + + if(!m_xsltHandler) { + initXSLTHandler(); + if(!m_xsltHandler) { // probably an error somewhere in the stylesheet loading + return; + } + } + + // assume result is always utf-8 + QString str = m_xsltHandler->applyStylesheet(xml); + Import::TellicoImporter imp(str); + Data::CollPtr coll = imp.collection(); + if(coll && coll->entryCount() > 0) { + myLog() << "CrossRefFetcher::updateEntrySynchronous() - found DOI result, merging" << endl; + Data::Collection::mergeEntry(entry, coll->entries().front(), false /*overwrite*/); + } +} + +Tellico::Fetch::ConfigWidget* CrossRefFetcher::configWidget(QWidget* parent_) const { + return new CrossRefFetcher::ConfigWidget(parent_, this); +} + +CrossRefFetcher::ConfigWidget::ConfigWidget(QWidget* parent_, const CrossRefFetcher* fetcher_) + : Fetch::ConfigWidget(parent_) { + QGridLayout* l = new QGridLayout(optionsWidget(), 4, 2); + l->setSpacing(4); + l->setColStretch(1, 10); + + int row = 0; + + KActiveLabel* al = new KActiveLabel(i18n("CrossRef requires an account for access. " + "Please read the terms and conditions and " + "<a href='http://www.crossref.org/requestaccount/'>" + "request an account</a>. Enter your OpenURL " + "account information below."), + optionsWidget()); + ++row; + l->addMultiCellWidget(al, row, row, 0, 1); + // richtext gets weird with size + al->setMinimumWidth(al->sizeHint().width()); + + QLabel* label = new QLabel(i18n("&Username: "), optionsWidget()); + l->addWidget(label, ++row, 0); + m_userEdit = new KLineEdit(optionsWidget()); + connect(m_userEdit, SIGNAL(textChanged(const QString&)), SLOT(slotSetModified())); + l->addWidget(m_userEdit, row, 1); + QString w = i18n("A username and password is required to access the CrossRef service. The password is " + "stored as plain text in the Tellico configuration file."); + QWhatsThis::add(label, w); + QWhatsThis::add(m_userEdit, w); + label->setBuddy(m_userEdit); + + label = new QLabel(i18n("&Password: "), optionsWidget()); + l->addWidget(label, ++row, 0); + m_passEdit = new KLineEdit(optionsWidget()); + connect(m_passEdit, SIGNAL(textChanged(const QString&)), SLOT(slotSetModified())); + l->addWidget(m_passEdit, row, 1); + QWhatsThis::add(label, w); + QWhatsThis::add(m_passEdit, w); + label->setBuddy(m_passEdit); + + if(fetcher_) { + m_userEdit->setText(fetcher_->m_user); + m_passEdit->setText(fetcher_->m_password); + } +} + +void CrossRefFetcher::ConfigWidget::saveConfig(KConfigGroup& config_) { + QString s = m_userEdit->text(); + config_.writeEntry("User", s); + + s = m_passEdit->text(); + config_.writeEntry("Password", s); + + slotSetModified(false); +} + +QString CrossRefFetcher::ConfigWidget::preferredName() const { + return CrossRefFetcher::defaultName(); +} + +#include "crossreffetcher.moc" |