1/*
2 This file is part of the kblog library.
3
4 Copyright (c) 2006-2009 Christian Weilbach <christian_weilbach@web.de>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
20*/
21
22#include "wordpressbuggy.h"
23#include "wordpressbuggy_p.h"
24
25#include "blogpost.h"
26
27#include <KDebug>
28#include <KLocalizedString>
29#include <KDateTime>
30
31#include <kio/job.h>
32
33#include <QtCore/QStringList>
34
35using namespace KBlog;
36
37WordpressBuggy::WordpressBuggy( const KUrl &server, QObject *parent )
38 : MovableType( server, *new WordpressBuggyPrivate, parent )
39{
40 kDebug();
41}
42
43WordpressBuggy::WordpressBuggy( const KUrl &server, WordpressBuggyPrivate &dd,
44 QObject *parent )
45 : MovableType( server, dd, parent )
46{
47 kDebug();
48}
49
50WordpressBuggy::~WordpressBuggy()
51{
52 kDebug();
53}
54
55void WordpressBuggy::createPost( KBlog::BlogPost *post )
56{
57 // reimplemented because we do this:
58 // http://comox.textdrive.com/pipermail/wp-testers/2005-July/000284.html
59 kDebug();
60 Q_D( WordpressBuggy );
61
62 // we need mCategoriesList to be loaded first, since we cannot use the post->categories()
63 // names later, but we need to map them to categoryId of the blog
64 d->loadCategories();
65 if ( d->mCategoriesList.isEmpty() ) {
66 kDebug() << "No categories in the cache yet. Have to fetch them first.";
67 d->mCreatePostCache << post;
68 connect( this,SIGNAL(listedCategories(QList<QMap<QString,QString> >)),
69 this,SLOT(slotTriggerCreatePost()) );
70 listCategories();
71 }
72 else {
73 kDebug() << "createPost()";
74 if ( !post ) {
75 kError() << "WordpressBuggy::createPost: post is a null pointer";
76 emit error ( Other, i18n( "Post is a null pointer." ) );
77 return;
78 }
79 kDebug() << "Creating new Post with blogId" << blogId();
80
81 bool publish = post->isPrivate();
82 // If we do setPostCategories() later than we disable publishing first.
83 if ( !post->categories().isEmpty() ) {
84 post->setPrivate( true );
85 if ( d->mSilentCreationList.contains( post ) ) {
86 kDebug() << "Post already in mSilentCreationList, this *should* never happen!";
87 } else {
88 d->mSilentCreationList << post;
89 }
90 }
91
92 QString xmlMarkup = QLatin1String("<?xml version=\"1.0\"?>");
93 xmlMarkup += QLatin1String("<methodCall>");
94 xmlMarkup += QLatin1String("<methodName>metaWeblog.newPost</methodName>");
95 xmlMarkup += QLatin1String("<params><param>");
96 xmlMarkup += QLatin1String("<value><string><![CDATA[")+blogId()+QLatin1String("]]></string></value>");
97 xmlMarkup += QLatin1String("</param>");
98 xmlMarkup += QLatin1String("<param>");
99 xmlMarkup += QLatin1String("<value><string><![CDATA[")+username()+QLatin1String("]]></string></value>");
100 xmlMarkup += QLatin1String("</param><param>");
101 xmlMarkup += QLatin1String("<value><string><![CDATA[")+password()+QLatin1String("]]></string></value>");
102 xmlMarkup += QLatin1String("</param>");
103 xmlMarkup += QLatin1String("<param><struct>");
104 xmlMarkup += QLatin1String("<member><name>description</name>");
105 xmlMarkup += QLatin1String("<value><string><![CDATA[")+post->content()+QLatin1String("]]></string></value>");
106 xmlMarkup += QLatin1String("</member><member>");
107 xmlMarkup += QLatin1String("<name>title</name>");
108 xmlMarkup += QLatin1String("<value><string><![CDATA[")+post->title()+QLatin1String("]]></string></value>");
109 xmlMarkup += QLatin1String("</member><member>");
110
111 xmlMarkup += QLatin1String("<name>dateCreated</name>");
112 xmlMarkup += QLatin1String("<value><dateTime.iso8601>") +
113 post->creationDateTime().dateTime().toUTC().toString( QLatin1String("yyyyMMddThh:mm:ss") ) +
114 QLatin1String("</dateTime.iso8601></value>");
115 xmlMarkup += QLatin1String("</member><member>");
116 xmlMarkup += QLatin1String("<name>mt_allow_comments</name>");
117 xmlMarkup += QString::fromLatin1( "<value><int>%1</int></value>" ).arg( (int)post->isCommentAllowed() );
118 xmlMarkup += QLatin1String("</member><member>");
119 xmlMarkup += QLatin1String("<name>mt_allow_pings</name>");
120 xmlMarkup += QString::fromLatin1( "<value><int>%1</int></value>" ).arg( (int)post->isTrackBackAllowed() );
121 xmlMarkup += QLatin1String("</member><member>");
122 if ( !post->additionalContent().isEmpty() ) {
123 xmlMarkup += QLatin1String("<name>mt_text_more</name>");
124 xmlMarkup += QLatin1String("<value><string><![CDATA[") + post->additionalContent() + QLatin1String("]]></string></value>");
125 xmlMarkup += QLatin1String("</member><member>");
126 }
127 xmlMarkup += QLatin1String("<name>wp_slug</name>");
128 xmlMarkup += QLatin1String("<value><string><![CDATA[") + post->slug() + QLatin1String("]]></string></value>");
129 xmlMarkup += QLatin1String("</member><member>");
130 xmlMarkup += QLatin1String("<name>mt_excerpt</name>");
131 xmlMarkup += QLatin1String("<value><string><![CDATA[") + post->summary() + QLatin1String("]]></string></value>");
132 xmlMarkup += QLatin1String("</member><member>");
133 xmlMarkup += QLatin1String("<name>mt_keywords</name>");
134 xmlMarkup += QLatin1String("<value><string><![CDATA[") + post->tags().join(QLatin1String(",")) + QLatin1String("]]></string></value>");
135 xmlMarkup += QLatin1String("</member></struct></param>");
136 xmlMarkup += QLatin1String("<param><value><boolean>") +
137 QString::fromLatin1( "%1" ).arg( (int)(!post->isPrivate() ) ) +
138 QLatin1String("</boolean></value></param>");
139 xmlMarkup += QLatin1String("</params></methodCall>");
140
141 QByteArray postData;
142 QDataStream stream( &postData, QIODevice::WriteOnly );
143 stream.writeRawData( xmlMarkup.toUtf8(), xmlMarkup.toUtf8().length() );
144
145 KIO::StoredTransferJob *job = KIO::storedHttpPost( postData, url(), KIO::HideProgressInfo );
146
147 d->mCreatePostMap[ job ] = post;
148
149 if ( !job ) {
150 kWarning() << "Failed to create job for: " << url().url();
151 }
152
153 job->addMetaData(
154 QLatin1String("customHTTPHeader"), QLatin1String("X-hacker: Shame on you Wordpress, ") + QString() +
155 QLatin1String("you took another 4 hours of my life to work around the stupid dateTime bug.") );
156 job->addMetaData( QLatin1String("content-type"), QLatin1String("Content-Type: text/xml; charset=utf-8") );
157 job->addMetaData( QLatin1String("ConnectTimeout"), QLatin1String("50") );
158 job->addMetaData( QLatin1String("UserAgent"), userAgent() );
159
160 connect( job, SIGNAL(result(KJob*)),
161 this, SLOT(slotCreatePost(KJob*)) );
162 // HACK: uuh this a bit ugly now... reenable the original publish argument,
163 // since createPost should have parsed now
164 post->setPrivate( publish );
165 }
166}
167
168void WordpressBuggy::modifyPost( KBlog::BlogPost *post )
169{
170 // reimplemented because we do this:
171 // http://comox.textdrive.com/pipermail/wp-testers/2005-July/000284.html
172 kDebug();
173 Q_D( WordpressBuggy );
174
175 // we need mCategoriesList to be loaded first, since we cannot use the post->categories()
176 // names later, but we need to map them to categoryId of the blog
177 d->loadCategories();
178 if ( d->mCategoriesList.isEmpty() ) {
179 kDebug() << "No categories in the cache yet. Have to fetch them first.";
180 d->mModifyPostCache << post;
181 connect( this,SIGNAL(listedCategories(QList<QMap<QString,QString> >)),
182 this,SLOT(slotTriggerModifyPost()) );
183 listCategories();
184 }
185 else {
186 if ( !post ) {
187 kError() << "WordpressBuggy::modifyPost: post is a null pointer";
188 emit error ( Other, i18n( "Post is a null pointer." ) );
189 return;
190 }
191
192 kDebug() << "Uploading Post with postId" << post->postId();
193
194 QString xmlMarkup = QLatin1String("<?xml version=\"1.0\"?>");
195 xmlMarkup += QLatin1String("<methodCall>");
196 xmlMarkup += QLatin1String("<methodName>metaWeblog.editPost</methodName>");
197 xmlMarkup += QLatin1String("<params><param>");
198 xmlMarkup += QLatin1String("<value><string><![CDATA[")+post->postId()+QLatin1String("]]></string></value>");
199 xmlMarkup += QLatin1String("</param>");
200 xmlMarkup += QLatin1String("<param>");
201 xmlMarkup += QLatin1String("<value><string><![CDATA[")+username()+QLatin1String("]]></string></value>");
202 xmlMarkup += QLatin1String("</param><param>");
203 xmlMarkup += QLatin1String("<value><string><![CDATA[")+password()+QLatin1String("]]></string></value>");
204 xmlMarkup += QLatin1String("</param>");
205 xmlMarkup += QLatin1String("<param><struct>");
206 xmlMarkup += QLatin1String("<member><name>description</name>");
207 xmlMarkup += QLatin1String("<value><string><![CDATA[")+post->content()+QLatin1String("]]></string></value>");
208 xmlMarkup += QLatin1String("</member><member>");
209 xmlMarkup += QLatin1String("<name>title</name>");
210 xmlMarkup += QLatin1String("<value><string><![CDATA[")+post->title()+QLatin1String("]]></string></value>");
211 xmlMarkup += QLatin1String("</member><member>");
212
213 xmlMarkup += QLatin1String("<name>lastModified</name>");
214 xmlMarkup += QLatin1String("<value><dateTime.iso8601>") +
215 post->modificationDateTime().dateTime().toUTC().toString( QLatin1String("yyyyMMddThh:mm:ss") ) +
216 QLatin1String("</dateTime.iso8601></value>");
217 xmlMarkup += QLatin1String("</member><member>");
218 xmlMarkup += QLatin1String("<name>dateCreated</name>");
219 xmlMarkup += QLatin1String("<value><dateTime.iso8601>") +
220 post->creationDateTime().dateTime().toUTC().toString( QLatin1String("yyyyMMddThh:mm:ss") ) +
221 QLatin1String("</dateTime.iso8601></value>");
222 xmlMarkup += QLatin1String("</member><member>");
223 xmlMarkup += QLatin1String("<name>mt_allow_comments</name>");
224 xmlMarkup += QString::fromLatin1( "<value><int>%1</int></value>" ).arg( (int)post->isCommentAllowed() );
225 xmlMarkup += QLatin1String("</member><member>");
226 xmlMarkup += QLatin1String("<name>mt_allow_pings</name>");
227 xmlMarkup += QString::fromLatin1( "<value><int>%1</int></value>" ).arg( (int)post->isTrackBackAllowed() );
228 xmlMarkup += QLatin1String("</member><member>");
229 if ( !post->additionalContent().isEmpty() ) {
230 xmlMarkup += QLatin1String("<name>mt_text_more</name>");
231 xmlMarkup += QLatin1String("<value><string><![CDATA[") + post->additionalContent() + QLatin1String("]]></string></value>");
232 xmlMarkup += QLatin1String("</member><member>");
233 }
234 xmlMarkup += QLatin1String("<name>wp_slug</name>");
235 xmlMarkup += QLatin1String("<value><string><![CDATA[") + post->slug() + QLatin1String("]]></string></value>");
236 xmlMarkup += QLatin1String("</member><member>");
237 xmlMarkup += QLatin1String("<name>mt_excerpt</name>");
238 xmlMarkup += QLatin1String("<value><string><![CDATA[") + post->summary() + QLatin1String("]]></string></value>");
239 xmlMarkup += QLatin1String("</member><member>");
240 xmlMarkup += QLatin1String("<name>mt_keywords</name>");
241 xmlMarkup += QLatin1String("<value><string><![CDATA[") + post->tags().join( QLatin1String(",") ) + QLatin1String("]]></string></value>");
242 xmlMarkup += QLatin1String("</member></struct></param>");
243 xmlMarkup += QLatin1String("<param><value><boolean>") +
244 QString::fromLatin1( "%1" ).arg( (int)( !post->isPrivate() ) ) +
245 QLatin1String("</boolean></value></param>");
246 xmlMarkup += QLatin1String("</params></methodCall>");
247
248 QByteArray postData;
249 QDataStream stream( &postData, QIODevice::WriteOnly );
250 stream.writeRawData( xmlMarkup.toUtf8(), xmlMarkup.toUtf8().length() );
251
252 KIO::StoredTransferJob *job = KIO::storedHttpPost( postData, url(), KIO::HideProgressInfo );
253
254 d->mModifyPostMap[ job ] = post;
255
256 if ( !job ) {
257 kWarning() << "Failed to create job for: " << url().url();
258 }
259
260 job->addMetaData(
261 QLatin1String("customHTTPHeader"), QLatin1String("X-hacker: Shame on you Wordpress, ") + QString() +
262 QLatin1String("you took another 4 hours of my life to work around the stupid dateTime bug.") );
263 job->addMetaData( QLatin1String("content-type"), QLatin1String("Content-Type: text/xml; charset=utf-8") );
264 job->addMetaData( QLatin1String("ConnectTimeout"), QLatin1String("50") );
265 job->addMetaData( QLatin1String("UserAgent"), userAgent() );
266
267 connect( job, SIGNAL(result(KJob*)),
268 this, SLOT(slotModifyPost(KJob*)) );
269 }
270}
271
272QString WordpressBuggy::interfaceName() const
273{
274 return QLatin1String( "Movable Type" );
275}
276
277WordpressBuggyPrivate::WordpressBuggyPrivate()
278{
279}
280
281WordpressBuggyPrivate::~WordpressBuggyPrivate()
282{
283 kDebug();
284}
285
286QList<QVariant> WordpressBuggyPrivate::defaultArgs( const QString &id )
287{
288 Q_Q( WordpressBuggy );
289 QList<QVariant> args;
290 if ( !id.isEmpty() ) {
291 args << QVariant( id );
292 }
293 args << QVariant( q->username() )
294 << QVariant( q->password() );
295 return args;
296}
297
298void WordpressBuggyPrivate::slotCreatePost( KJob *job )
299{
300 kDebug();
301
302 KIO::StoredTransferJob *stj = qobject_cast<KIO::StoredTransferJob*>( job );
303 const QString data = QString::fromUtf8( stj->data(), stj->data().size() );
304
305 Q_Q( WordpressBuggy );
306
307 KBlog::BlogPost *post = mCreatePostMap[ job ];
308 mCreatePostMap.remove( job );
309
310 if ( job->error() != 0 ) {
311 kError() << "slotCreatePost error:" << job->errorString();
312 emit q->errorPost( WordpressBuggy::XmlRpc, job->errorString(), post );
313 return;
314 }
315
316 QRegExp rxError( QLatin1String("faultString") );
317 if ( rxError.indexIn( data ) != -1 ) {
318 rxError = QRegExp( QLatin1String("<string>(.+)</string>") );
319 if ( rxError.indexIn( data ) != -1 ) {
320 kDebug() << "RegExp of faultString failed.";
321 }
322 kDebug() << rxError.cap( 1 );
323 emit q->errorPost( WordpressBuggy::XmlRpc, rxError.cap( 1 ), post );
324 return;
325 }
326
327 QRegExp rxId( QLatin1String("<string>(.+)</string>") );
328 if ( rxId.indexIn( data ) == -1 ) {
329 kError() << "Could not regexp the id out of the result:" << data;
330 emit q->errorPost( WordpressBuggy::XmlRpc,
331 i18n( "Could not regexp the id out of the result." ), post );
332 return;
333 }
334 kDebug() << "QRegExp rx( \"<string>(.+)</string>\" ) matches" << rxId.cap( 1 );
335
336 post->setPostId( rxId.cap( 1 ) );
337 if ( mSilentCreationList.contains( post ) )
338 {
339 // set the categories and publish afterwards
340 setPostCategories( post, !post->isPrivate() );
341 } else {
342 kDebug() << "emitting createdPost()"
343 << "for title: \"" << post->title();
344 emit q->createdPost( post );
345 post->setStatus( KBlog::BlogPost::Created );
346 }
347}
348
349void WordpressBuggyPrivate::slotModifyPost( KJob *job )
350{
351 kDebug();
352
353 KIO::StoredTransferJob *stj = qobject_cast<KIO::StoredTransferJob*>( job );
354 const QString data = QString::fromUtf8( stj->data(), stj->data().size() );
355
356 KBlog::BlogPost *post = mModifyPostMap[ job ];
357 mModifyPostMap.remove( job );
358 Q_Q( WordpressBuggy );
359 if ( job->error() != 0 ) {
360 kError() << "slotModifyPost error:" << job->errorString();
361 emit q->errorPost( WordpressBuggy::XmlRpc, job->errorString(), post );
362 return;
363 }
364
365 QRegExp rxError( QLatin1String("faultString") );
366 if ( rxError.indexIn( data ) != -1 ) {
367 rxError = QRegExp( QLatin1String("<string>(.+)</string>") );
368 if ( rxError.indexIn( data ) != -1 ) {
369 kDebug() << "RegExp of faultString failed.";
370 }
371 kDebug() << rxError.cap( 1 );
372 emit q->errorPost( WordpressBuggy::XmlRpc, rxError.cap( 1 ), post );
373 return;
374 }
375
376 QRegExp rxId( QLatin1String("<boolean>(.+)</boolean>") );
377 if ( rxId.indexIn( data ) == -1 ) {
378 kError() << "Could not regexp the id out of the result:" << data;
379 emit q->errorPost( WordpressBuggy::XmlRpc,
380 i18n( "Could not regexp the id out of the result." ), post );
381 return;
382 }
383 kDebug() << "QRegExp rx( \"<boolean>(.+)</boolean>\" ) matches" << rxId.cap( 1 );
384
385 if ( rxId.cap( 1 ).toInt() == 1 ) {
386 kDebug() << "Post successfully updated.";
387 if ( mSilentCreationList.contains( post ) ) {
388 post->setStatus( KBlog::BlogPost::Created );
389 emit q->createdPost( post );
390 mSilentCreationList.removeOne( post );
391 } else {
392 if ( !post->categories().isEmpty() ) {
393 setPostCategories( post, false );
394 }
395 }
396 }
397}
398
399#include "moc_wordpressbuggy.cpp"
400