diff --git a/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_edit.cpp b/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_edit.cpp index 38d41c11c7..288f3816bb 100644 --- a/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_edit.cpp +++ b/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_edit.cpp @@ -1,446 +1,482 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2011-03-22 * Description : a Iface C++ interface * * Copyright (C) 2011-2020 by Gilles Caulier * Copyright (C) 2011 by Alexandre Mendes * Copyright (C) 2011 by Hormiere Guillaume * Copyright (C) 2011 by Manuel Campomanes * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "mediawiki_edit.h" // Qt includes #include #include #include #include #include #include #include #include #include #include // Local includes #include "mediawiki_iface.h" #include "mediawiki_queryinfo.h" #include "mediawiki_job_p.h" namespace MediaWiki { class Q_DECL_HIDDEN Result { public: explicit Result() + : m_captchaId(-1) { - m_captchaId = -1; } unsigned int m_captchaId; QVariant m_captchaQuestion; QString m_captchaAnswer; }; class Q_DECL_HIDDEN EditPrivate : public JobPrivate { public: explicit EditPrivate(Iface& MediaWiki) : JobPrivate(MediaWiki) { } static int error(const QString& error) { QString temp = error; int ret = 0; QStringList list; list << QStringLiteral("notext") << QStringLiteral("invalidsection") << QStringLiteral("protectedtitle") << QStringLiteral("cantcreate") << QStringLiteral("cantcreateanon") << QStringLiteral("articleexists") << QStringLiteral("noimageredirectanon") << QStringLiteral("noimageredirect") << QStringLiteral("spamdetected") << QStringLiteral("filtered") << QStringLiteral("contenttoobig") << QStringLiteral("noeditanon") << QStringLiteral("noedit") << QStringLiteral("pagedeleted") << QStringLiteral("emptypage") << QStringLiteral("emptynewsection") << QStringLiteral("editconflict") << QStringLiteral("revwrongpage") << QStringLiteral("undofailure"); ret = list.indexOf(temp.remove(QChar::fromLatin1('-'))); if (ret == -1) { ret = 0; } return ret + (int)Edit::TextMissing ; } QUrl baseUrl; QMap requestParameter; Result result; }; Edit::Edit(Iface& media, QObject* const parent) : Job(*new EditPrivate(media), parent) { } void Edit::setUndoAfter(int undoafter) { Q_D(Edit); d->requestParameter[QStringLiteral("undoafter")] = QString::number(undoafter); } void Edit::setUndo(int undo) { Q_D(Edit); d->requestParameter[QStringLiteral("undo")] = QString::number(undo); } void Edit::setPrependText(const QString& prependText) { Q_D(Edit); d->requestParameter[QStringLiteral("prependtext")] = prependText; d->requestParameter[QStringLiteral("md5")] = QString(); } void Edit::setAppendText(const QString& appendText) { Q_D(Edit); d->requestParameter[QStringLiteral("appendtext")] = appendText; d->requestParameter[QStringLiteral("md5")] = QString(); } void Edit::setPageName(const QString& pageName) { Q_D(Edit); d->requestParameter[QStringLiteral("title")] = pageName; } void Edit::setToken(const QString& token) { Q_D(Edit); d->requestParameter[QStringLiteral("token")] = token; } void Edit::setBaseTimestamp(const QDateTime& baseTimestamp) { Q_D(Edit); d->requestParameter[QStringLiteral("basetimestamp")] = baseTimestamp.toString(QStringLiteral("yyyy-MM-ddThh:mm:ssZ")); } void Edit::setStartTimestamp(const QDateTime& startTimestamp) { Q_D(Edit); d->requestParameter[QStringLiteral("starttimestamp")] = startTimestamp.toString(QStringLiteral("yyyy-MM-ddThh:mm:ssZ")); } void Edit::setText(const QString& text) { Q_D(Edit); d->requestParameter[QStringLiteral("text")] = text; d->requestParameter[QStringLiteral("md5")] = QString(); } void Edit::setRecreate(bool recreate) { Q_D(Edit); if (recreate) { d->requestParameter[QStringLiteral("recreate")] = QStringLiteral("on"); d->requestParameter[QStringLiteral("md5")] = QString(); } } void Edit::setCreateonly(bool createonly) { Q_D(Edit); if (createonly) { d->requestParameter[QStringLiteral("createonly")] = QStringLiteral("on"); d->requestParameter[QStringLiteral("md5")] = QString(); } } void Edit::setNocreate(bool norecreate) { Q_D(Edit); if (norecreate) { d->requestParameter[QStringLiteral("nocreate")] = QStringLiteral("on"); d->requestParameter[QStringLiteral("md5")] = QString(); } } void Edit::setMinor(bool minor) { Q_D(Edit); if (minor) + { d->requestParameter[QStringLiteral("minor")] = QStringLiteral("on"); + } else + { d->requestParameter[QStringLiteral("notminor")] = QStringLiteral("on"); + } } void Edit::setSection(const QString& section) { Q_D(Edit); d->requestParameter[QStringLiteral("section")] = section; } void Edit::setSummary(const QString& summary) { Q_D(Edit); d->requestParameter[QStringLiteral("summary")] = summary; } void Edit::setWatchList(Edit::Watchlist watchlist) { Q_D(Edit); switch (watchlist) { case Edit::watch: d->requestParameter[QStringLiteral("watchlist")] = QString(QStringLiteral("watch")); break; + case Edit::unwatch: d->requestParameter[QStringLiteral("watchlist")] = QString(QStringLiteral("unwatch")); break; + case Edit::nochange: d->requestParameter[QStringLiteral("watchlist")] = QString(QStringLiteral("nochange")); break; + case Edit::preferences: d->requestParameter[QStringLiteral("watchlist")] = QString(QStringLiteral("preferences")); break; } } Edit::~Edit() { } void Edit::start() { Q_D(Edit); QueryInfo* const info = new QueryInfo(d->MediaWiki,this); info->setPageName(d->requestParameter[QStringLiteral("title")]); info->setToken(QStringLiteral("edit")); connect(info, SIGNAL(page(Page)), this, SLOT(doWorkSendRequest(Page))); info->start(); } void Edit::doWorkSendRequest(Page page) { Q_D(Edit); d->requestParameter[QStringLiteral("token")] = page.pageEditToken(); + // Set the url + QUrl url = d->MediaWiki.url(); QUrlQuery query; query.addQueryItem(QStringLiteral("format"), QStringLiteral("xml")); query.addQueryItem(QStringLiteral("action"), QStringLiteral("edit")); // Add params + if (d->requestParameter.contains(QStringLiteral("md5"))) { QString text; if (d->requestParameter.contains(QStringLiteral("prependtext"))) + { text += d->requestParameter[QStringLiteral("prependtext")]; + } if (d->requestParameter.contains(QStringLiteral("appendtext"))) + { text += d->requestParameter[QStringLiteral("appendtext")]; + } if (d->requestParameter.contains(QStringLiteral("text"))) + { text = d->requestParameter[QStringLiteral("text")]; + } QByteArray hash = QCryptographicHash::hash(text.toUtf8(),QCryptographicHash::Md5); d->requestParameter[QStringLiteral("md5")] = QString::fromLatin1(hash.toHex()); } - QMapIterator i(d->requestParameter); + QMapIterator it(d->requestParameter); - while (i.hasNext()) + while (it.hasNext()) { - i.next(); + it.next(); - if (i.key() != QStringLiteral("token")) - query.addQueryItem(i.key(),i.value()); + if (it.key() != QStringLiteral("token")) + { + query.addQueryItem(it.key(), it.value()); + } } QByteArray cookie; QList MediaWikiCookies = d->manager->cookieJar()->cookiesForUrl(d->MediaWiki.url()); for (int i = 0 ; i < MediaWikiCookies.size() ; ++i) { cookie += MediaWikiCookies.at(i).toRawForm(QNetworkCookie::NameAndValueOnly); cookie += ';'; } // Add the token + query.addQueryItem(QStringLiteral("token"), d->requestParameter[QStringLiteral("token")]); url.setQuery(query); d->baseUrl = url; // Set the request + QNetworkRequest request( url ); request.setRawHeader("User-Agent", d->MediaWiki.userAgent().toUtf8()); request.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/x-www-form-urlencoded")); request.setRawHeader("Cookie", cookie); setPercent(25); // Request ready. // Send the request + d->reply = d->manager->post( request, url.toString().toUtf8() ); connectReply(); connect( d->reply, SIGNAL(finished()), this, SLOT(finishedEdit()) ); setPercent(50); // Request sent. } void Edit::finishedEdit() { Q_D(Edit); disconnect(d->reply, SIGNAL(finished()), this, SLOT(finishedEdit())); setPercent(75); // Response received. if (d->reply->error() != QNetworkReply::NoError) { this->setError(this->NetworkError); d->reply->close(); d->reply->deleteLater(); + emitResult(); + return; } QXmlStreamReader reader( d->reply ); while (!reader.atEnd() && !reader.hasError()) { QXmlStreamReader::TokenType token = reader.readNext(); if (token == QXmlStreamReader::StartElement) { QXmlStreamAttributes attrs = reader.attributes(); if (reader.name() == QStringLiteral("edit")) { - if (attrs.value( QStringLiteral("result") ).toString() == QLatin1String("Success")) + if (attrs.value( QStringLiteral("result") ).toString() == QLatin1String("Success")) { setPercent(100); // Response parsed successfully. this->setError(KJob::NoError); d->reply->close(); d->reply->deleteLater(); + emitResult(); + return; } - else if (attrs.value( QStringLiteral("result") ).toString() == QLatin1String("Failure")) + else if (attrs.value(QStringLiteral("result")).toString() == QLatin1String("Failure")) { this->setError(KJob::NoError); reader.readNext(); attrs = reader.attributes(); d->result.m_captchaId = attrs.value( QStringLiteral("id") ).toString().toUInt(); - if (!attrs.value( QStringLiteral("question") ).isEmpty()) - d->result.m_captchaQuestion = QVariant(attrs.value( QStringLiteral("question") ).toString()) ; + if (!attrs.value( QStringLiteral("question") ).isEmpty()) + { + d->result.m_captchaQuestion = QVariant(attrs.value( QStringLiteral("question") ).toString()); + } else if (!attrs.value( QStringLiteral("url") ).isEmpty()) - d->result.m_captchaQuestion = QVariant(attrs.value( QStringLiteral("url") ).toString()) ; + { + d->result.m_captchaQuestion = QVariant(attrs.value( QStringLiteral("url") ).toString()); + } } } else if (reader.name() == QStringLiteral("error")) { this->setError(EditPrivate::error(attrs.value( QStringLiteral("code") ).toString())); d->reply->close(); d->reply->deleteLater(); + emitResult(); + return; } } - else if (token == QXmlStreamReader::Invalid && reader.error() != QXmlStreamReader::PrematureEndOfDocumentError) + else if ((token == QXmlStreamReader::Invalid) && (reader.error() != QXmlStreamReader::PrematureEndOfDocumentError)) { this->setError(this->XmlError); d->reply->close(); d->reply->deleteLater(); + emitResult(); + return; } } d->reply->close(); d->reply->deleteLater(); emit resultCaptcha(d->result.m_captchaQuestion); } void Edit::finishedCaptcha(const QString& captcha) { Q_D(Edit); d->result.m_captchaAnswer = captcha; QUrl url = d->baseUrl; QUrlQuery query; query.addQueryItem(QStringLiteral("CaptchaId"), QString::number(d->result.m_captchaId)); query.addQueryItem(QStringLiteral("CaptchaAnswer"), d->result.m_captchaAnswer); url.setQuery(query); QString data = url.toString(); QByteArray cookie; QList MediaWikiCookies = d->manager->cookieJar()->cookiesForUrl(d->MediaWiki.url()); for (int i = 0 ; i < MediaWikiCookies.size() ; ++i) { cookie += MediaWikiCookies.at(i).toRawForm(QNetworkCookie::NameAndValueOnly); cookie += ';'; } // Set the request + QNetworkRequest request(url); request.setRawHeader("User-Agent", d->MediaWiki.userAgent().toUtf8()); request.setRawHeader("Cookie", cookie); request.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/x-www-form-urlencoded")); + // Send the request + d->reply = d->manager->post(request, data.toUtf8()); connect( d->reply, SIGNAL(finished()), this, SLOT(finishedEdit()) ); } } // namespace MediaWiki diff --git a/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_edit.h b/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_edit.h index 0a374773f3..a1696b6ec2 100644 --- a/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_edit.h +++ b/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_edit.h @@ -1,324 +1,325 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2011-03-22 * Description : a Iface C++ interface * * Copyright (C) 2011-2020 by Gilles Caulier * Copyright (C) 2011 by Alexandre Mendes * Copyright (C) 2011 by Hormiere Guillaume * Copyright (C) 2011 by Manuel Campomanes * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_MEDIAWIKI_EDIT_H #define DIGIKAM_MEDIAWIKI_EDIT_H // Qt includes #include #include #include #include #include // Local includes #include "mediawiki_job.h" #include "mediawiki_queryinfo.h" namespace MediaWiki { class Iface; class EditPrivate; /** * @brief Edit job. * * Uses for create or edit a wiki. */ class Edit : public Job { Q_OBJECT Q_DECLARE_PRIVATE(Edit) public: /** * @brief Indicates all possible error conditions found during the processing of the edit. */ enum { /** * @brief Text is missing. */ TextMissing = Job::UserDefinedError+1, /** * @brief The section is invalid. */ InvalidSection, /** * @brief The page name is protected. */ TitleProtected, /** * @brief The permission for create page is missing. */ CreatePagePermissionMissing, /** * @brief The permission for create page is missing for anonymous. */ AnonymousCreatePagePermissionMissing, /** * @brief The article already exist. */ ArticleDuplication, /** * @brief The permission for create image is missing for anonymous. */ AnonymousCreateImagePermissionMissing, /** * @brief The permission for create image is missing. */ CreateImagePermissionMissing, /** * @brief The MediaWiki considers you are spamming. */ SpamDetected, /** * @brief The MediaWiki refuses your edit. */ Filtered, /** * @brief The size of the article exceed. */ ArticleSizeExceed, /** * @brief The permission for edit page is missing for anonymous. */ AnonymousEditPagePermissionMissing, /** * @brief The permission for edit page is missing. */ EditPagePermissionMissing, /** * @brief The page is deleted. */ PageDeleted, /** * @brief The page is empty. */ EmptyPage, /** * @brief The section is empty. */ EmptySection, /** * @brief Mediwiki detect an edit conflict. */ EditConflict, /** * @brief The revision isn't a valid revision. */ RevWrongPage, /** * @brief The undo failed. */ UndoFailed }; /** * @brief Specify how the watchlist is affected by this edit. */ enum Watchlist { /** * @brief Add the page to the watchlist */ watch, /** * @brief Remove the page from the watchlist */ unwatch, /** * @brief Use the preference settings */ preferences, /** * @brief Don't change the watchlist */ nochange }; public: /** * @brief Constructs an Edit job. * @param parent the QObject parent */ explicit Edit(Iface& media, QObject* const parent = nullptr); + /** * @brief Destroys the Edit job. */ virtual ~Edit(); /** * @brief Starts the job asynchronously. */ void start() Q_DECL_OVERRIDE; /** * @brief Specify how the watchlist is affected by this edit. * @param watchlist Specify how the watchlist is affected by this edit */ void setWatchList(Edit::Watchlist watchlist); /** * @brief If set, suppress errors about the page having been deleted in the meantime and recreate it. * @param recreate if set, suppress errors about the page having been deleted in the meantime and recreate it */ void setRecreate(bool recreate); /** * @brief If set, throw an error if the page already exists. * @param if set, throw an error if the page already exists */ void setCreateonly(bool createonly); /** * @brief If set, throw a missingtitle error if the page doesn't exist. * @param norecreate if set, throw a missingtitle error if the page doesn't exist */ void setNocreate(bool norecreate); /** * @brief If set to true, mark the edit as minor * @param minor if set to true, mark the edit as minor */ void setMinor(bool minor); /** * @brief Set the section. * @param section new section or integer */ void setSection(const QString& section); /** * @brief Set the summary. * @param summary the summary */ void setSummary(const QString& summary); /** * @brief Undo all revisions from undo to this one. If not set, just undo one revision. * @param undoafter if true set the undo after property */ void setUndoAfter(int undoafter); /** * @brief Undo this revision. Overrides text, prependtext and appendtext. * @param undo if true set the undo */ void setUndo(int undo); /** * @brief Set the text added to the beginning of the page. Overrides text. * @param prependText the text added to the beginning of the page */ void setPrependText(const QString& prependText); /** * @brief Set the text added to the end of the page. Overrides text. * @param appendText the text added to the end of the page */ void setAppendText(const QString& appendText); /** * @brief Set the page title. * @param pageName the page title */ void setPageName(const QString& pageName); /** * @brief Set the edit token. Retrieve from QueryInfo. * @param token the edit token */ void setToken(const QString& token); /** * @brief Set the timestamp of the base revision. Leave unset to ignore conflit. * @param baseTimestamp the timestamp of the base revision */ void setBaseTimestamp(const QDateTime& baseTimestamp); /** * @brief Set the timestamp when you obtained the edit token. * @param startTimestamp the timestamp when you obtained the edit token */ void setStartTimestamp(const QDateTime& startTimestamp); /** * @brief Set the page content. * @param text the page content. */ void setText(const QString& text); Q_SIGNALS: /** * @brief Emit the captcha question. * @param captcha the captcha question */ void resultCaptcha(const QVariant& captcha); private Q_SLOTS: void doWorkSendRequest(Page page); void finishedEdit(); public Q_SLOTS: /** * @brief Put the captcha answer. * @param captcha the captcha answer */ void finishedCaptcha(const QString& captcha); }; } // namespace MediaWiki #endif // DIGIKAM_MEDIAWIKI_EDIT_H diff --git a/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_queryinfo.cpp b/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_queryinfo.cpp index c49c0103e7..51300bf1d3 100644 --- a/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_queryinfo.cpp +++ b/core/dplugins/generic/webservices/mediawiki/backend/mediawiki_queryinfo.cpp @@ -1,236 +1,242 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2011-03-22 * Description : a Iface C++ interface * * Copyright (C) 2011-2020 by Gilles Caulier * Copyright (C) 2011 by Alexandre Mendes * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "mediawiki_queryinfo.h" // Qt includes #include #include #include #include #include #include #include #include #include #include // Local includes #include "mediawiki_iface.h" #include "mediawiki_job_p.h" namespace MediaWiki { class Q_DECL_HIDDEN QueryInfoPrivate : public JobPrivate { public: explicit QueryInfoPrivate(Iface& MediaWiki) : JobPrivate(MediaWiki) { } QVector protections; QMap requestParameter; Page page; }; QueryInfo::QueryInfo(Iface& MediaWiki, QObject* const parent) : Job(*new QueryInfoPrivate(MediaWiki), parent) { } QueryInfo::~QueryInfo() { } void QueryInfo::setPageName(const QString& title) { Q_D(QueryInfo); d->requestParameter[QStringLiteral("titles")] = title; } void QueryInfo::setToken(const QString& token) { Q_D(QueryInfo); d->requestParameter[QStringLiteral("intoken")] = token; } void QueryInfo::setPageId(unsigned int id) { Q_D(QueryInfo); d->requestParameter[QStringLiteral("pageids")] = QString::number(id); } void QueryInfo::setRevisionId(unsigned int id) { Q_D(QueryInfo); d->requestParameter[QStringLiteral("revids")] = QString::number(id); } void QueryInfo::start() { QTimer::singleShot(0, this, SLOT(doWorkSendRequest())); } void QueryInfo::doWorkSendRequest() { Q_D(QueryInfo); // Set the url + QUrl url = d->MediaWiki.url(); QUrlQuery query; query.addQueryItem(QStringLiteral("format"), QStringLiteral("xml")); query.addQueryItem(QStringLiteral("action"), QStringLiteral("query")); query.addQueryItem(QStringLiteral("prop"), QStringLiteral("info")); query.addQueryItem(QStringLiteral("inprop"), QStringLiteral("protection|talkid|watched|subjectid|url|readable|preload")); - QMapIterator i(d->requestParameter); + QMapIterator it(d->requestParameter); - while (i.hasNext()) + while (it.hasNext()) { - i.next(); - query.addQueryItem(i.key(), i.value()); + it.next(); + query.addQueryItem(it.key(), it.value()); } + url.setQuery(query); // Set the request + QNetworkRequest request(url); request.setRawHeader("User-Agent", d->MediaWiki.userAgent().toUtf8()); QByteArray cookie = ""; QList MediaWikiCookies = d->manager->cookieJar()->cookiesForUrl(d->MediaWiki.url()); for (int i = 0 ; i < MediaWikiCookies.size() ; ++i) { cookie += MediaWikiCookies.at(i).toRawForm(QNetworkCookie::NameAndValueOnly); cookie += ';'; } + request.setRawHeader( "Cookie", cookie ); // Send the request + d->reply = d->manager->get(request); connectReply(); connect(d->reply, SIGNAL(finished()), this, SLOT(doWorkProcessReply())); } void QueryInfo::doWorkProcessReply() { Q_D(QueryInfo); disconnect(d->reply, SIGNAL(finished()), this, SLOT(doWorkProcessReply())); if (d->reply->error() == QNetworkReply::NoError) { // Replace & in & + QString content = QString::fromUtf8(d->reply->readAll()); QRegExp regex(QStringLiteral("&(?!\\w+;)")); content.replace(regex, QStringLiteral("&")); QXmlStreamReader reader(content); QVector protect; while (!reader.atEnd() && !reader.hasError()) { QXmlStreamReader::TokenType token = reader.readNext(); QXmlStreamAttributes attrs = reader.attributes(); if (token == QXmlStreamReader::StartElement) { - if (reader.name() == QLatin1String("page")) + if (reader.name() == QLatin1String("page")) { d->page.setPageId(attrs.value(QStringLiteral("pageid")).toString().toUInt()); d->page.setTitle(attrs.value(QStringLiteral("title")).toString()); d->page.setNs(attrs.value(QStringLiteral("ns")).toString().toUInt()); d->page.setTouched(QDateTime::fromString(attrs.value(QStringLiteral("touched")).toString(), QStringLiteral("yyyy'-'MM'-'dd'T'hh':'mm':'ss'Z'"))); d->page.setLastRevId(attrs.value(QStringLiteral("lastrevid")).toString().toUInt()); d->page.setCounter(attrs.value(QStringLiteral("counter")).toString().toUInt()); d->page.setLength(attrs.value(QStringLiteral("length")).toString().toUInt()); d->page.setStarttimestamp(QDateTime::fromString(attrs.value(QStringLiteral("starttimestamp")).toString(), QStringLiteral("yyyy'-'MM'-'dd'T'hh':'mm':'ss'Z'"))); d->page.setEditToken(attrs.value(QStringLiteral("edittoken")).toString()); d->page.setTalkid(attrs.value(QStringLiteral("talkid")).toString().toUInt()); d->page.setFullurl(QUrl(attrs.value(QStringLiteral("fullurl")).toString())); d->page.setEditurl(QUrl(attrs.value(QStringLiteral("editurl")).toString())); d->page.setReadable(attrs.value(QStringLiteral("readable")).toString()); d->page.setPreload(attrs.value(QStringLiteral("preload")).toString()); } else if (reader.name() == QLatin1String("protection")) { protect.clear(); } else if (reader.name() == QLatin1String("pr")) { QString expiry(attrs.value(QStringLiteral("expiry")).toString()); QString level(attrs.value(QStringLiteral("level")).toString()); QString type(attrs.value(QStringLiteral("type")).toString()); QString source; - if (!attrs.value(QStringLiteral("source")).toString().isEmpty()) + if (!attrs.value(QStringLiteral("source")).toString().isEmpty()) { source = attrs.value(QStringLiteral("source")).toString(); } else if (!attrs.value(QStringLiteral("cascade")).toString().isEmpty()) { source = attrs.value(QStringLiteral("cascade")).toString(); } Protection p; p.setExpiry(expiry); p.setLevel(level); p.setType(type); p.setSource(source); protect.push_back(p); } } else if (token == QXmlStreamReader::EndElement) { if (reader.name() == QLatin1String("page")) { d->protections = protect; } } } if (!reader.hasError()) { setError(KJob::NoError); emit protection(protect); emit page(d->page); } else { setError(Job::XmlError); } } else { setError(Job::NetworkError); } emitResult(); } } // namespace MediaWiki