1/*
2 This file is part of the kblog library.
3
4 Copyright (c) 2004 Reinhold Kainhofer <reinhold@kainhofer.com>
5 Copyright (c) 2006-2007 Christian Weilbach <christian_weilbach@web.de>
6 Copyright (c) 2007 Mike McQuaid <mike@mikemcquaid.com>
7
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public
10 License as published by the Free Software Foundation; either
11 version 2 of the License, or (at your option) any later version.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Library General Public License for more details.
17
18 You should have received a copy of the GNU Library General Public License
19 along with this library; see the file COPYING.LIB. If not, write to
20 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 Boston, MA 02110-1301, USA.
22*/
23
24#include "metaweblog.h"
25#include "metaweblog_p.h"
26#include "blogpost.h"
27#include "blogmedia.h"
28
29#include <kxmlrpcclient/client.h>
30#include <KDebug>
31#include <KLocalizedString>
32#include <KDateTime>
33#include <kstandarddirs.h>
34
35#include <QtCore/QFile>
36#include <QtCore/QDataStream>
37
38using namespace KBlog;
39
40MetaWeblog::MetaWeblog( const KUrl &server, QObject *parent )
41 : Blogger1( server, *new MetaWeblogPrivate, parent )
42{
43 kDebug();
44}
45
46MetaWeblog::MetaWeblog( const KUrl &server, MetaWeblogPrivate &dd, QObject *parent )
47 : Blogger1( server, dd, parent )
48{
49 kDebug();
50}
51
52MetaWeblog::~MetaWeblog()
53{
54 kDebug();
55}
56
57QString MetaWeblog::interfaceName() const
58{
59 return QLatin1String( "MetaWeblog" );
60}
61
62void MetaWeblog::listCategories()
63{
64 Q_D( MetaWeblog );
65 kDebug() << "Fetching List of Categories...";
66 QList<QVariant> args( d->defaultArgs( blogId() ) );
67 d->mXmlRpcClient->call(
68 QLatin1String("metaWeblog.getCategories"), args,
69 this, SLOT(slotListCategories(QList<QVariant>,QVariant)),
70 this, SLOT(slotError(int,QString,QVariant)) );
71}
72
73void MetaWeblog::createMedia( KBlog::BlogMedia *media )
74{
75 Q_D( MetaWeblog );
76 if ( !media ) {
77 kError() << "MetaWeblog::createMedia: media is a null pointer";
78 emit error ( Other, i18n( "Media is a null pointer." ) );
79 return;
80 }
81 unsigned int i = d->mCallMediaCounter++;
82 d->mCallMediaMap[ i ] = media;
83 kDebug() << "MetaWeblog::createMedia: name=" << media->name();
84 QList<QVariant> args( d->defaultArgs( blogId() ) );
85 QMap<QString, QVariant> map;
86 map[QLatin1String("name")] = media->name();
87 map[QLatin1String("type")] = media->mimetype();
88 map[QLatin1String("bits")] = media->data();
89 args << map;
90 d->mXmlRpcClient->call(
91 QLatin1String("metaWeblog.newMediaObject"), args,
92 this, SLOT(slotCreateMedia(QList<QVariant>,QVariant)),
93 this, SLOT(slotError(int,QString,QVariant)),
94 QVariant( i ) );
95
96}
97
98MetaWeblogPrivate::MetaWeblogPrivate()
99{
100 kDebug();
101 mCallMediaCounter=1;
102 mCatLoaded=false;
103}
104
105MetaWeblogPrivate::~MetaWeblogPrivate()
106{
107 kDebug();
108}
109
110QList<QVariant> MetaWeblogPrivate::defaultArgs( const QString &id )
111{
112 Q_Q( MetaWeblog );
113 QList<QVariant> args;
114 if ( !id.isEmpty() ) {
115 args << QVariant( id );
116 }
117 args << QVariant( q->username() )
118 << QVariant( q->password() );
119 return args;
120}
121
122void MetaWeblogPrivate::loadCategories()
123{
124 kDebug();
125
126 if ( mCatLoaded ) {
127 return;
128 }
129 mCatLoaded = true;
130
131 if ( mUrl.isEmpty() || mBlogId.isEmpty() || mUsername.isEmpty() ) {
132 kDebug() << "We need at least url, blogId and the username to create a unique filename.";
133 return;
134 }
135
136 QString filename = QLatin1String("kblog/") + mUrl.host() + QLatin1Char('_') + mBlogId + QLatin1Char('_') + mUsername;
137 filename = KStandardDirs::locateLocal( "data", filename, true );
138
139 QFile file( filename );
140 if ( !file.open( QIODevice::ReadOnly ) ) {
141 kDebug() << "Cannot open cached categories file: " << filename;
142 return;
143 }
144
145 QDataStream stream( &file );
146 stream >> mCategoriesList;
147 file.close();
148}
149
150void MetaWeblogPrivate::saveCategories()
151{
152 kDebug();
153 if ( mUrl.isEmpty() || mBlogId.isEmpty() || mUsername.isEmpty() ) {
154 kDebug() << "We need at least url, blogId and the username to create a unique filename.";
155 return;
156 }
157
158 QString filename = QLatin1String("kblog/") + mUrl.host() + QLatin1Char('_') + mBlogId + QLatin1Char('_') + mUsername;
159 filename = KStandardDirs::locateLocal( "data", filename, true );
160
161 QFile file( filename );
162 if ( !file.open( QIODevice::WriteOnly ) ) {
163 kDebug() << "Cannot open cached categories file: " << filename;
164 return;
165 }
166
167 QDataStream stream( &file );
168 stream << mCategoriesList;
169 file.close();
170}
171
172void MetaWeblogPrivate::slotListCategories( const QList<QVariant> &result,
173 const QVariant &id )
174{
175 Q_Q( MetaWeblog );
176 Q_UNUSED( id );
177
178 kDebug() << "MetaWeblogPrivate::slotListCategories";
179 kDebug() << "TOP:" << result[0].typeName();
180 if ( result[0].type() != QVariant::Map &&
181 result[0].type() != QVariant::List ) {
182 // include fix for not metaweblog standard compatible apis with
183 // array of structs instead of struct of structs, e.g. wordpress
184 kError() << "Could not list categories out of the result from the server.";
185 emit q->error( MetaWeblog::ParsingError,
186 i18n( "Could not list categories out of the result "
187 "from the server." ) );
188 } else {
189 if ( result[0].type() == QVariant::Map ) {
190 const QMap<QString, QVariant> serverMap = result[0].toMap();
191 const QList<QString> serverKeys = serverMap.keys();
192
193 QList<QString>::ConstIterator it = serverKeys.begin();
194 QList<QString>::ConstIterator end = serverKeys.end();
195 for ( ; it != end; ++it ) {
196 kDebug() << "MIDDLE:" << ( *it );
197 QMap<QString,QString> category;
198 const QMap<QString, QVariant> serverCategory = serverMap[*it].toMap();
199 category[QLatin1String("name")]= ( *it );
200 category[QLatin1String("description")] = serverCategory[ QLatin1String("description") ].toString();
201 category[QLatin1String("htmlUrl")] = serverCategory[ QLatin1String("htmlUrl") ].toString();
202 category[QLatin1String("rssUrl")] = serverCategory[ QLatin1String("rssUrl") ].toString();
203 category[QLatin1String("categoryId")] = serverCategory[ QLatin1String("categoryId") ].toString();
204 category[QLatin1String("parentId")] = serverCategory[ QLatin1String("parentId") ].toString();
205 mCategoriesList.append( category );
206 }
207 kDebug() << "Emitting listedCategories";
208 emit q->listedCategories( mCategoriesList );
209 }
210 }
211 if ( result[0].type() == QVariant::List ) {
212 // include fix for not metaweblog standard compatible apis with
213 // array of structs instead of struct of structs, e.g. wordpress
214 const QList<QVariant> serverList = result[0].toList();
215 QList<QVariant>::ConstIterator it = serverList.begin();
216 QList<QVariant>::ConstIterator end = serverList.end();
217 for ( ; it != end; ++it ) {
218 kDebug() << "MIDDLE:" << ( *it ).typeName();
219 QMap<QString,QString> category;
220 const QMap<QString, QVariant> serverCategory = ( *it ).toMap();
221 category[ QLatin1String("name") ] = serverCategory[QLatin1String("categoryName")].toString();
222 category[QLatin1String("description")] = serverCategory[ QLatin1String("description") ].toString();
223 category[QLatin1String("htmlUrl")] = serverCategory[ QLatin1String("htmlUrl") ].toString();
224 category[QLatin1String("rssUrl")] = serverCategory[ QLatin1String("rssUrl") ].toString();
225 category[QLatin1String("categoryId")] = serverCategory[ QLatin1String("categoryId") ].toString();
226 category[QLatin1String("parentId")] = serverCategory[ QLatin1String("parentId") ].toString();
227 mCategoriesList.append( category );
228 }
229 kDebug() << "Emitting listedCategories()";
230 emit q->listedCategories( mCategoriesList );
231 }
232 saveCategories();
233}
234
235void MetaWeblogPrivate::slotCreateMedia( const QList<QVariant> &result,
236 const QVariant &id )
237{
238 Q_Q( MetaWeblog );
239
240 KBlog::BlogMedia *media = mCallMediaMap[ id.toInt() ];
241 mCallMediaMap.remove( id.toInt() );
242
243 kDebug() << "MetaWeblogPrivate::slotCreateMedia, no error!";
244 kDebug() << "TOP:" << result[0].typeName();
245 if ( result[0].type() != 8 ) {
246 kError() << "Could not read the result, not a map.";
247 emit q->errorMedia( MetaWeblog::ParsingError,
248 i18n( "Could not read the result, not a map." ),
249 media );
250 return;
251 }
252 const QMap<QString, QVariant> resultStruct = result[0].toMap();
253 const QString url = resultStruct[QLatin1String("url")].toString();
254 kDebug() << "MetaWeblog::slotCreateMedia url=" << url;
255
256 if ( !url.isEmpty() ) {
257 media->setUrl( KUrl( url ) );
258 media->setStatus( BlogMedia::Created );
259 kDebug() << "Emitting createdMedia( url=" << url << ");";
260 emit q->createdMedia( media );
261 }
262}
263
264bool MetaWeblogPrivate::readPostFromMap( BlogPost *post,
265 const QMap<QString, QVariant> &postInfo )
266{
267 // FIXME: integrate error handling
268 kDebug() << "readPostFromMap()";
269 if ( !post ) {
270 return false;
271 }
272 QStringList mapkeys = postInfo.keys();
273 kDebug() << endl << "Keys:" << mapkeys.join( QLatin1String(", ") );
274 kDebug() << endl;
275
276 KDateTime dt =
277 KDateTime( postInfo[QLatin1String("dateCreated")].toDateTime(), KDateTime::UTC );
278 if ( dt.isValid() && !dt.isNull() ) {
279 post->setCreationDateTime( dt.toLocalZone() );
280 }
281
282 dt =
283 KDateTime( postInfo[QLatin1String("lastModified")].toDateTime(), KDateTime::UTC );
284 if ( dt.isValid() && !dt.isNull() ) {
285 post->setModificationDateTime( dt.toLocalZone() );
286 }
287
288 post->setPostId( postInfo[QLatin1String("postid")].toString().isEmpty() ? postInfo[QLatin1String("postId")].toString() :
289 postInfo[QLatin1String("postid")].toString() );
290
291 QString title( postInfo[QLatin1String("title")].toString() );
292 QString description( postInfo[QLatin1String("description")].toString() );
293 QStringList categories( postInfo[QLatin1String("categories")].toStringList() );
294
295 post->setTitle( title );
296 post->setContent( description );
297 if ( !categories.isEmpty() ) {
298 kDebug() << "Categories:" << categories;
299 post->setCategories( categories );
300 }
301 return true;
302}
303
304bool MetaWeblogPrivate::readArgsFromPost( QList<QVariant> *args, const BlogPost &post )
305{
306 if ( !args ) {
307 return false;
308 }
309 QMap<QString, QVariant> map;
310 map[QLatin1String("categories")] = post.categories();
311 map[QLatin1String("description")] = post.content();
312 map[QLatin1String("title")] = post.title();
313 map[QLatin1String("lastModified")] = post.modificationDateTime().dateTime().toUTC();
314 map[QLatin1String("dateCreated")] = post.creationDateTime().dateTime().toUTC();
315 *args << map;
316 *args << QVariant( !post.isPrivate() );
317 return true;
318}
319
320QString MetaWeblogPrivate::getCallFromFunction( FunctionToCall type )
321{
322 switch ( type ) {
323 case GetRecentPosts: return QLatin1String("metaWeblog.getRecentPosts");
324 case CreatePost: return QLatin1String("metaWeblog.newPost");
325 case ModifyPost: return QLatin1String("metaWeblog.editPost");
326 case FetchPost: return QLatin1String("metaWeblog.getPost");
327 default: return QString();
328 }
329}
330#include "moc_metaweblog.cpp"
331