00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "movabletype.h"
00025 #include "movabletype_p.h"
00026 #include "blogpost.h"
00027
00028 #include <kxmlrpcclient/client.h>
00029 #include <kio/job.h>
00030
00031 #include <KDebug>
00032 #include <KLocale>
00033 #include <KDateTime>
00034
00035 #include <QtCore/QStringList>
00036
00037 using namespace KBlog;
00038
00039 MovableType::MovableType( const KUrl &server, QObject *parent )
00040 : MetaWeblog( server, *new MovableTypePrivate, parent )
00041 {
00042 kDebug();
00043 }
00044
00045 MovableType::MovableType( const KUrl &server, MovableTypePrivate &dd,
00046 QObject *parent )
00047 : MetaWeblog( server, dd, parent )
00048 {
00049 kDebug();
00050 }
00051
00052 MovableType::~MovableType()
00053 {
00054 kDebug();
00055 }
00056
00057 QString MovableType::interfaceName() const
00058 {
00059 return QLatin1String( "Movable Type" );
00060 }
00061
00062 void MovableType::listRecentPosts( int number )
00063 {
00064 Q_D( MovableType );
00065 kDebug();
00066 QList<QVariant> args( d->defaultArgs( blogId() ) );
00067 args << QVariant( number );
00068 d->mXmlRpcClient->call(
00069 "metaWeblog.getRecentPosts", args,
00070 this, SLOT(slotListRecentPosts(const QList<QVariant>&,const QVariant&)),
00071 this, SLOT(slotError(int,const QString&,const QVariant&)),
00072 QVariant( number ) );
00073 }
00074
00075 void MovableType::listTrackBackPings( KBlog::BlogPost *post )
00076 {
00077 Q_D( MovableType );
00078 kDebug();
00079 QList<QVariant> args;
00080 args << QVariant( post->postId() );
00081 unsigned int i = d->mCallCounter++;
00082 d->mCallMap[ i ] = post;
00083 d->mXmlRpcClient->call(
00084 "mt.getTrackbackPings", args,
00085 this, SLOT(slotListTrackbackPings(const QList<QVariant>&,const QVariant&)),
00086 this, SLOT(slotError(int,const QString&,const QVariant&)),
00087 QVariant( i ) );
00088 }
00089
00090 void MovableType::fetchPost( BlogPost *post )
00091 {
00092 Q_D( MovableType );
00093 kDebug();
00094 d->loadCategories();
00095 if ( d->mCategoriesList.isEmpty() && post->categories( ).count() ) {
00096 d->mFetchPostCache << post;
00097 if ( d->mFetchPostCache.count() ) {
00098
00099
00100 return;
00101 }
00102
00103 connect( this, SIGNAL( listedCategories( const QList<QMap<QString,QString> >& ) )
00104 , this, SLOT( slotTriggerFetchPost() ) );
00105 listCategories();
00106 } else {
00107 MetaWeblog::fetchPost( post );
00108 }
00109 }
00110
00111 void MovableType::createPost( BlogPost *post )
00112 {
00113
00114
00115 kDebug();
00116 Q_D( MovableType );
00117
00118
00119
00120 d->loadCategories();
00121 if(d->mCategoriesList.isEmpty()&&!post->categories().isEmpty()){
00122 kDebug() << "No categories in the cache yet. Have to fetch them first.";
00123 d->mCreatePostCache << post;
00124 connect(this,SIGNAL(listedCategories(const QList<QMap<QString,QString> >&)),
00125 this,SLOT(slotTriggerCreatePost()));
00126 listCategories();
00127 }
00128 else {
00129 bool publish = post->isPrivate();
00130
00131 if( !post->categories().isEmpty() ){
00132 post->setPrivate( true );
00133 if ( d->mSilentCreationList.contains( post ) ) {
00134 kDebug()<< "Post already in mSilentCreationList, this *should* never happen!";
00135 } else {
00136 d->mSilentCreationList << post;
00137 }
00138 }
00139 MetaWeblog::createPost( post );
00140
00141
00142 post->setPrivate(publish);
00143 }
00144 }
00145
00146 void MovableType::modifyPost( BlogPost *post )
00147 {
00148
00149
00150 kDebug();
00151 Q_D( MovableType );
00152
00153
00154
00155 d->loadCategories();
00156 if(d->mCategoriesList.isEmpty() && !post->categories().isEmpty()){
00157 kDebug() << "No categories in the cache yet. Have to fetch them first.";
00158 d->mModifyPostCache << post;
00159 connect(this,SIGNAL(listedCategories(const QList<QMap<QString,QString> >&)),
00160 this,SLOT(slotTriggerModifyPost()));
00161 listCategories();
00162 }
00163 else {
00164 MetaWeblog::modifyPost( post );
00165 }
00166 }
00167
00168 void MovableTypePrivate::slotTriggerCreatePost()
00169 {
00170 kDebug();
00171 Q_Q( MovableType );
00172
00173 q->disconnect(q,SIGNAL(listedCategories(const QList<QMap<QString,QString> >&)),
00174 q,SLOT(slotTriggerCreatePost()));
00175
00176 QList<BlogPost*>::Iterator it = mCreatePostCache.begin();
00177 QList<BlogPost*>::Iterator end = mCreatePostCache.end();
00178 for ( ; it!=end; it++ ) {
00179 q->createPost( *it );
00180 }
00181 mCreatePostCache.clear();
00182 }
00183
00184 void MovableTypePrivate::slotTriggerModifyPost()
00185 {
00186 kDebug();
00187 Q_Q( MovableType );
00188
00189 q->disconnect(q,SIGNAL(listedCategories(const QList<QMap<QString,QString> >&)),
00190 q,SLOT(slotTriggerModifyPost()));
00191
00192 QList<BlogPost*>::Iterator it = mModifyPostCache.begin();
00193 QList<BlogPost*>::Iterator end = mModifyPostCache.end();
00194 for ( ; it!=end; it++ ) {
00195 q->modifyPost( *it );
00196 }
00197 mModifyPostCache.clear();
00198 }
00199
00200 void MovableTypePrivate::slotTriggerFetchPost()
00201 {
00202 kDebug();
00203 Q_Q( MovableType );
00204
00205 q->disconnect( q,SIGNAL( listedCategories( const QList<QMap<QString,QString> >& ) ),
00206 q,SLOT( slotTriggerFetchPost() ) );
00207 QList<BlogPost*>::Iterator it = mFetchPostCache.begin();
00208 QList<BlogPost*>::Iterator end = mFetchPostCache.end();
00209 for ( ; it!=end; it++ ) {
00210 q->fetchPost( *it );
00211 }
00212 mFetchPostCache.clear();
00213 }
00214
00215
00216 MovableTypePrivate::MovableTypePrivate()
00217 {
00218 kDebug();
00219 }
00220
00221 MovableTypePrivate::~MovableTypePrivate()
00222 {
00223 kDebug();
00224 }
00225
00226 void MovableTypePrivate::slotCreatePost( const QList<QVariant> &result, const QVariant &id )
00227 {
00228 Q_Q( MovableType );
00229
00230 kDebug();
00231 KBlog::BlogPost *post = mCallMap[ id.toInt() ];
00232 mCallMap.remove( id.toInt() );
00233
00234 kDebug();
00235
00236
00237 kDebug () << "TOP:" << result[0].typeName();
00238 if ( result[0].type() != QVariant::String && result[0].type() != QVariant::Int ) {
00239 kError() << "Could not read the postId, not a string or an integer.";
00240 emit q->errorPost( Blogger1::ParsingError,
00241 i18n( "Could not read the postId, not a string or an integer." ),
00242 post );
00243 return;
00244 }
00245 QString serverID;
00246 if ( result[0].type() == QVariant::String ) {
00247 serverID = result[0].toString();
00248 }
00249 if ( result[0].type() == QVariant::Int ) {
00250 serverID = QString( "%1" ).arg( result[0].toInt() );
00251 }
00252 post->setPostId( serverID );
00253 if ( mSilentCreationList.contains( post ) )
00254 {
00255
00256 setPostCategories( post, !post->isPrivate() );
00257 } else {
00258 kDebug() << "emitting createdPost()"
00259 << "for title: \"" << post->title()
00260 << "\" server id: " << serverID;
00261 post->setStatus( KBlog::BlogPost::Created );
00262 emit q->createdPost( post );
00263 }
00264 }
00265
00266 void MovableTypePrivate::slotFetchPost( const QList<QVariant> &result, const QVariant &id )
00267 {
00268 Q_Q( MovableType );
00269 kDebug();
00270
00271 KBlog::BlogPost *post = mCallMap[ id.toInt() ];
00272 mCallMap.remove( id.toInt() );
00273
00274
00275
00276 kDebug () << "TOP:" << result[0].typeName();
00277 if ( result[0].type() == QVariant::Map && readPostFromMap( post, result[0].toMap() ) ) {
00278 } else {
00279 kError() << "Could not fetch post out of the result from the server.";
00280 post->setError( i18n( "Could not fetch post out of the result from the server." ) );
00281 post->setStatus( BlogPost::Error );
00282 emit q->errorPost( Blogger1::ParsingError,
00283 i18n( "Could not fetch post out of the result from the server." ), post );
00284 }
00285 if ( post->categories().isEmpty() ) {
00286 QList<QVariant> args( defaultArgs( post->postId() ) );
00287 unsigned int i= mCallCounter++;
00288 mCallMap[ i ] = post;
00289 mXmlRpcClient->call(
00290 "mt.getPostCategories", args,
00291 q, SLOT(slotGetPostCategories(const QList<QVariant>&,const QVariant&)),
00292 q, SLOT(slotError(int, const QString&,const QVariant&)),
00293 QVariant( i ) );
00294 } else {
00295 kDebug() << "Emitting fetchedPost()";
00296 post->setStatus( KBlog::BlogPost::Fetched );
00297 emit q->fetchedPost( post );
00298 }
00299 }
00300
00301 void MovableTypePrivate::slotModifyPost( const QList<QVariant> &result, const QVariant &id )
00302 {
00303 Q_Q( MovableType );
00304
00305 kDebug();
00306 KBlog::BlogPost *post = mCallMap[ id.toInt() ];
00307 mCallMap.remove( id.toInt() );
00308
00309
00310
00311 kDebug() << "TOP:" << result[0].typeName();
00312 if ( result[0].type() != QVariant::Bool && result[0].type() != QVariant::Int ) {
00313 kError() << "Could not read the result, not a boolean.";
00314 emit q->errorPost( Blogger1::ParsingError,
00315 i18n( "Could not read the result, not a boolean." ),
00316 post );
00317 return;
00318 }
00319 if ( mSilentCreationList.contains( post ) ) {
00320 post->setStatus( KBlog::BlogPost::Created );
00321 mSilentCreationList.removeOne( post );
00322 emit q->createdPost( post );
00323 } else {
00324 if( !post->categories().isEmpty() ){
00325 setPostCategories( post, false );
00326 }
00327 }
00328 }
00329
00330 void MovableTypePrivate::setPostCategories( BlogPost *post, bool publishAfterCategories )
00331 {
00332 kDebug();
00333 Q_Q( MovableType );
00334
00335 unsigned int i = mCallCounter++;
00336 mCallMap[ i ] = post;
00337 mPublishAfterCategories[ i ] = publishAfterCategories;
00338 QList<QVariant> catList;
00339 QList<QVariant> args( defaultArgs( post->postId() ) );
00340
00341
00342 QStringList categories = post->categories();
00343 for( int j=0; j<categories.count(); j++ ){
00344 for( int k=0; k<mCategoriesList.count(); k++ ){
00345 if(mCategoriesList[k]["name"]==categories[j]){
00346 kDebug() << "Matched category with name: " << categories[ j ] << " and id: " << mCategoriesList[ k ][ "categoryId" ];
00347 QMap<QString,QVariant> category;
00348
00349
00350 category["categoryId"]=mCategoriesList[k]["categoryId"].toInt();
00351 catList<<QVariant( category );
00352 break;
00353 }
00354 if(k==mCategoriesList.count()){
00355 kDebug() << "Couldn't find categoryId for: " << categories[j];
00356 }
00357 }
00358 }
00359 args<<QVariant( catList );
00360
00361 mXmlRpcClient->call(
00362 "mt.setPostCategories", args,
00363 q, SLOT(slotSetPostCategories(const QList<QVariant>&,const QVariant&)),
00364 q, SLOT(slotError(int, const QString&,const QVariant&)),
00365 QVariant( i ) );
00366 }
00367
00368 void MovableTypePrivate::slotGetPostCategories(const QList<QVariant>& result,const QVariant& id)
00369 {
00370 kDebug();
00371 Q_Q( MovableType );
00372
00373 int i = id.toInt();
00374 BlogPost* post = mCallMap[ i ];
00375 mCallMap.remove(i);
00376
00377 if ( result[ 0 ].type() != QVariant::List ) {
00378 kError() << "Could not read the result, not a list. Category fetching failed! We will still emit fetched post now.";
00379 emit q->errorPost( Blogger1::ParsingError,
00380 i18n( "Could not read the result - is not a list. Category fetching failed." ), post );
00381
00382 post->setStatus( KBlog::BlogPost::Fetched );
00383 emit q->fetchedPost( post );
00384 } else {
00385 QList<QVariant> categoryList = result[ 0 ].toList();
00386 QList<QString> newCatList;
00387 QList<QVariant>::ConstIterator it = categoryList.constBegin();
00388 QList<QVariant>::ConstIterator end = categoryList.constEnd();
00389 for ( ;it!=end;it++ ) {
00390 newCatList << ( *it ).toMap()[ "categoryName" ].toString();
00391 }
00392 kDebug()<< "categories list: " << newCatList;
00393 post->setCategories( newCatList );
00394 post->setStatus( KBlog::BlogPost::Fetched );
00395 emit q->fetchedPost( post );
00396 }
00397 }
00398
00399 void MovableTypePrivate::slotSetPostCategories(const QList<QVariant>& result,const QVariant& id)
00400 {
00401 kDebug();
00402 Q_Q( MovableType );
00403
00404 int i = id.toInt();
00405 BlogPost* post = mCallMap[ i ];
00406 bool publish = mPublishAfterCategories[ i ];
00407 mCallMap.remove(i);
00408 mPublishAfterCategories.remove(i);
00409
00410 if ( result[0].type() != QVariant::Bool ) {
00411 kError() << "Could not read the result, not a boolean. Category setting failed! We will still publish if now if necessary. ";
00412 emit q->errorPost( Blogger1::ParsingError,
00413 i18n( "Could not read the result - is not a boolean value. Category setting failed. Will still publish now if necessary." ),
00414 post );
00415 }
00416
00417
00418
00419 if( publish && !post->isPrivate() ){
00420 q->modifyPost( post );
00421 }
00422
00423
00424 if ( !publish ) {
00425 if ( mSilentCreationList.contains( post ) ) {
00426 kDebug() << "emitting createdPost() for title: \""
00427 << post->title() << "\"";
00428 post->setStatus( KBlog::BlogPost::Created );
00429 mSilentCreationList.removeOne( post );
00430 emit q->createdPost( post );
00431 } else {
00432 kDebug() << "emitting modifiedPost() for title: \""
00433 << post->title() << "\"";
00434 post->setStatus( KBlog::BlogPost::Modified );
00435 emit q->modifiedPost( post );
00436 }
00437 }
00438 }
00439
00440 QList<QVariant> MovableTypePrivate::defaultArgs( const QString &id )
00441 {
00442 Q_Q( MovableType );
00443 QList<QVariant> args;
00444 if( !id.isEmpty() ) {
00445 args << QVariant( id );
00446 }
00447 args << QVariant( q->username() )
00448 << QVariant( q->password() );
00449 return args;
00450 }
00451
00452 bool MovableTypePrivate::readPostFromMap( BlogPost *post, const QMap<QString, QVariant> &postInfo )
00453 {
00454
00455
00456 kDebug() << "readPostFromMap()";
00457 if ( !post ) {
00458 return false;
00459 }
00460 QStringList mapkeys = postInfo.keys();
00461 kDebug() << endl << "Keys:" << mapkeys.join( ", " );
00462 kDebug() << endl;
00463
00464 KDateTime dt =
00465 KDateTime( postInfo["dateCreated"].toDateTime(), KDateTime::UTC );
00466 if ( dt.isValid() && !dt.isNull() ) {
00467 post->setCreationDateTime( dt.toLocalZone() );
00468 }
00469
00470 dt =
00471 KDateTime( postInfo["lastModified"].toDateTime(), KDateTime::UTC );
00472 if ( dt.isValid() && !dt.isNull() ) {
00473 post->setModificationDateTime( dt.toLocalZone() );
00474 }
00475
00476 post->setPostId( postInfo["postid"].toString().isEmpty() ? postInfo["postId"].toString() :
00477 postInfo["postid"].toString() );
00478
00479 QString title( postInfo["title"].toString() );
00480 QString description( postInfo["description"].toString() );
00481 QStringList categoryIdList = postInfo["categories"].toStringList();
00482 QStringList categories;
00483
00484
00485 for ( int i=0; i<categoryIdList.count(); i++ ) {
00486 for ( int k=0; k<mCategoriesList.count(); k++ ) {
00487 if ( mCategoriesList[ k ][ "name" ]==categoryIdList[ i ] ){
00488 categories << mCategoriesList[ k ][ "name" ];
00489 } else if ( mCategoriesList[ k ][ "categoryId" ]==categoryIdList[ i ]) {
00490 categories << mCategoriesList[ k ][ "name" ];
00491 }
00492 }
00493 }
00494
00495
00496
00497 post->setSlug( postInfo["wp_slug"].toString() );
00498 post->setAdditionalContent( postInfo["mt_text_more"].toString() );
00499 post->setTitle( title );
00500 post->setContent( description );
00501 post->setCommentAllowed( (bool)postInfo["mt_allow_comments"].toInt() );
00502 post->setTrackBackAllowed( (bool)postInfo["mt_allow_pings"].toInt() );
00503 post->setSummary( postInfo["mt_excerpt"].toString() );
00504 post->setTags( postInfo["mt_keywords"].toStringList() );
00505 post->setLink( postInfo["link"].toString() );
00506 post->setPermaLink( postInfo["permaLink"].toString() );
00507 QString postStatus = postInfo["post_status"].toString();
00508 if( postStatus != "publish" && !postStatus.isEmpty() ){
00514 post->setPrivate(true);
00515 }
00516 if ( !categories.isEmpty() ){
00517 kDebug() << "Categories:" << categories;
00518 post->setCategories( categories );
00519 }
00520 return true;
00521 }
00522
00523 void MovableTypePrivate::slotListTrackBackPings(
00524 const QList<QVariant> &result, const QVariant &id )
00525 {
00526 Q_Q( MovableType );
00527 kDebug() << "slotTrackbackPings()";
00528 BlogPost *post = mCallMap[ id.toInt() ];
00529 mCallMap.remove( id.toInt() );
00530 QList<QMap<QString,QString> > trackBackList;
00531 if ( result[0].type() != QVariant::List ) {
00532 kError() << "Could not fetch list of trackback pings out of the"
00533 << "result from the server.";
00534 emit q->error( MovableType::ParsingError,
00535 i18n( "Could not fetch list of trackback pings out of the "
00536 "result from the server." ) );
00537 return;
00538 }
00539 const QList<QVariant> trackBackReceived = result[0].toList();
00540 QList<QVariant>::ConstIterator it = trackBackReceived.begin();
00541 QList<QVariant>::ConstIterator end = trackBackReceived.end();
00542 for ( ; it != end; ++it ) {
00543 QMap<QString,QString> tping;
00544 kDebug() << "MIDDLE:" << ( *it ).typeName();
00545 const QMap<QString, QVariant> trackBackInfo = ( *it ).toMap();
00546 tping[ "title" ] = trackBackInfo[ "pingTitle"].toString();
00547 tping[ "url" ] = trackBackInfo[ "pingURL"].toString();
00548 tping[ "ip" ] = trackBackInfo[ "pingIP"].toString();
00549 trackBackList << tping;
00550 }
00551 kDebug() << "Emitting listedTrackBackPings()";
00552 emit q->listedTrackBackPings( post, trackBackList );
00553 }
00554
00555 bool MovableTypePrivate::readArgsFromPost( QList<QVariant> *args, const BlogPost &post )
00556 {
00557
00558
00559
00560 if ( !args ) {
00561 return false;
00562 }
00563 QMap<QString, QVariant> map;
00564 map["categories"] = post.categories();
00565 map["description"] = post.content();
00566 if( !post.additionalContent().isEmpty() )
00567 map["mt_text_more"] = post.additionalContent();
00568 map["title"] = post.title();
00569 map["dateCreated"] = post.creationDateTime().dateTime().toUTC();
00570 map["mt_allow_comments"] = (int)post.isCommentAllowed();
00571 map["mt_allow_pings"] = (int)post.isTrackBackAllowed();
00572 map["mt_excerpt"] = post.summary();
00573 map["mt_keywords"] = post.tags().join(",");
00574
00575 *args << map;
00576 *args << QVariant( !post.isPrivate() );
00577 return true;
00578 }
00579
00580 #include "movabletype.moc"