File indexing completed on 2024-05-12 05:09:41
0001 /*************************************************************************** 0002 Copyright (C) 2003-2009 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 "srufetcher.h" 0026 #include "../fieldformat.h" 0027 #include "../collection.h" 0028 #include "../translators/tellico_xml.h" 0029 #include "../translators/xslthandler.h" 0030 #include "../translators/tellicoimporter.h" 0031 #include "../utils/guiproxy.h" 0032 #include "../gui/lineedit.h" 0033 #include "../gui/combobox.h" 0034 #include "../gui/stringmapwidget.h" 0035 #include "../utils/string_utils.h" 0036 #include "../utils/lccnvalidator.h" 0037 #include "../utils/isbnvalidator.h" 0038 #include "../utils/datafileregistry.h" 0039 #include "../tellico_debug.h" 0040 0041 #include <KLocalizedString> 0042 #include <KIO/Job> 0043 #include <KJobUiDelegate> 0044 #include <KJobWidgets/KJobWidgets> 0045 #include <KConfigGroup> 0046 #include <KComboBox> 0047 #include <KAcceleratorManager> 0048 0049 #include <QSpinBox> 0050 #include <QLabel> 0051 #include <QGridLayout> 0052 #include <QFile> 0053 #include <QUrlQuery> 0054 #include <QDomDocument> 0055 0056 namespace { 0057 // 7090 was the old default port, but that was just because LoC used it 0058 // let's use default HTTP port of 80 now 0059 static const int SRU_DEFAULT_PORT = 80; 0060 static const int SRU_MAX_RECORDS = 25; 0061 } 0062 0063 using namespace Tellico; 0064 using Tellico::Fetch::SRUFetcher; 0065 0066 SRUFetcher::SRUFetcher(QObject* parent_) 0067 : Fetcher(parent_), m_port(SRU_DEFAULT_PORT), m_job(nullptr), m_MARCXMLHandler(nullptr), m_MODSHandler(nullptr), m_SRWHandler(nullptr), m_started(false) { 0068 } 0069 0070 SRUFetcher::SRUFetcher(const QString& name_, const QString& host_, uint port_, const QString& path_, 0071 const QString& format_, QObject* parent_) : Fetcher(parent_), 0072 m_scheme(QStringLiteral("http")), m_host(host_), m_port(port_), m_path(path_), m_format(format_), 0073 m_job(nullptr), m_MARCXMLHandler(nullptr), m_MODSHandler(nullptr), m_SRWHandler(nullptr), m_started(false) { 0074 m_name = name_; // m_name is protected in super class 0075 if(!m_path.startsWith(QLatin1Char('/'))) { 0076 m_path.prepend(QLatin1Char('/')); 0077 } 0078 } 0079 0080 SRUFetcher::~SRUFetcher() { 0081 delete m_MARCXMLHandler; 0082 m_MARCXMLHandler = nullptr; 0083 delete m_MODSHandler; 0084 m_MODSHandler = nullptr; 0085 delete m_SRWHandler; 0086 m_SRWHandler = nullptr; 0087 } 0088 0089 QString SRUFetcher::source() const { 0090 return m_name.isEmpty() ? defaultName() : m_name; 0091 } 0092 0093 // No Raw for now. 0094 bool SRUFetcher::canSearch(Fetch::FetchKey k) const { 0095 return k == Title || k == Person || k == ISBN || k == Keyword || k == LCCN; 0096 } 0097 0098 bool SRUFetcher::canFetch(int type) const { 0099 return type == Data::Collection::Book || type == Data::Collection::Bibtex; 0100 } 0101 0102 void SRUFetcher::readConfigHook(const KConfigGroup& config_) { 0103 m_scheme = config_.readEntry("Scheme", "http"); 0104 m_host = config_.readEntry("Host"); 0105 int p = config_.readEntry("Port", SRU_DEFAULT_PORT); 0106 if(p > 0) { 0107 m_port = p; 0108 } 0109 m_path = config_.readEntry("Path"); 0110 // used to be called Database 0111 if(m_path.isEmpty()) { 0112 m_path = config_.readEntry("Database"); 0113 } 0114 if(!m_path.startsWith(QLatin1Char('/'))) { 0115 m_path.prepend(QLatin1Char('/')); 0116 } 0117 m_format = config_.readEntry("Format", "mods"); 0118 const QStringList queryFields = config_.readEntry("QueryFields", QStringList()); 0119 const QStringList queryValues = config_.readEntry("QueryValues", QStringList()); 0120 Q_ASSERT(queryFields.count() == queryValues.count()); 0121 for(int i = 0; i < qMin(queryFields.count(), queryValues.count()); ++i) { 0122 m_queryMap.insert(queryFields.at(i), queryValues.at(i)); 0123 } 0124 } 0125 0126 void SRUFetcher::search() { 0127 m_started = true; 0128 if(m_host.isEmpty() || m_path.isEmpty() || m_format.isEmpty()) { 0129 myDebug() << source() << "- host and path are not set!"; 0130 stop(); 0131 return; 0132 } 0133 0134 QUrl u; 0135 u.setScheme(m_scheme); 0136 u.setHost(m_host); 0137 u.setPort(m_port); 0138 u = QUrl::fromUserInput(u.url() + m_path); 0139 0140 QUrlQuery query; 0141 for(StringMap::ConstIterator it = m_queryMap.constBegin(); it != m_queryMap.constEnd(); ++it) { 0142 query.addQueryItem(it.key(), it.value()); 0143 } 0144 // allow user to override these so check for existing item first 0145 if(!query.hasQueryItem(QStringLiteral("operation"))) { 0146 query.addQueryItem(QStringLiteral("operation"), QStringLiteral("searchRetrieve")); 0147 } 0148 if(!query.hasQueryItem(QStringLiteral("version"))) { 0149 query.addQueryItem(QStringLiteral("version"), QStringLiteral("1.1")); 0150 } 0151 if(!query.hasQueryItem(QStringLiteral("maximumRecords"))) { 0152 query.addQueryItem(QStringLiteral("maximumRecords"), QString::number(SRU_MAX_RECORDS)); 0153 } 0154 if(!m_format.isEmpty() && m_format != QLatin1String("none") 0155 && !query.hasQueryItem(QStringLiteral("recordSchema"))) { 0156 query.addQueryItem(QStringLiteral("recordSchema"), m_format); 0157 } 0158 0159 const int type = collectionType(); 0160 QString str = QLatin1Char('"') + request().value() + QLatin1Char('"'); 0161 switch(request().key()) { 0162 case Title: 0163 query.addQueryItem(QStringLiteral("query"), QLatin1String("dc.title=") + str); 0164 break; 0165 0166 case Person: 0167 { 0168 QString s; 0169 if(type == Data::Collection::Book || type == Data::Collection::Bibtex) { 0170 s = QLatin1String("author=") + str + QLatin1String(" or dc.author=") + str; 0171 } else { 0172 s = QLatin1String("dc.creator=") + str + QLatin1String(" or dc.editor=") + str; 0173 } 0174 query.addQueryItem(QStringLiteral("query"), s); 0175 } 0176 break; 0177 0178 case ISBN: 0179 { 0180 QString s = request().value(); 0181 s.remove(QLatin1Char('-')); 0182 QStringList isbnList = FieldFormat::splitValue(s); 0183 // also search for isbn10 values 0184 for(QStringList::Iterator it = isbnList.begin(); it != isbnList.end(); ++it) { 0185 if((*it).startsWith(QLatin1String("978"))) { 0186 QString isbn10 = ISBNValidator::isbn10(*it); 0187 isbn10.remove(QLatin1Char('-')); 0188 it = isbnList.insert(it, isbn10); 0189 ++it; 0190 } 0191 } 0192 QString q; 0193 for(int i = 0; i < isbnList.count(); ++i) { 0194 // make an assumption that DC output uses the dc profile and everything else uses Bath for ISBN 0195 // no idea if this holds true universally, but matches LOC, COPAC, and KB 0196 if(m_format == QLatin1String("dc")) { 0197 q += QLatin1String("dc.identifier=") + isbnList.at(i); 0198 } else { 0199 q += QLatin1String("bath.isbn=") + isbnList.at(i); 0200 } 0201 if(i < isbnList.count()-1) { 0202 q += QLatin1String(" or "); 0203 } 0204 } 0205 query.addQueryItem(QStringLiteral("query"), q); 0206 } 0207 break; 0208 0209 case LCCN: 0210 { 0211 QString s = request().value(); 0212 QStringList lccnList = FieldFormat::splitValue(s); 0213 QString q; 0214 for(int i = 0; i < lccnList.count(); ++i) { 0215 q += QLatin1String("bath.lccn=") + lccnList.at(i); 0216 q += QLatin1String(" or bath.lccn=") + LCCNValidator::formalize(lccnList.at(i)); 0217 if(i < lccnList.count()-1) { 0218 q += QLatin1String(" or "); 0219 } 0220 } 0221 query.addQueryItem(QStringLiteral("query"), q); 0222 } 0223 break; 0224 0225 case Keyword: 0226 query.addQueryItem(QStringLiteral("query"), str); 0227 break; 0228 0229 case Raw: 0230 { 0231 QString key = request().value().section(QLatin1Char('='), 0, 0).trimmed(); 0232 QString str = request().value().section(QLatin1Char('='), 1).trimmed(); 0233 query.addQueryItem(key, str); 0234 } 0235 break; 0236 0237 default: 0238 myWarning() << source() << "- key not recognized:" << request().key(); 0239 stop(); 0240 break; 0241 } 0242 u.setQuery(query); 0243 // myDebug() << u.url(); 0244 0245 m_job = KIO::storedGet(u, KIO::NoReload, KIO::HideProgressInfo); 0246 KJobWidgets::setWindow(m_job, GUI::Proxy::widget()); 0247 connect(m_job.data(), &KJob::result, 0248 this, &SRUFetcher::slotComplete); 0249 } 0250 0251 void SRUFetcher::stop() { 0252 if(!m_started) { 0253 return; 0254 } 0255 if(m_job) { 0256 m_job->kill(); 0257 m_job = nullptr; 0258 } 0259 0260 m_started = false; 0261 emit signalDone(this); 0262 } 0263 0264 void SRUFetcher::slotComplete(KJob*) { 0265 if(m_job->error()) { 0266 m_job->uiDelegate()->showErrorMessage(); 0267 stop(); 0268 return; 0269 } 0270 0271 QByteArray data = m_job->data(); 0272 if(data.isEmpty()) { 0273 stop(); 0274 return; 0275 } 0276 // see bug 319662. If fetcher is cancelled, job is killed 0277 // if the pointer is retained, it gets double-deleted 0278 m_job = nullptr; 0279 0280 #if 0 0281 myWarning() << "Remove debug from srufetcher.cpp"; 0282 QFile f(QString::fromLatin1("/tmp/test.xml")); 0283 if(f.open(QIODevice::WriteOnly)) { 0284 QTextStream t(&f); 0285 t.setCodec("UTF-8"); 0286 t << data; 0287 } 0288 f.close(); 0289 #endif 0290 0291 Data::CollPtr coll; 0292 QString msg; 0293 0294 const QString result = QString::fromUtf8(data.constData(), data.size()); 0295 0296 // first check for SRU errors 0297 QDomDocument dom; 0298 if(!dom.setContent(result, true /*namespace*/)) { 0299 myWarning() << "server did not return valid XML."; 0300 stop(); 0301 return; 0302 } 0303 0304 const QString& diag = XML::nsZingDiag; 0305 QDomNodeList diagList = dom.elementsByTagNameNS(diag, QStringLiteral("diagnostic")); 0306 for(int i = 0; i < diagList.count(); ++i) { 0307 QDomElement elem = diagList.item(i).toElement(); 0308 QDomNodeList nodeList1 = elem.elementsByTagNameNS(diag, QStringLiteral("message")); 0309 QDomNodeList nodeList2 = elem.elementsByTagNameNS(diag, QStringLiteral("details")); 0310 for(int j = 0; j < nodeList1.count(); ++j) { 0311 QString d = nodeList1.item(j).toElement().text(); 0312 if(!d.isEmpty()) { 0313 QString d2 = nodeList2.item(j).toElement().text(); 0314 if(!d2.isEmpty()) { 0315 d += QLatin1String(" (") + d2 + QLatin1Char(')'); 0316 } 0317 myDebug() << "[" << m_host << "/" << m_path << "]" << d; 0318 if(!msg.isEmpty()) { 0319 msg += QLatin1Char('\n'); 0320 } 0321 msg += d; 0322 } 0323 } 0324 } 0325 0326 QString modsResult; 0327 if(m_format == QLatin1String("mods")) { 0328 modsResult = result; 0329 // } else if(m_format == QLatin1String("marcxml") && initMARCXMLHandler()) { 0330 // some SRU data sources call it MARC21-xml or something other than marcxml 0331 } else if(m_format.startsWith(QLatin1String("marc"), Qt::CaseInsensitive) && initMARCXMLHandler()) { 0332 // brute force marcxchange conversion. This is probably wrong at some level 0333 QString newResult = result; 0334 if(m_format.startsWith(QLatin1String("marcxchange"), Qt::CaseInsensitive)) { 0335 static const QRegularExpression marcRx(QLatin1String("xmlns:marc=\"info:lc/xmlns/marcxchange-v[12]\"")); 0336 newResult.replace(marcRx, 0337 QStringLiteral("xmlns:marc=\"http://www.loc.gov/MARC21/slim\"")); 0338 } 0339 modsResult = m_MARCXMLHandler->applyStylesheet(newResult); 0340 } 0341 if(!modsResult.isEmpty() && initMODSHandler()) { 0342 Import::TellicoImporter imp(m_MODSHandler->applyStylesheet(modsResult)); 0343 coll = imp.collection(); 0344 if(!msg.isEmpty()) { 0345 msg += QLatin1Char('\n'); 0346 } 0347 msg += imp.statusMessage(); 0348 } else if((m_format == QLatin1String("pam") || 0349 m_format == QLatin1String("dc") || 0350 m_format == QLatin1String("none")) && 0351 initSRWHandler()) { 0352 Import::TellicoImporter imp(m_SRWHandler->applyStylesheet(result)); 0353 coll = imp.collection(); 0354 if(!msg.isEmpty()) { 0355 msg += QLatin1Char('\n'); 0356 } 0357 msg += imp.statusMessage(); 0358 } else { 0359 myDebug() << "unrecognized format:" << m_format; 0360 stop(); 0361 return; 0362 } 0363 0364 if(coll && !msg.isEmpty()) { 0365 message(msg, coll->entryCount() == 0 ? MessageHandler::Warning : MessageHandler::Status); 0366 } 0367 0368 if(!coll) { 0369 myDebug() << "no collection pointer"; 0370 if(!msg.isEmpty()) { 0371 message(msg, MessageHandler::Error); 0372 } 0373 stop(); 0374 return; 0375 } 0376 0377 // since the Dewey and LoC field titles have a context in their i18n call here 0378 // but not in the stylesheet where the field is actually created 0379 // update the field titles here 0380 QHashIterator<QString, QString> i(allOptionalFields()); 0381 while(i.hasNext()) { 0382 i.next(); 0383 Data::FieldPtr field = coll->fieldByName(i.key()); 0384 if(field) { 0385 field->setTitle(i.value()); 0386 coll->modifyField(field); 0387 } 0388 } 0389 0390 foreach(Data::EntryPtr entry, coll->entries()) { 0391 FetchResult* r = new FetchResult(this, entry); 0392 m_entries.insert(r->uid, entry); 0393 emit signalResultFound(r); 0394 } 0395 stop(); 0396 } 0397 0398 Tellico::Data::EntryPtr SRUFetcher::fetchEntryHook(uint uid_) { 0399 return m_entries[uid_]; 0400 } 0401 0402 Tellico::Fetch::FetchRequest SRUFetcher::updateRequest(Data::EntryPtr entry_) { 0403 // myDebug() << source() << ": " << entry_->title(); 0404 QString isbn = entry_->field(QStringLiteral("isbn")); 0405 if(!isbn.isEmpty()) { 0406 return FetchRequest(Fetch::ISBN, isbn); 0407 } 0408 0409 QString lccn = entry_->field(QStringLiteral("lccn")); 0410 if(!lccn.isEmpty()) { 0411 return FetchRequest(Fetch::LCCN, lccn); 0412 } 0413 0414 // optimistically try searching for title and rely on Collection::sameEntry() to figure things out 0415 QString t = entry_->field(QStringLiteral("title")); 0416 if(!t.isEmpty()) { 0417 return FetchRequest(Fetch::Title, t); 0418 } 0419 return FetchRequest(); 0420 } 0421 0422 bool SRUFetcher::initMARCXMLHandler() { 0423 if(m_MARCXMLHandler) { 0424 return true; 0425 } 0426 0427 QString xsltfile = DataFileRegistry::self()->locate(QStringLiteral("MARC21slim2MODS3.xsl")); 0428 if(xsltfile.isEmpty()) { 0429 myWarning() << "can not locate MARC21slim2MODS3.xsl."; 0430 return false; 0431 } 0432 0433 QUrl u = QUrl::fromLocalFile(xsltfile); 0434 0435 m_MARCXMLHandler = new XSLTHandler(u); 0436 if(!m_MARCXMLHandler->isValid()) { 0437 myWarning() << "error in MARC21slim2MODS3.xsl."; 0438 delete m_MARCXMLHandler; 0439 m_MARCXMLHandler = nullptr; 0440 return false; 0441 } 0442 return true; 0443 } 0444 0445 bool SRUFetcher::initMODSHandler() { 0446 if(m_MODSHandler) { 0447 return true; 0448 } 0449 0450 QString xsltfile = DataFileRegistry::self()->locate(QStringLiteral("mods2tellico.xsl")); 0451 if(xsltfile.isEmpty()) { 0452 myWarning() << "can not locate mods2tellico.xsl."; 0453 return false; 0454 } 0455 0456 QUrl u = QUrl::fromLocalFile(xsltfile); 0457 0458 m_MODSHandler = new XSLTHandler(u); 0459 if(!m_MODSHandler->isValid()) { 0460 myWarning() << "error in mods2tellico.xsl."; 0461 delete m_MODSHandler; 0462 m_MODSHandler = nullptr; 0463 return false; 0464 } 0465 return true; 0466 } 0467 0468 bool SRUFetcher::initSRWHandler() { 0469 if(m_SRWHandler) { 0470 return true; 0471 } 0472 0473 QString xsltfile = DataFileRegistry::self()->locate(QStringLiteral("srw2tellico.xsl")); 0474 if(xsltfile.isEmpty()) { 0475 myWarning() << "can not locate srw2tellico.xsl."; 0476 return false; 0477 } 0478 0479 QUrl u = QUrl::fromLocalFile(xsltfile); 0480 0481 m_SRWHandler = new XSLTHandler(u); 0482 if(!m_SRWHandler->isValid()) { 0483 myWarning() << "error in srw2tellico.xsl."; 0484 delete m_SRWHandler; 0485 m_SRWHandler = nullptr; 0486 return false; 0487 } 0488 return true; 0489 } 0490 0491 Tellico::Fetch::Fetcher::Ptr SRUFetcher::libraryOfCongress(QObject* parent_) { 0492 return Fetcher::Ptr(new SRUFetcher(i18n("Library of Congress (US)"), QStringLiteral("lx2.loc.gov"), 210, 0493 QStringLiteral("LCDB"), QStringLiteral("mods"), parent_)); 0494 } 0495 0496 QString SRUFetcher::defaultName() { 0497 return i18n("SRU Server"); 0498 } 0499 0500 QString SRUFetcher::defaultIcon() { 0501 return QStringLiteral(":/icons/sru"); 0502 } 0503 0504 // static 0505 Tellico::StringHash SRUFetcher::allOptionalFields() { 0506 StringHash hash; 0507 hash[QStringLiteral("address")] = i18n("Address"); 0508 hash[QStringLiteral("abstract")] = i18n("Abstract"); 0509 hash[QStringLiteral("dewey")] = i18nc("Dewey Decimal classification system", "Dewey Decimal"); 0510 hash[QStringLiteral("lcc")] = i18nc("Library of Congress classification system", "LoC Classification"); 0511 return hash; 0512 } 0513 0514 Tellico::Fetch::ConfigWidget* SRUFetcher::configWidget(QWidget* parent_) const { 0515 return new ConfigWidget(parent_, this); 0516 } 0517 0518 SRUFetcher::ConfigWidget::ConfigWidget(QWidget* parent_, const SRUFetcher* fetcher_ /*=0*/) 0519 : Fetch::ConfigWidget(parent_) { 0520 QGridLayout* l = new QGridLayout(optionsWidget()); 0521 l->setSpacing(4); 0522 l->setColumnStretch(1, 10); 0523 0524 int row = -1; 0525 QLabel* label = new QLabel(i18n("Scheme: "), optionsWidget()); 0526 l->addWidget(label, ++row, 0); 0527 m_schemeCombo = new GUI::ComboBox(optionsWidget()); 0528 m_schemeCombo->addItem(QStringLiteral("http")); 0529 m_schemeCombo->addItem(QStringLiteral("https")); 0530 void (GUI::ComboBox::* activatedInt)(int) = &GUI::ComboBox::activated; 0531 connect(m_schemeCombo, activatedInt, this, &ConfigWidget::slotSetModified); 0532 connect(m_schemeCombo, &QComboBox::editTextChanged, this, &ConfigWidget::slotSetModified); 0533 l->addWidget(m_schemeCombo, row, 1); 0534 QString w = i18n("Enter the path to the database used by the server."); 0535 label->setWhatsThis(w); 0536 m_schemeCombo->setWhatsThis(w); 0537 label->setBuddy(m_schemeCombo); 0538 0539 label = new QLabel(i18n("Hos&t: "), optionsWidget()); 0540 l->addWidget(label, ++row, 0); 0541 m_hostEdit = new GUI::LineEdit(optionsWidget()); 0542 connect(m_hostEdit, &QLineEdit::textChanged, this, &ConfigWidget::slotSetModified); 0543 connect(m_hostEdit, &QLineEdit::textChanged, this, &ConfigWidget::signalName); 0544 connect(m_hostEdit, &QLineEdit::textChanged, this, &ConfigWidget::slotCheckHost); 0545 l->addWidget(m_hostEdit, row, 1); 0546 w = i18n("Enter the host name of the server."); 0547 label->setWhatsThis(w); 0548 m_hostEdit->setWhatsThis(w); 0549 label->setBuddy(m_hostEdit); 0550 0551 label = new QLabel(i18n("&Port: "), optionsWidget()); 0552 l->addWidget(label, ++row, 0); 0553 m_portSpinBox = new QSpinBox(optionsWidget()); 0554 m_portSpinBox->setMaximum(999999); 0555 m_portSpinBox->setMinimum(0); 0556 m_portSpinBox->setValue(SRU_DEFAULT_PORT); 0557 void (QSpinBox::* valueChanged)(int) = &QSpinBox::valueChanged; 0558 connect(m_portSpinBox, valueChanged, this, &ConfigWidget::slotSetModified); 0559 l->addWidget(m_portSpinBox, row, 1); 0560 w = i18n("Enter the port number of the server. The default is %1.", SRU_DEFAULT_PORT); 0561 label->setWhatsThis(w); 0562 m_portSpinBox->setWhatsThis(w); 0563 label->setBuddy(m_portSpinBox); 0564 0565 label = new QLabel(i18n("Path: "), optionsWidget()); 0566 l->addWidget(label, ++row, 0); 0567 m_pathEdit = new GUI::LineEdit(optionsWidget()); 0568 connect(m_pathEdit, &QLineEdit::textChanged, this, &ConfigWidget::slotSetModified); 0569 l->addWidget(m_pathEdit, row, 1); 0570 w = i18n("Enter the path to the database used by the server."); 0571 label->setWhatsThis(w); 0572 m_pathEdit->setWhatsThis(w); 0573 label->setBuddy(m_pathEdit); 0574 0575 label = new QLabel(i18n("Format: "), optionsWidget()); 0576 l->addWidget(label, ++row, 0); 0577 m_formatCombo = new GUI::ComboBox(optionsWidget()); 0578 m_formatCombo->addItem(QStringLiteral("MODS"), QLatin1String("mods")); 0579 m_formatCombo->addItem(QStringLiteral("MARCXML"), QLatin1String("marcxml")); 0580 m_formatCombo->addItem(QStringLiteral("PAM"), QLatin1String("pam")); 0581 m_formatCombo->addItem(QStringLiteral("Dublin Core"), QLatin1String("dc")); 0582 m_formatCombo->setEditable(true); 0583 connect(m_formatCombo, activatedInt, this, &ConfigWidget::slotSetModified); 0584 connect(m_formatCombo, &QComboBox::editTextChanged, this, &ConfigWidget::slotSetModified); 0585 l->addWidget(m_formatCombo, row, 1); 0586 w = i18n("Enter the result format used by the server."); 0587 label->setWhatsThis(w); 0588 m_formatCombo->setWhatsThis(w); 0589 label->setBuddy(m_formatCombo); 0590 0591 l->setRowStretch(++row, 1); 0592 0593 m_queryTree = new GUI::StringMapWidget(StringMap(), optionsWidget()); 0594 l->addWidget(m_queryTree, row, 0, 1, 2); 0595 m_queryTree->setLabels(i18n("Field"), i18n("Value")); 0596 0597 // now add additional fields widget 0598 addFieldsWidget(SRUFetcher::allOptionalFields(), fetcher_ ? fetcher_->optionalFields() : QStringList()); 0599 0600 if(fetcher_) { 0601 m_schemeCombo->setCurrentText(fetcher_->m_scheme); 0602 m_hostEdit->setText(fetcher_->m_host); 0603 m_portSpinBox->setValue(fetcher_->m_port); 0604 m_pathEdit->setText(fetcher_->m_path); 0605 if(m_formatCombo->findData(fetcher_->m_format) == -1) { 0606 m_formatCombo->addItem(fetcher_->m_format, fetcher_->m_format); 0607 } 0608 m_formatCombo->setCurrentData(fetcher_->m_format); 0609 m_queryTree->setStringMap(fetcher_->m_queryMap); 0610 } 0611 KAcceleratorManager::manage(optionsWidget()); 0612 } 0613 0614 void SRUFetcher::ConfigWidget::saveConfigHook(KConfigGroup& config_) { 0615 QString s = m_hostEdit->text().trimmed(); 0616 if(!s.isEmpty()) { 0617 config_.writeEntry("Host", s); 0618 } 0619 int port = m_portSpinBox->value(); 0620 if(port > 0) { 0621 config_.writeEntry("Port", port); 0622 } 0623 s = m_pathEdit->text().trimmed(); 0624 if(!s.isEmpty()) { 0625 config_.writeEntry("Path", s); 0626 } 0627 s = m_formatCombo->currentData().toString().trimmed(); 0628 if(s.isEmpty()) { 0629 // user-entered format will not have data set for the item. Just use the text itself 0630 s = m_formatCombo->currentText().trimmed(); 0631 } 0632 if(!s.isEmpty()) { 0633 config_.writeEntry("Format", s); 0634 } 0635 s = m_schemeCombo->currentText(); 0636 if(!s.isEmpty()) { 0637 config_.writeEntry("Scheme", s); 0638 } 0639 StringMap queryMap = m_queryTree->stringMap(); 0640 if(!queryMap.isEmpty()) { 0641 config_.writeEntry("QueryFields", queryMap.keys()); 0642 config_.writeEntry("QueryValues", queryMap.values()); 0643 } 0644 } 0645 0646 QString SRUFetcher::ConfigWidget::preferredName() const { 0647 QString s = m_hostEdit->text(); 0648 return s.isEmpty() ? SRUFetcher::defaultName() : s; 0649 } 0650 0651 void SRUFetcher::ConfigWidget::slotCheckHost() { 0652 QString s = m_hostEdit->text(); 0653 // someone might be pasting a full URL, check that 0654 if(s.indexOf(QLatin1Char(':')) > -1 || s.indexOf(QLatin1Char('/')) > -1) { 0655 QUrl u(s); 0656 if(u.isValid()) { 0657 m_hostEdit->setText(u.host()); 0658 if(u.port() > 0) { 0659 m_portSpinBox->setValue(u.port()); 0660 } 0661 if(!u.path().isEmpty()) { 0662 m_pathEdit->setText(u.path().trimmed()); 0663 } 0664 } 0665 } 0666 }