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 | |
35 | using namespace KBlog; |
36 | |
37 | WordpressBuggy::WordpressBuggy( const KUrl &server, QObject *parent ) |
38 | : MovableType( server, *new WordpressBuggyPrivate, parent ) |
39 | { |
40 | kDebug(); |
41 | } |
42 | |
43 | WordpressBuggy::WordpressBuggy( const KUrl &server, WordpressBuggyPrivate &dd, |
44 | QObject *parent ) |
45 | : MovableType( server, dd, parent ) |
46 | { |
47 | kDebug(); |
48 | } |
49 | |
50 | WordpressBuggy::~WordpressBuggy() |
51 | { |
52 | kDebug(); |
53 | } |
54 | |
55 | void 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 | |
168 | void 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 | |
272 | QString WordpressBuggy::interfaceName() const |
273 | { |
274 | return QLatin1String( "Movable Type" ); |
275 | } |
276 | |
277 | WordpressBuggyPrivate::WordpressBuggyPrivate() |
278 | { |
279 | } |
280 | |
281 | WordpressBuggyPrivate::~WordpressBuggyPrivate() |
282 | { |
283 | kDebug(); |
284 | } |
285 | |
286 | QList<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 | |
298 | void 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 | |
349 | void 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 | |