1 /***************************************************************************
2 Copyright (C) 2003-2009 Robby Stephenson <robby@periapsis.org>
3 ***************************************************************************/
4
5 /***************************************************************************
6 * *
7 * This program is free software; you can redistribute it and/or *
8 * modify it under the terms of the GNU General Public License as *
9 * published by the Free Software Foundation; either version 2 of *
10 * the License or (at your option) version 3 or any later version *
11 * accepted by the membership of KDE e.V. (or its successor approved *
12 * by the membership of KDE e.V.), which shall act as a proxy *
13 * defined in Section 14 of version 3 of the license. *
14 * *
15 * This program is distributed in the hope that it will be useful, *
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18 * GNU General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU General Public License *
21 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
22 * *
23 ***************************************************************************/
24
25 #include "srufetcher.h"
26 #include "../fieldformat.h"
27 #include "../collection.h"
28 #include "../translators/tellico_xml.h"
29 #include "../translators/xslthandler.h"
30 #include "../translators/tellicoimporter.h"
31 #include "../utils/guiproxy.h"
32 #include "../gui/lineedit.h"
33 #include "../gui/combobox.h"
34 #include "../gui/stringmapwidget.h"
35 #include "../utils/string_utils.h"
36 #include "../utils/lccnvalidator.h"
37 #include "../utils/isbnvalidator.h"
38 #include "../utils/datafileregistry.h"
39 #include "../tellico_debug.h"
40
41 #include <KLocalizedString>
42 #include <KIO/Job>
43 #include <KJobUiDelegate>
44 #include <KJobWidgets/KJobWidgets>
45 #include <KConfigGroup>
46 #include <KComboBox>
47 #include <KAcceleratorManager>
48
49 #include <QSpinBox>
50 #include <QLabel>
51 #include <QGridLayout>
52 #include <QFile>
53 #include <QUrlQuery>
54 #include <QDomDocument>
55
56 namespace {
57 // 7090 was the old default port, but that was just because LoC used it
58 // let's use default HTTP port of 80 now
59 static const int SRU_DEFAULT_PORT = 80;
60 static const int SRU_MAX_RECORDS = 25;
61 }
62
63 using namespace Tellico;
64 using Tellico::Fetch::SRUFetcher;
65
SRUFetcher(QObject * parent_)66 SRUFetcher::SRUFetcher(QObject* parent_)
67 : Fetcher(parent_), m_port(SRU_DEFAULT_PORT), m_job(nullptr), m_MARCXMLHandler(nullptr), m_MODSHandler(nullptr), m_SRWHandler(nullptr), m_started(false) {
68 }
69
SRUFetcher(const QString & name_,const QString & host_,uint port_,const QString & path_,const QString & format_,QObject * parent_)70 SRUFetcher::SRUFetcher(const QString& name_, const QString& host_, uint port_, const QString& path_,
71 const QString& format_, QObject* parent_) : Fetcher(parent_),
72 m_host(host_), m_port(port_), m_path(path_), m_format(format_),
73 m_job(nullptr), m_MARCXMLHandler(nullptr), m_MODSHandler(nullptr), m_SRWHandler(nullptr), m_started(false) {
74 m_name = name_; // m_name is protected in super class
75 if(!m_path.startsWith(QLatin1Char('/'))) {
76 m_path.prepend(QLatin1Char('/'));
77 }
78 }
79
~SRUFetcher()80 SRUFetcher::~SRUFetcher() {
81 delete m_MARCXMLHandler;
82 m_MARCXMLHandler = nullptr;
83 delete m_MODSHandler;
84 m_MODSHandler = nullptr;
85 delete m_SRWHandler;
86 m_SRWHandler = nullptr;
87 }
88
source() const89 QString SRUFetcher::source() const {
90 return m_name.isEmpty() ? defaultName() : m_name;
91 }
92
93 // No Raw for now.
canSearch(Fetch::FetchKey k) const94 bool SRUFetcher::canSearch(Fetch::FetchKey k) const {
95 return k == Title || k == Person || k == ISBN || k == Keyword || k == LCCN;
96 }
97
canFetch(int type) const98 bool SRUFetcher::canFetch(int type) const {
99 return type == Data::Collection::Book || type == Data::Collection::Bibtex;
100 }
101
readConfigHook(const KConfigGroup & config_)102 void SRUFetcher::readConfigHook(const KConfigGroup& config_) {
103 m_host = config_.readEntry("Host");
104 int p = config_.readEntry("Port", SRU_DEFAULT_PORT);
105 if(p > 0) {
106 m_port = p;
107 }
108 m_path = config_.readEntry("Path");
109 // used to be called Database
110 if(m_path.isEmpty()) {
111 m_path = config_.readEntry("Database");
112 }
113 if(!m_path.startsWith(QLatin1Char('/'))) {
114 m_path.prepend(QLatin1Char('/'));
115 }
116 m_format = config_.readEntry("Format", "mods");
117 const QStringList queryFields = config_.readEntry("QueryFields", QStringList());
118 const QStringList queryValues = config_.readEntry("QueryValues", QStringList());
119 Q_ASSERT(queryFields.count() == queryValues.count());
120 for(int i = 0; i < qMin(queryFields.count(), queryValues.count()); ++i) {
121 m_queryMap.insert(queryFields.at(i), queryValues.at(i));
122 }
123 }
124
search()125 void SRUFetcher::search() {
126 m_started = true;
127 if(m_host.isEmpty() || m_path.isEmpty() || m_format.isEmpty()) {
128 myDebug() << "settings are not set!";
129 stop();
130 return;
131 }
132
133 QUrl u;
134 u.setScheme(QStringLiteral("http"));
135 u.setHost(m_host);
136 u.setPort(m_port);
137 u = QUrl::fromUserInput(u.url() + m_path);
138
139 QUrlQuery query;
140 for(StringMap::ConstIterator it = m_queryMap.constBegin(); it != m_queryMap.constEnd(); ++it) {
141 query.addQueryItem(it.key(), it.value());
142 }
143 // allow user to override these so check for existing item first
144 if(!query.hasQueryItem(QStringLiteral("operation"))) {
145 query.addQueryItem(QStringLiteral("operation"), QStringLiteral("searchRetrieve"));
146 }
147 if(!query.hasQueryItem(QStringLiteral("version"))) {
148 query.addQueryItem(QStringLiteral("version"), QStringLiteral("1.1"));
149 }
150 if(!query.hasQueryItem(QStringLiteral("maximumRecords"))) {
151 query.addQueryItem(QStringLiteral("maximumRecords"), QString::number(SRU_MAX_RECORDS));
152 }
153 if(!m_format.isEmpty() && m_format != QLatin1String("none")
154 && !query.hasQueryItem(QStringLiteral("recordSchema"))) {
155 query.addQueryItem(QStringLiteral("recordSchema"), m_format);
156 }
157
158 const int type = collectionType();
159 QString str = QLatin1Char('"') + request().value() + QLatin1Char('"');
160 switch(request().key()) {
161 case Title:
162 query.addQueryItem(QStringLiteral("query"), QLatin1String("dc.title=") + str);
163 break;
164
165 case Person:
166 {
167 QString s;
168 if(type == Data::Collection::Book || type == Data::Collection::Bibtex) {
169 s = QLatin1String("author=") + str + QLatin1String(" or dc.author=") + str;
170 } else {
171 s = QLatin1String("dc.creator=") + str + QLatin1String(" or dc.editor=") + str;
172 }
173 query.addQueryItem(QStringLiteral("query"), s);
174 }
175 break;
176
177 case ISBN:
178 {
179 QString s = request().value();
180 s.remove(QLatin1Char('-'));
181 QStringList isbnList = FieldFormat::splitValue(s);
182 // also search for isbn10 values
183 for(QStringList::Iterator it = isbnList.begin(); it != isbnList.end(); ++it) {
184 if((*it).startsWith(QLatin1String("978"))) {
185 QString isbn10 = ISBNValidator::isbn10(*it);
186 isbn10.remove(QLatin1Char('-'));
187 it = isbnList.insert(it, isbn10);
188 ++it;
189 }
190 }
191 QString q;
192 for(int i = 0; i < isbnList.count(); ++i) {
193 // make an assumption that DC output uses the dc profile and everything else uses Bath for ISBN
194 // no idea if this holds true universally, but matches LOC, COPAC, and KB
195 if(m_format == QLatin1String("dc")) {
196 q += QLatin1String("dc.identifier=") + isbnList.at(i);
197 } else {
198 q += QLatin1String("bath.isbn=") + isbnList.at(i);
199 }
200 if(i < isbnList.count()-1) {
201 q += QLatin1String(" or ");
202 }
203 }
204 query.addQueryItem(QStringLiteral("query"), q);
205 }
206 break;
207
208 case LCCN:
209 {
210 QString s = request().value();
211 QStringList lccnList = FieldFormat::splitValue(s);
212 QString q;
213 for(int i = 0; i < lccnList.count(); ++i) {
214 q += QLatin1String("bath.lccn=") + lccnList.at(i);
215 q += QLatin1String(" or bath.lccn=") + LCCNValidator::formalize(lccnList.at(i));
216 if(i < lccnList.count()-1) {
217 q += QLatin1String(" or ");
218 }
219 }
220 query.addQueryItem(QStringLiteral("query"), q);
221 }
222 break;
223
224 case Keyword:
225 query.addQueryItem(QStringLiteral("query"), str);
226 break;
227
228 case Raw:
229 {
230 QString key = request().value().section(QLatin1Char('='), 0, 0).trimmed();
231 QString str = request().value().section(QLatin1Char('='), 1).trimmed();
232 query.addQueryItem(key, str);
233 }
234 break;
235
236 default:
237 myWarning() << "key not recognized: " << request().key();
238 stop();
239 break;
240 }
241 u.setQuery(query);
242 // myDebug() << u.url();
243
244 m_job = KIO::storedGet(u, KIO::NoReload, KIO::HideProgressInfo);
245 KJobWidgets::setWindow(m_job, GUI::Proxy::widget());
246 connect(m_job.data(), &KJob::result,
247 this, &SRUFetcher::slotComplete);
248 }
249
stop()250 void SRUFetcher::stop() {
251 if(!m_started) {
252 return;
253 }
254 if(m_job) {
255 m_job->kill();
256 m_job = nullptr;
257 }
258
259 m_started = false;
260 emit signalDone(this);
261 }
262
slotComplete(KJob *)263 void SRUFetcher::slotComplete(KJob*) {
264 if(m_job->error()) {
265 m_job->uiDelegate()->showErrorMessage();
266 stop();
267 return;
268 }
269
270 QByteArray data = m_job->data();
271 if(data.isEmpty()) {
272 stop();
273 return;
274 }
275 // see bug 319662. If fetcher is cancelled, job is killed
276 // if the pointer is retained, it gets double-deleted
277 m_job = nullptr;
278
279 #if 0
280 myWarning() << "Remove debug from srufetcher.cpp";
281 QFile f(QString::fromLatin1("/tmp/test.xml"));
282 if(f.open(QIODevice::WriteOnly)) {
283 QTextStream t(&f);
284 t.setCodec("UTF-8");
285 t << data;
286 }
287 f.close();
288 #endif
289
290 Data::CollPtr coll;
291 QString msg;
292
293 const QString result = QString::fromUtf8(data.constData(), data.size());
294
295 // first check for SRU errors
296 QDomDocument dom;
297 if(!dom.setContent(result, true /*namespace*/)) {
298 myWarning() << "server did not return valid XML.";
299 stop();
300 return;
301 }
302
303 const QString& diag = XML::nsZingDiag;
304 QDomNodeList diagList = dom.elementsByTagNameNS(diag, QStringLiteral("diagnostic"));
305 for(int i = 0; i < diagList.count(); ++i) {
306 QDomElement elem = diagList.item(i).toElement();
307 QDomNodeList nodeList1 = elem.elementsByTagNameNS(diag, QStringLiteral("message"));
308 QDomNodeList nodeList2 = elem.elementsByTagNameNS(diag, QStringLiteral("details"));
309 for(int j = 0; j < nodeList1.count(); ++j) {
310 QString d = nodeList1.item(j).toElement().text();
311 if(!d.isEmpty()) {
312 QString d2 = nodeList2.item(j).toElement().text();
313 if(!d2.isEmpty()) {
314 d += QLatin1String(" (") + d2 + QLatin1Char(')');
315 }
316 myDebug() << "[" << m_host << "/" << m_path << "]" << d;
317 if(!msg.isEmpty()) {
318 msg += QLatin1Char('\n');
319 }
320 msg += d;
321 }
322 }
323 }
324
325 QString modsResult;
326 if(m_format == QLatin1String("mods")) {
327 modsResult = result;
328 // } else if(m_format == QLatin1String("marcxml") && initMARCXMLHandler()) {
329 // some SRU data sources call it MARC21-xml or something other than marcxml
330 } else if(m_format.startsWith(QLatin1String("marc"), Qt::CaseInsensitive) && initMARCXMLHandler()) {
331 // brute force marcxchange conversion. This is probably wrong at some level
332 QString newResult = result;
333 if(m_format.startsWith(QLatin1String("marcxchange"), Qt::CaseInsensitive)) {
334 newResult.replace(QRegularExpression(QLatin1String("xmlns:marc=\"info:lc/xmlns/marcxchange-v[12]\"")),
335 QStringLiteral("xmlns:marc=\"http://www.loc.gov/MARC21/slim\""));
336 }
337 modsResult = m_MARCXMLHandler->applyStylesheet(newResult);
338 }
339 if(!modsResult.isEmpty() && initMODSHandler()) {
340 Import::TellicoImporter imp(m_MODSHandler->applyStylesheet(modsResult));
341 coll = imp.collection();
342 if(!msg.isEmpty()) {
343 msg += QLatin1Char('\n');
344 }
345 msg += imp.statusMessage();
346 } else if((m_format == QLatin1String("pam") ||
347 m_format == QLatin1String("dc") ||
348 m_format == QLatin1String("none")) &&
349 initSRWHandler()) {
350 Import::TellicoImporter imp(m_SRWHandler->applyStylesheet(result));
351 coll = imp.collection();
352 if(!msg.isEmpty()) {
353 msg += QLatin1Char('\n');
354 }
355 msg += imp.statusMessage();
356 } else {
357 myDebug() << "unrecognized format:" << m_format;
358 stop();
359 return;
360 }
361
362 if(coll && !msg.isEmpty()) {
363 message(msg, coll->entryCount() == 0 ? MessageHandler::Warning : MessageHandler::Status);
364 }
365
366 if(!coll) {
367 myDebug() << "no collection pointer";
368 if(!msg.isEmpty()) {
369 message(msg, MessageHandler::Error);
370 }
371 stop();
372 return;
373 }
374
375 // since the Dewey and LoC field titles have a context in their i18n call here
376 // but not in the stylesheet where the field is actually created
377 // update the field titles here
378 QHashIterator<QString, QString> i(allOptionalFields());
379 while(i.hasNext()) {
380 i.next();
381 Data::FieldPtr field = coll->fieldByName(i.key());
382 if(field) {
383 field->setTitle(i.value());
384 coll->modifyField(field);
385 }
386 }
387
388 foreach(Data::EntryPtr entry, coll->entries()) {
389 FetchResult* r = new FetchResult(this, entry);
390 m_entries.insert(r->uid, entry);
391 emit signalResultFound(r);
392 }
393 stop();
394 }
395
fetchEntryHook(uint uid_)396 Tellico::Data::EntryPtr SRUFetcher::fetchEntryHook(uint uid_) {
397 return m_entries[uid_];
398 }
399
updateRequest(Data::EntryPtr entry_)400 Tellico::Fetch::FetchRequest SRUFetcher::updateRequest(Data::EntryPtr entry_) {
401 // myDebug() << source() << ": " << entry_->title();
402 QString isbn = entry_->field(QStringLiteral("isbn"));
403 if(!isbn.isEmpty()) {
404 return FetchRequest(Fetch::ISBN, isbn);
405 }
406
407 QString lccn = entry_->field(QStringLiteral("lccn"));
408 if(!lccn.isEmpty()) {
409 return FetchRequest(Fetch::LCCN, lccn);
410 }
411
412 // optimistically try searching for title and rely on Collection::sameEntry() to figure things out
413 QString t = entry_->field(QStringLiteral("title"));
414 if(!t.isEmpty()) {
415 return FetchRequest(Fetch::Title, t);
416 }
417 return FetchRequest();
418 }
419
initMARCXMLHandler()420 bool SRUFetcher::initMARCXMLHandler() {
421 if(m_MARCXMLHandler) {
422 return true;
423 }
424
425 QString xsltfile = DataFileRegistry::self()->locate(QStringLiteral("MARC21slim2MODS3.xsl"));
426 if(xsltfile.isEmpty()) {
427 myWarning() << "can not locate MARC21slim2MODS3.xsl.";
428 return false;
429 }
430
431 QUrl u = QUrl::fromLocalFile(xsltfile);
432
433 m_MARCXMLHandler = new XSLTHandler(u);
434 if(!m_MARCXMLHandler->isValid()) {
435 myWarning() << "error in MARC21slim2MODS3.xsl.";
436 delete m_MARCXMLHandler;
437 m_MARCXMLHandler = nullptr;
438 return false;
439 }
440 return true;
441 }
442
initMODSHandler()443 bool SRUFetcher::initMODSHandler() {
444 if(m_MODSHandler) {
445 return true;
446 }
447
448 QString xsltfile = DataFileRegistry::self()->locate(QStringLiteral("mods2tellico.xsl"));
449 if(xsltfile.isEmpty()) {
450 myWarning() << "can not locate mods2tellico.xsl.";
451 return false;
452 }
453
454 QUrl u = QUrl::fromLocalFile(xsltfile);
455
456 m_MODSHandler = new XSLTHandler(u);
457 if(!m_MODSHandler->isValid()) {
458 myWarning() << "error in mods2tellico.xsl.";
459 delete m_MODSHandler;
460 m_MODSHandler = nullptr;
461 return false;
462 }
463 return true;
464 }
465
initSRWHandler()466 bool SRUFetcher::initSRWHandler() {
467 if(m_SRWHandler) {
468 return true;
469 }
470
471 QString xsltfile = DataFileRegistry::self()->locate(QStringLiteral("srw2tellico.xsl"));
472 if(xsltfile.isEmpty()) {
473 myWarning() << "can not locate srw2tellico.xsl.";
474 return false;
475 }
476
477 QUrl u = QUrl::fromLocalFile(xsltfile);
478
479 m_SRWHandler = new XSLTHandler(u);
480 if(!m_SRWHandler->isValid()) {
481 myWarning() << "error in srw2tellico.xsl.";
482 delete m_SRWHandler;
483 m_SRWHandler = nullptr;
484 return false;
485 }
486 return true;
487 }
488
libraryOfCongress(QObject * parent_)489 Tellico::Fetch::Fetcher::Ptr SRUFetcher::libraryOfCongress(QObject* parent_) {
490 return Fetcher::Ptr(new SRUFetcher(i18n("Library of Congress (US)"), QStringLiteral("z3950.loc.gov"), 7090,
491 QStringLiteral("voyager"), QStringLiteral("mods"), parent_));
492 }
493
defaultName()494 QString SRUFetcher::defaultName() {
495 return i18n("SRU Server");
496 }
497
defaultIcon()498 QString SRUFetcher::defaultIcon() {
499 // return QLatin1String("network-workgroup"); // just to be different than z3950
500 return QStringLiteral(":/icons/sru");
501 }
502
503 // static
allOptionalFields()504 Tellico::StringHash SRUFetcher::allOptionalFields() {
505 StringHash hash;
506 hash[QStringLiteral("address")] = i18n("Address");
507 hash[QStringLiteral("abstract")] = i18n("Abstract");
508 hash[QStringLiteral("dewey")] = i18nc("Dewey Decimal classification system", "Dewey Decimal");
509 hash[QStringLiteral("lcc")] = i18nc("Library of Congress classification system", "LoC Classification");
510 return hash;
511 }
512
configWidget(QWidget * parent_) const513 Tellico::Fetch::ConfigWidget* SRUFetcher::configWidget(QWidget* parent_) const {
514 return new ConfigWidget(parent_, this);
515 }
516
ConfigWidget(QWidget * parent_,const SRUFetcher * fetcher_)517 SRUFetcher::ConfigWidget::ConfigWidget(QWidget* parent_, const SRUFetcher* fetcher_ /*=0*/)
518 : Fetch::ConfigWidget(parent_) {
519 QGridLayout* l = new QGridLayout(optionsWidget());
520 l->setSpacing(4);
521 l->setColumnStretch(1, 10);
522
523 int row = -1;
524 QLabel* label = new QLabel(i18n("Hos&t: "), optionsWidget());
525 l->addWidget(label, ++row, 0);
526 m_hostEdit = new GUI::LineEdit(optionsWidget());
527 connect(m_hostEdit, &QLineEdit::textChanged, this, &ConfigWidget::slotSetModified);
528 connect(m_hostEdit, &QLineEdit::textChanged, this, &ConfigWidget::signalName);
529 connect(m_hostEdit, &QLineEdit::textChanged, this, &ConfigWidget::slotCheckHost);
530 l->addWidget(m_hostEdit, row, 1);
531 QString w = i18n("Enter the host name of the server.");
532 label->setWhatsThis(w);
533 m_hostEdit->setWhatsThis(w);
534 label->setBuddy(m_hostEdit);
535
536 label = new QLabel(i18n("&Port: "), optionsWidget());
537 l->addWidget(label, ++row, 0);
538 m_portSpinBox = new QSpinBox(optionsWidget());
539 m_portSpinBox->setMaximum(999999);
540 m_portSpinBox->setMinimum(0);
541 m_portSpinBox->setValue(SRU_DEFAULT_PORT);
542 void (QSpinBox::* valueChanged)(int) = &QSpinBox::valueChanged;
543 connect(m_portSpinBox, valueChanged, this, &ConfigWidget::slotSetModified);
544 l->addWidget(m_portSpinBox, row, 1);
545 w = i18n("Enter the port number of the server. The default is %1.", SRU_DEFAULT_PORT);
546 label->setWhatsThis(w);
547 m_portSpinBox->setWhatsThis(w);
548 label->setBuddy(m_portSpinBox);
549
550 label = new QLabel(i18n("Path: "), optionsWidget());
551 l->addWidget(label, ++row, 0);
552 m_pathEdit = new GUI::LineEdit(optionsWidget());
553 connect(m_pathEdit, &QLineEdit::textChanged, this, &ConfigWidget::slotSetModified);
554 l->addWidget(m_pathEdit, row, 1);
555 w = i18n("Enter the path to the database used by the server.");
556 label->setWhatsThis(w);
557 m_pathEdit->setWhatsThis(w);
558 label->setBuddy(m_pathEdit);
559
560 label = new QLabel(i18n("Format: "), optionsWidget());
561 l->addWidget(label, ++row, 0);
562 m_formatCombo = new GUI::ComboBox(optionsWidget());
563 m_formatCombo->addItem(QStringLiteral("MODS"), QLatin1String("mods"));
564 m_formatCombo->addItem(QStringLiteral("MARCXML"), QLatin1String("marcxml"));
565 m_formatCombo->addItem(QStringLiteral("PAM"), QLatin1String("pam"));
566 m_formatCombo->addItem(QStringLiteral("Dublin Core"), QLatin1String("dc"));
567 m_formatCombo->setEditable(true);
568 void (GUI::ComboBox::* activatedInt)(int) = &GUI::ComboBox::activated;
569 connect(m_formatCombo, activatedInt, this, &ConfigWidget::slotSetModified);
570 connect(m_formatCombo, &QComboBox::editTextChanged, this, &ConfigWidget::slotSetModified);
571 l->addWidget(m_formatCombo, row, 1);
572 w = i18n("Enter the result format used by the server.");
573 label->setWhatsThis(w);
574 m_formatCombo->setWhatsThis(w);
575 label->setBuddy(m_formatCombo);
576
577 l->setRowStretch(++row, 1);
578
579 m_queryTree = new GUI::StringMapWidget(StringMap(), optionsWidget());
580 l->addWidget(m_queryTree, row, 0, 1, 2);
581 m_queryTree->setLabels(i18n("Field"), i18n("Value"));
582
583 // now add additional fields widget
584 addFieldsWidget(SRUFetcher::allOptionalFields(), fetcher_ ? fetcher_->optionalFields() : QStringList());
585
586 if(fetcher_) {
587 m_hostEdit->setText(fetcher_->m_host);
588 m_portSpinBox->setValue(fetcher_->m_port);
589 m_pathEdit->setText(fetcher_->m_path);
590 if(m_formatCombo->findData(fetcher_->m_format) == -1) {
591 m_formatCombo->addItem(fetcher_->m_format, fetcher_->m_format);
592 }
593 m_formatCombo->setCurrentData(fetcher_->m_format);
594 m_queryTree->setStringMap(fetcher_->m_queryMap);
595 }
596 KAcceleratorManager::manage(optionsWidget());
597 }
598
saveConfigHook(KConfigGroup & config_)599 void SRUFetcher::ConfigWidget::saveConfigHook(KConfigGroup& config_) {
600 QString s = m_hostEdit->text().trimmed();
601 if(!s.isEmpty()) {
602 config_.writeEntry("Host", s);
603 }
604 int port = m_portSpinBox->value();
605 if(port > 0) {
606 config_.writeEntry("Port", port);
607 }
608 s = m_pathEdit->text().trimmed();
609 if(!s.isEmpty()) {
610 config_.writeEntry("Path", s);
611 }
612 s = m_formatCombo->currentData().toString().trimmed();
613 if(s.isEmpty()) {
614 // user-entered format will not have data set for the item. Just use the text itself
615 s = m_formatCombo->currentText().trimmed();
616 }
617 if(!s.isEmpty()) {
618 config_.writeEntry("Format", s);
619 }
620 StringMap queryMap = m_queryTree->stringMap();
621 if(!queryMap.isEmpty()) {
622 config_.writeEntry("QueryFields", queryMap.keys());
623 config_.writeEntry("QueryValues", queryMap.values());
624 }
625 }
626
preferredName() const627 QString SRUFetcher::ConfigWidget::preferredName() const {
628 QString s = m_hostEdit->text();
629 return s.isEmpty() ? SRUFetcher::defaultName() : s;
630 }
631
slotCheckHost()632 void SRUFetcher::ConfigWidget::slotCheckHost() {
633 QString s = m_hostEdit->text();
634 // someone might be pasting a full URL, check that
635 if(s.indexOf(QLatin1Char(':')) > -1 || s.indexOf(QLatin1Char('/')) > -1) {
636 QUrl u(s);
637 if(u.isValid()) {
638 m_hostEdit->setText(u.host());
639 if(u.port() > 0) {
640 m_portSpinBox->setValue(u.port());
641 }
642 if(!u.path().isEmpty()) {
643 m_pathEdit->setText(u.path().trimmed());
644 }
645 }
646 }
647 }
648