Commit 985acef6 authored by Daniel Vrátil's avatar Daniel Vrátil 🤖

Blogilo: Move Blogger API implementation from KBlog to Blogilo

The Blogger API implementation requires LibKGAPI, but LibKGAPI depends
on kdepimlibs. To avoid circular dependency, the Blogger API is temporarily
moved from kdepimlibs' KBlog directly to Blogilo, which requires the
Blogger API.

The code can be moved back to KBlog in Frameworks 5, when the dependency
circle between LibKGAPI and kdepimlibs frameworks will be broken.

REVIEW: 119196
parent d508f611
......@@ -100,7 +100,7 @@ set(CMAKE_AUTOMOC ON)
find_package(KDE4 4.12.0 REQUIRED)
include(KDE4Defaults)
find_package(KdepimLibs 4.13.80)
find_package(KdepimLibs 4.13.81)
set_package_properties(KdepimLibs PROPERTIES DESCRIPTION "The KDEPIM libraries" URL "http://www.kde.org" TYPE REQUIRED)
############### Load the CTest options ###############
......
......@@ -2,7 +2,8 @@
include_directories( ${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_SOURCE_DIR}/libkdepim
${CMAKE_SOURCE_DIR}/pimcommon)
${CMAKE_SOURCE_DIR}/pimcommon
${LibKGAPI2_INCLUDE_DIR} )
set(blogilo_SRCS
main.cpp
......@@ -34,6 +35,11 @@ set(blogilo_SRCS
composer/blogilocomposerwidget.cpp
storageservice/storageservicemanagersettingsjob.cpp
configurestorageservicewidget.cpp
# Blogger API implementation imported from KBlog due to circular deps between
# kdepimlibs and libkgapi
# TODO: Move back to KBlog in KF5
blogger.cpp
)
......@@ -55,7 +61,7 @@ kde4_add_kcfg_files( blogilo_SRCS settings.kcfgc)
kde4_add_app_icon(blogilo_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/../icons/hi*-app-blogilo.png")
kde4_add_executable( blogilo ${blogilo_SRCS})
target_link_libraries( blogilo kdepim ${KDE4_KDEUI_LIBS} ${QT_QTSQL_LIBRARY} ${KDE4_KTEXTEDITOR_LIBS} ${QT_QTWEBKIT_LIBRARY} ${KDE4_KDEWEBKIT_LIBRARY} ${KDEPIMLIBS_KBLOG_LIBS} composereditorng pimcommon)
target_link_libraries( blogilo kdepim ${KDE4_KDEUI_LIBS} ${QT_QTSQL_LIBRARY} ${KDE4_KTEXTEDITOR_LIBS} ${QT_QTWEBKIT_LIBRARY} ${KDE4_KDEWEBKIT_LIBRARY} ${KDEPIMLIBS_KBLOG_LIBS} ${LibKGAPI2_LIBRARY} composereditorng pimcommon)
install( TARGETS blogilo ${INSTALL_TARGETS_DEFAULT_ARGS} )
......
......@@ -31,7 +31,7 @@
#include <kblog/metaweblog.h>
#include <kblog/movabletype.h>
#include <kblog/wordpressbuggy.h>
#include <kblog/blogger.h>
#include "blogger.h"
#include <kurl.h>
#include <kmessagebox.h>
#include <kdebug.h>
......
......@@ -35,7 +35,7 @@
#include <kblog/movabletype.h>
#include <kblog/wordpressbuggy.h>
#include <kblog/blogmedia.h>
#include <kblog/blogger.h>
#include "blogger.h"
#include <kdebug.h>
#include <KDE/KLocale>
......
......@@ -25,7 +25,7 @@
#include "bilboblog.h"
#include "dbman.h"
#include <kblog/wordpressbuggy.h>
#include <kblog/blogger.h>
#include "blogger.h"
#include <KDebug>
#include <QApplication>
......
/*
This file is part of the kblog library.
Copyright (c) 2006-2007 Christian Weilbach <christian_weilbach@web.de>
Copyright (c) 2007 Mike McQuaid <mike@mikemcquaid.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef BLOG_P_H
#define BLOG_P_H
#include <kblog/blog.h>
#include <KTimeZone>
#include <KUrl>
#include <kxmlrpcclient/client.h>
namespace KBlog {
class BlogPrivate
{
public:
BlogPrivate():
q_ptr(0)
{
}
virtual ~BlogPrivate()
{
}
Blog *q_ptr;
QString mBlogId;
QString mUsername;
QString mPassword;
QString mUserAgent;
KUrl mUrl;
KTimeZone mTimeZone;
Q_DECLARE_PUBLIC( Blog )
};
} //namespace KBlog
#endif
/*
* Copyright (C) 2014 Daniel Vrátil <dvratil@redhat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "blogger.h"
#include "blog_p.h"
#include <kblog/blogpost.h>
#include <kblog/blogcomment.h>
#include <KUrl>
#include <KDebug>
#include <KLocalizedString>
#include <LibKGAPI2/Blogger/Blog>
#include <LibKGAPI2/Blogger/BlogFetchJob>
#include <LibKGAPI2/Blogger/Post>
#include <LibKGAPI2/Blogger/PostCreateJob>
#include <LibKGAPI2/Blogger/PostDeleteJob>
#include <LibKGAPI2/Blogger/PostFetchJob>
#include <LibKGAPI2/Blogger/PostModifyJob>
#include <LibKGAPI2/Blogger/Comment>
#include <LibKGAPI2/Blogger/CommentFetchJob>
#include <LibKGAPI2/Account>
#include <LibKGAPI2/AuthJob>
Q_DECLARE_METATYPE(KBlog::BlogPost*)
Q_DECLARE_METATYPE(KGAPI2::Job*)
typedef QMap<QString, QString> KBlogInfo;
#define KBLOGPOST_PROPERTY "KBlogPostProperty"
#define JOB_PROPERTY "JobProperty"
namespace KBlog
{
class BloggerPrivate: public KBlog::BlogPrivate
{
public:
BloggerPrivate(Blogger *parent);
virtual ~BloggerPrivate();
void updateKBlogPost(KBlog::BlogPost *kblog, const KGAPI2::Blogger::PostPtr &postPtr);
KBlog::BlogPost KGAPIPostToKBlogPost(const KGAPI2::Blogger::PostPtr &postPtr);
QList<KBlog::BlogPost> KGAPIPostsToKBlogPosts(const KGAPI2::ObjectsList &posts);
KGAPI2::Blogger::PostPtr KBlogPostToKGAPI(const BlogPost * const kblog);
KBlogInfo KGAPIBlogToKBlogBlog(const KGAPI2::Blogger::BlogPtr &blogPtr);
QList<KBlogInfo> KGAPIBlogsToKBlogBlogs(const KGAPI2::ObjectsList &blogs);
KBlog::BlogComment KGAPICommentToKBlogComment(const KGAPI2::Blogger::CommentPtr &commentPtr);
QList<KBlog::BlogComment> KGAPICommentsToKBlogComments(const KGAPI2::ObjectsList &comments);
bool handleError(KGAPI2::Job *job);
void _k_onAuthenticateFinished(KGAPI2::Job *job);
void _k_onListBlogsFinished(KGAPI2::Job *job);
void _k_onListRecentPostsFinished(KGAPI2::Job *job);
void _k_onFetchPostFinished(KGAPI2::Job *job);
void _k_onCreatePostFinished(KGAPI2::Job *job);
void _k_onRemovePostFinished(KGAPI2::Job *job);
void _k_onModifyPostFinished(KGAPI2::Job *job);
void _k_onListCommentsFinished(KGAPI2::Job *job);
QString apiKey;
QString secretKey;
KGAPI2::AccountPtr account;
private:
Blogger * const q_ptr;
Q_DECLARE_PUBLIC(Blogger)
};
} // namespace KBlog
using namespace KBlog;
BloggerPrivate::BloggerPrivate(Blogger *parent)
: BlogPrivate()
, q_ptr(parent)
{
}
BloggerPrivate::~BloggerPrivate()
{
}
void BloggerPrivate::updateKBlogPost(BlogPost *kblog, const KGAPI2::Blogger::PostPtr &postPtr)
{
kblog->setPostId(postPtr->id());
kblog->setTitle(postPtr->title());
kblog->setContent(postPtr->content());
kblog->setTags(postPtr->labels());
kblog->setCreationDateTime(postPtr->published());
kblog->setModificationDateTime(postPtr->updated());
kblog->setLink(postPtr->url());
kblog->setPrivate(postPtr->status() == QLatin1String("DRAFT"));
// TODO: Try to match more?
}
KGAPI2::Blogger::PostPtr BloggerPrivate::KBlogPostToKGAPI(const BlogPost *const kblog)
{
Q_Q(Blogger);
KGAPI2::Blogger::PostPtr postPtr(new KGAPI2::Blogger::Post);
postPtr->setId(kblog->postId());
postPtr->setBlogId(q->blogId());
postPtr->setTitle(kblog->title());
postPtr->setContent(kblog->content());
postPtr->setLabels(kblog->tags());
postPtr->setPublished(kblog->creationDateTime());
postPtr->setUpdated(kblog->modificationDateTime());
postPtr->setUrl(kblog->link());
return postPtr;
}
BlogPost BloggerPrivate::KGAPIPostToKBlogPost(const KGAPI2::Blogger::PostPtr &postPtr)
{
BlogPost kblog;
updateKBlogPost(&kblog, postPtr);
return kblog;
}
QList<BlogPost> BloggerPrivate::KGAPIPostsToKBlogPosts(const KGAPI2::ObjectsList &posts)
{
QList<BlogPost> blogPosts;
Q_FOREACH (const KGAPI2::ObjectPtr &obj, posts) {
blogPosts << KGAPIPostToKBlogPost(obj.dynamicCast<KGAPI2::Blogger::Post>());
}
return blogPosts;
}
KBlogInfo BloggerPrivate::KGAPIBlogToKBlogBlog(const KGAPI2::Blogger::BlogPtr &blogPtr)
{
KBlogInfo kblogInfo;
kblogInfo[QLatin1String("id")] = blogPtr->id();
kblogInfo[QLatin1String("title")] = blogPtr->name();
kblogInfo[QLatin1String("url")] = blogPtr->url().toString();
kblogInfo[QLatin1String("summay")] = blogPtr->description();
return kblogInfo;
}
QList<KBlogInfo> BloggerPrivate::KGAPIBlogsToKBlogBlogs(const KGAPI2::ObjectsList &blogs)
{
QList<KBlogInfo> kblogInfos;
Q_FOREACH (const KGAPI2::ObjectPtr &obj, blogs) {
kblogInfos << KGAPIBlogToKBlogBlog(obj.dynamicCast<KGAPI2::Blogger::Blog>());
}
return kblogInfos;
}
BlogComment BloggerPrivate::KGAPICommentToKBlogComment(const KGAPI2::Blogger::CommentPtr &commentPtr)
{
BlogComment kblogComment;
kblogComment.setCommentId(commentPtr->id());
kblogComment.setContent(commentPtr->content());
kblogComment.setCreationDateTime(commentPtr->published());
kblogComment.setModificationDateTime(commentPtr->updated());
kblogComment.setName(commentPtr->authorName());
return kblogComment;
}
QList<BlogComment> BloggerPrivate::KGAPICommentsToKBlogComments(const KGAPI2::ObjectsList &comments)
{
QList<BlogComment> kblogComments;
Q_FOREACH (const KGAPI2::ObjectPtr &obj, comments) {
kblogComments << KGAPICommentToKBlogComment(obj.dynamicCast<KGAPI2::Blogger::Comment>());
}
return kblogComments;
}
bool BloggerPrivate::handleError(KGAPI2::Job *job)
{
Q_Q(Blogger);
if (!job->error()) {
return true;
}
KBlog::BlogPost *post = job->property(KBLOGPOST_PROPERTY).value<BlogPost*>();
KBlog::Blog::ErrorType errCode = Blog::Other;
switch (job->error()) {
case KGAPI2::Unauthorized: {
KGAPI2::AuthJob *authJob = new KGAPI2::AuthJob(account, apiKey, secretKey, q);
authJob->setProperty(JOB_PROPERTY, QVariant::fromValue(job));
q->connect(authJob, SIGNAL(finished(KGAPI2::Job*)),
q, SLOT(_k_onAuthenticateFinished(KGAPI2::Job*)));
return false;
}
case KGAPI2::AuthCancelled:
case KGAPI2::AuthError:
errCode = Blog::AuthenticationError;
break;
case KGAPI2::BadRequest:
errCode = Blog::XmlRpc;
break;
// Not found is handled in callers
case KGAPI2::NotFound:
return true;
default:
errCode = Blog::Other;
break;
}
if (post) {
Q_EMIT q->errorPost(errCode, job->errorString(), post);
} else {
Q_EMIT q->error(errCode, job->errorString());
}
job->deleteLater();
return false;
}
Blogger::Blogger(const KUrl &server, QObject *parent)
: Blog(server, *new BloggerPrivate(this), parent)
{
kDebug();
}
Blogger::~Blogger()
{
kDebug();
}
QString Blogger::interfaceName() const
{
return QLatin1String("Blogger 3.0");
}
void Blogger::setApiKey(const QString &apiKey)
{
Q_D(Blogger);
d->apiKey = apiKey;
}
void Blogger::setSecretKey(const QString &secretKey)
{
Q_D(Blogger);
d->secretKey = secretKey;
}
void Blogger::authenticate(const QMap<QString, QString> &authData)
{
Q_D(Blogger);
KGAPI2::AccountPtr account;
kDebug() << authData;
if (!authData.isEmpty()) {
QList<QUrl> scopes;
scopes << KGAPI2::Account::bloggerScopeUrl();
account = KGAPI2::AccountPtr(new KGAPI2::Account(authData[QLatin1String("account")],
authData[QLatin1String("accessToken")],
authData[QLatin1String("refreshToken")],
scopes));
d->account = account;
} else {
account = KGAPI2::AccountPtr(new KGAPI2::Account);
account->addScope(KGAPI2::Account::bloggerScopeUrl());
KGAPI2::AuthJob *authJob = new KGAPI2::AuthJob(account, d->apiKey, d->secretKey, this);
if (account->accessToken().isEmpty()) {
authJob->setUsername(username());
authJob->setPassword(password());
}
connect(authJob, SIGNAL(finished(KGAPI2::Job*)),
this, SLOT(_k_onAuthenticateFinished(KGAPI2::Job*)));
}
}
void BloggerPrivate::_k_onAuthenticateFinished(KGAPI2::Job *job)
{
Q_Q(Blogger);
if (!handleError(job)) {
return;
}
KGAPI2::AuthJob *authJob = qobject_cast<KGAPI2::AuthJob*>(job);
account = authJob->account();
QMap<QString, QString> authData;
authData[QLatin1String("account")] = account->accountName();
authData[QLatin1String("accessToken")] = account->accessToken();
authData[QLatin1String("refreshToken")] = account->refreshToken();
Q_EMIT q->authenticated(authData);
if (authJob->property(JOB_PROPERTY).isValid()) {
KGAPI2::Job *originalJob = authJob->property(JOB_PROPERTY).value<KGAPI2::Job*>();
if (originalJob) {
originalJob->restart();
}
}
}
void Blogger::listBlogs()
{
Q_D(Blogger);
KGAPI2::Blogger::BlogFetchJob *fetchJob
= new KGAPI2::Blogger::BlogFetchJob(QLatin1String("self"),
KGAPI2::Blogger::BlogFetchJob::FetchByUserId,
d->account,
this);
connect(fetchJob, SIGNAL(finished(KGAPI2::Job*)),
this, SLOT(_k_onListBlogsFinished(KGAPI2::Job*)));
}
void BloggerPrivate::_k_onListBlogsFinished(KGAPI2::Job *job)
{
Q_Q(Blogger);
if (!handleError(job)) {
return;
}
job->deleteLater();
KGAPI2::FetchJob *fetchJob = qobject_cast<KGAPI2::FetchJob*>(job);
const QList<KBlogInfo> blogs = KGAPIBlogsToKBlogBlogs(fetchJob->items());
Q_EMIT q->listedBlogs(blogs);
}
void Blogger::listRecentPosts(int number)
{
Q_D(Blogger);
KGAPI2::Blogger::PostFetchJob *fetchJob
= new KGAPI2::Blogger::PostFetchJob(blogId(), d->account, this);
fetchJob->setMaxResults(number);
connect(fetchJob, SIGNAL(finished(KGAPI2::Job*)),
this, SLOT(_k_onListRecentPostsFinished(KGAPI2::Job*)));
}
void BloggerPrivate::_k_onListRecentPostsFinished(KGAPI2::Job *job)
{
Q_Q(Blogger);
if (!handleError(job)) {
return;
}
job->deleteLater();
KGAPI2::FetchJob *fetchJob = qobject_cast<KGAPI2::FetchJob*>(job);
QList<BlogPost> posts = KGAPIPostsToKBlogPosts(fetchJob->items());
QList<BlogPost>::Iterator iter, endIter = posts.end();
for (iter = posts.begin(); iter != endIter; ++iter) {
(*iter).setStatus(BlogPost::Fetched);
}
Q_EMIT q->listedRecentPosts(posts);
}
void Blogger::fetchPost(KBlog::BlogPost *post)
{
Q_D(Blogger);
KGAPI2::Blogger::PostFetchJob *fetchJob
= new KGAPI2::Blogger::PostFetchJob(blogId(), post->postId(), d->account, this);
fetchJob->setProperty(KBLOGPOST_PROPERTY, QVariant::fromValue(post));
connect(fetchJob, SIGNAL(finished(KGAPI2::Job*)),
this, SLOT(_k_onFetchPostFinished(KGAPI2::Job*)));
}
void BloggerPrivate::_k_onFetchPostFinished(KGAPI2::Job *job)
{
Q_Q(Blogger);
if (!handleError(job)) {
return;
}
job->deleteLater();
KGAPI2::FetchJob *fetchJob = qobject_cast<KGAPI2::FetchJob*>(job);
BlogPost *kblog = fetchJob->property(KBLOGPOST_PROPERTY).value<BlogPost*>();
const KGAPI2::ObjectsList items = fetchJob->items();
if (items.count() != 1) {
Q_EMIT q->errorPost(Blog::Other, i18n("Blog post not found"), kblog);
return;
}
updateKBlogPost(kblog, items.first().dynamicCast<KGAPI2::Blogger::Post>());
kblog->setStatus(BlogPost::Fetched);
Q_EMIT q->fetchedPost(kblog);
}
void Blogger::removePost(KBlog::BlogPost *post)
{
Q_D(Blogger);
KGAPI2::Blogger::PostDeleteJob *deleteJob
= new KGAPI2::Blogger::PostDeleteJob(blogId(), post->postId(), d->account, this);
deleteJob->setProperty(KBLOGPOST_PROPERTY, QVariant::fromValue(post));
connect(deleteJob, SIGNAL(finished(KGAPI2::Job*)),
this, SLOT(_k_onRemovePostFinished(KGAPI2::Job*)));
}
void BloggerPrivate::_k_onRemovePostFinished(KGAPI2::Job *job)
{
Q_Q(Blogger);
if (!handleError(job)) {
return;
}
job->deleteLater();
BlogPost *kblog = job->property(KBLOGPOST_PROPERTY).value<BlogPost*>();
kblog->setStatus(BlogPost::Removed);
Q_EMIT q->removedPost(kblog);
}
void Blogger::createPost(KBlog::BlogPost *post)
{
Q_D(Blogger);
KGAPI2::Blogger::PostPtr postPtr = d->KBlogPostToKGAPI(post);
KGAPI2::Blogger::PostCreateJob *createJob
= new KGAPI2::Blogger::PostCreateJob(postPtr, post->isPrivate(), d->account, this);
createJob->setProperty(KBLOGPOST_PROPERTY, QVariant::fromValue(post));
connect(createJob, SIGNAL(finished(KGAPI2::Job*)),
this, SLOT(_k_onCreatePostFinished(KGAPI2::Job*)));
}
void BloggerPrivate::_k_onCreatePostFinished(KGAPI2::Job *job)
{
Q_Q(Blogger);
if (!handleError(job)) {
return;
}
job->deleteLater();
KGAPI2::CreateJob *createJob = qobject_cast<KGAPI2::CreateJob*>(job);
BlogPost* kblog = createJob->property(KBLOGPOST_PROPERTY).value<BlogPost*>();
const KGAPI2::ObjectsList items = createJob->items();
if (items.count() != 1) {
Q_EMIT q->errorPost(Blog::Other, i18n("Failed to create new post"), kblog);
return;
}
updateKBlogPost(kblog, items.first().dynamicCast<KGAPI2::Blogger::Post>());
kblog->setStatus(BlogPost::Created);
Q_EMIT q->createdPost(kblog);
}
void Blogger::modifyPost(KBlog::BlogPost *post)
{
Q_D(Blogger);
KGAPI2::Blogger::PostPtr postPtr = d->KBlogPostToKGAPI(post);
postPtr->setPublished(KDateTime());
postPtr->setUpdated(KDateTime());
KGAPI2::Blogger::PostModifyJob *modifyJob
= new KGAPI2::Blogger::PostModifyJob(postPtr, d->account, this);
modifyJob->setProperty(KBLOGPOST_PROPERTY, QVariant::fromValue(post));
connect(modifyJob, SIGNAL(finished(KGAPI2::Job*)),
this, SLOT(_k_onModifyPostFinished(KGAPI2::Job*)));
}
void BloggerPrivate::_k_onModifyPostFinished(KGAPI2::Job *job)