• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdepimlibs-4.9.5 API Reference
  • KDE Home
  • Contact Us
 

KBlog Client Library

  • kblog
movabletype.cpp
1 /*
2  This file is part of the kblog library.
3 
4  Copyright (c) 2004 Reinhold Kainhofer <reinhold@kainhofer.com>
5  Copyright (c) 2006-2009 Christian Weilbach <christian_weilbach@web.de>
6  Copyright (c) 2007-2008 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 "movabletype.h"
25 #include "movabletype_p.h"
26 #include "blogpost.h"
27 
28 #include <kxmlrpcclient/client.h>
29 #include <kio/job.h>
30 
31 #include <KDebug>
32 #include <KLocale>
33 #include <KDateTime>
34 
35 #include <QtCore/QStringList>
36 
37 using namespace KBlog;
38 
39 MovableType::MovableType( const KUrl &server, QObject *parent )
40  : MetaWeblog( server, *new MovableTypePrivate, parent )
41 {
42  kDebug();
43 }
44 
45 MovableType::MovableType( const KUrl &server, MovableTypePrivate &dd,
46  QObject *parent )
47  : MetaWeblog( server, dd, parent )
48 {
49  kDebug();
50 }
51 
52 MovableType::~MovableType()
53 {
54  kDebug();
55 }
56 
57 QString MovableType::interfaceName() const
58 {
59  return QLatin1String( "Movable Type" );
60 }
61 
62 void MovableType::listRecentPosts( int number )
63 {
64  Q_D( MovableType );
65  kDebug();
66  QList<QVariant> args( d->defaultArgs( blogId() ) );
67  args << QVariant( number );
68  d->mXmlRpcClient->call(
69  "metaWeblog.getRecentPosts", args,
70  this, SLOT(slotListRecentPosts(QList<QVariant>,QVariant)),
71  this, SLOT(slotError(int,QString,QVariant)),
72  QVariant( number ) );
73 }
74 
75 void MovableType::listTrackBackPings( KBlog::BlogPost *post )
76 {
77  Q_D( MovableType );
78  kDebug();
79  QList<QVariant> args;
80  args << QVariant( post->postId() );
81  unsigned int i = d->mCallCounter++;
82  d->mCallMap[ i ] = post;
83  d->mXmlRpcClient->call(
84  "mt.getTrackbackPings", args,
85  this, SLOT(slotListTrackbackPings(QList<QVariant>,QVariant)),
86  this, SLOT(slotError(int,QString,QVariant)),
87  QVariant( i ) );
88 }
89 
90 void MovableType::fetchPost( BlogPost *post )
91 {
92  Q_D( MovableType );
93  kDebug();
94  d->loadCategories();
95  if ( d->mCategoriesList.isEmpty() && post->categories( ).count() ) {
96  d->mFetchPostCache << post;
97  if ( d->mFetchPostCache.count() ) {
98  // we are already trying to fetch another post, so we don't need to start
99  // another listCategories() job
100  return;
101  }
102 
103  connect( this, SIGNAL(listedCategories(QList<QMap<QString,QString> >))
104  , this, SLOT(slotTriggerFetchPost()) );
105  listCategories();
106  } else {
107  MetaWeblog::fetchPost( post );
108  }
109 }
110 
111 void MovableType::createPost( BlogPost *post )
112 {
113  // reimplemented because we do this:
114  // http://comox.textdrive.com/pipermail/wp-testers/2005-July/000284.html
115  kDebug();
116  Q_D( MovableType );
117 
118  // we need mCategoriesList to be loaded first, since we cannot use the post->categories()
119  // names later, but we need to map them to categoryId of the blog
120  d->loadCategories();
121  if(d->mCategoriesList.isEmpty()&&!post->categories().isEmpty()){
122  kDebug() << "No categories in the cache yet. Have to fetch them first.";
123  d->mCreatePostCache << post;
124  connect(this,SIGNAL(listedCategories(QList<QMap<QString,QString> >)),
125  this,SLOT(slotTriggerCreatePost()));
126  listCategories();
127  }
128  else {
129  bool publish = post->isPrivate();
130  // If we do setPostCategories() later than we disable publishing first.
131  if( !post->categories().isEmpty() ){
132  post->setPrivate( true );
133  if ( d->mSilentCreationList.contains( post ) ) {
134  kDebug()<< "Post already in mSilentCreationList, this *should* never happen!";
135  } else {
136  d->mSilentCreationList << post;
137  }
138  }
139  MetaWeblog::createPost( post );
140  // HACK: uuh this a bit ugly now... reenable the original publish argument,
141  // since createPost should have parsed now
142  post->setPrivate(publish);
143  }
144 }
145 
146 void MovableType::modifyPost( BlogPost *post )
147 {
148  // reimplemented because we do this:
149  // http://comox.textdrive.com/pipermail/wp-testers/2005-July/000284.html
150  kDebug();
151  Q_D( MovableType );
152 
153  // we need mCategoriesList to be loaded first, since we cannot use the post->categories()
154  // names later, but we need to map them to categoryId of the blog
155  d->loadCategories();
156  if(d->mCategoriesList.isEmpty() && !post->categories().isEmpty()){
157  kDebug() << "No categories in the cache yet. Have to fetch them first.";
158  d->mModifyPostCache << post;
159  connect(this,SIGNAL(listedCategories(QList<QMap<QString,QString> >)),
160  this,SLOT(slotTriggerModifyPost()));
161  listCategories();
162  }
163  else {
164  MetaWeblog::modifyPost( post );
165  }
166 }
167 
168 void MovableTypePrivate::slotTriggerCreatePost()
169 {
170  kDebug();
171  Q_Q( MovableType );
172 
173  q->disconnect(q,SIGNAL(listedCategories(QList<QMap<QString,QString> >)),
174  q,SLOT(slotTriggerCreatePost()));
175  // now we can recall createPost with the posts from the cache
176  QList<BlogPost*>::Iterator it = mCreatePostCache.begin();
177  QList<BlogPost*>::Iterator end = mCreatePostCache.end();
178  for ( ; it!=end; it++ ) {
179  q->createPost( *it );
180  }
181  mCreatePostCache.clear();
182 }
183 
184 void MovableTypePrivate::slotTriggerModifyPost()
185 {
186  kDebug();
187  Q_Q( MovableType );
188 
189  q->disconnect(q,SIGNAL(listedCategories(QList<QMap<QString,QString> >)),
190  q,SLOT(slotTriggerModifyPost()));
191  // now we can recall createPost with the posts from the cache
192  QList<BlogPost*>::Iterator it = mModifyPostCache.begin();
193  QList<BlogPost*>::Iterator end = mModifyPostCache.end();
194  for ( ; it!=end; it++ ) {
195  q->modifyPost( *it );
196  }
197  mModifyPostCache.clear();
198 }
199 
200 void MovableTypePrivate::slotTriggerFetchPost()
201 {
202  kDebug();
203  Q_Q( MovableType );
204 
205  q->disconnect( q,SIGNAL(listedCategories(QList<QMap<QString,QString> >)),
206  q,SLOT(slotTriggerFetchPost()) );
207  QList<BlogPost*>::Iterator it = mFetchPostCache.begin();
208  QList<BlogPost*>::Iterator end = mFetchPostCache.end();
209  for ( ; it!=end; it++ ) {
210  q->fetchPost( *it );
211  }
212  mFetchPostCache.clear();
213 }
214 
215 
216 MovableTypePrivate::MovableTypePrivate()
217 {
218  kDebug();
219 }
220 
221 MovableTypePrivate::~MovableTypePrivate()
222 {
223  kDebug();
224 }
225 
226 void MovableTypePrivate::slotCreatePost( const QList<QVariant> &result, const QVariant &id )
227 {
228  Q_Q( MovableType );
229  // reimplement from Blogger1 to chainload the categories stuff before emit()
230  kDebug();
231  KBlog::BlogPost *post = mCallMap[ id.toInt() ];
232  mCallMap.remove( id.toInt() );
233 
234  kDebug();
235  //array of structs containing ISO.8601
236  // dateCreated, String userid, String postid, String content;
237  kDebug () << "TOP:" << result[0].typeName();
238  if ( result[0].type() != QVariant::String && result[0].type() != QVariant::Int ) {
239  kError() << "Could not read the postId, not a string or an integer.";
240  emit q->errorPost( Blogger1::ParsingError,
241  i18n( "Could not read the postId, not a string or an integer." ),
242  post );
243  return;
244  }
245  QString serverID;
246  if ( result[0].type() == QVariant::String ) {
247  serverID = result[0].toString();
248  }
249  if ( result[0].type() == QVariant::Int ) {
250  serverID = QString( "%1" ).arg( result[0].toInt() );
251  }
252  post->setPostId( serverID );
253  if ( mSilentCreationList.contains( post ) )
254  {
255  // set the categories and publish afterwards
256  setPostCategories( post, !post->isPrivate() );
257  } else {
258  kDebug() << "emitting createdPost()"
259  << "for title: \"" << post->title()
260  << "\" server id: " << serverID;
261  post->setStatus( KBlog::BlogPost::Created );
262  emit q->createdPost( post );
263  }
264 }
265 
266 void MovableTypePrivate::slotFetchPost( const QList<QVariant> &result, const QVariant &id )
267 {
268  Q_Q( MovableType );
269  kDebug();
270 
271  KBlog::BlogPost *post = mCallMap[ id.toInt() ];
272  mCallMap.remove( id.toInt() );
273 
274  //array of structs containing ISO.8601
275  // dateCreated, String userid, String postid, String content;
276  kDebug () << "TOP:" << result[0].typeName();
277  if ( result[0].type() == QVariant::Map && readPostFromMap( post, result[0].toMap() ) ) {
278  } else {
279  kError() << "Could not fetch post out of the result from the server.";
280  post->setError( i18n( "Could not fetch post out of the result from the server." ) );
281  post->setStatus( BlogPost::Error );
282  emit q->errorPost( Blogger1::ParsingError,
283  i18n( "Could not fetch post out of the result from the server." ), post );
284  }
285  if ( post->categories().isEmpty() ) {
286  QList<QVariant> args( defaultArgs( post->postId() ) );
287  unsigned int i= mCallCounter++;
288  mCallMap[ i ] = post;
289  mXmlRpcClient->call(
290  "mt.getPostCategories", args,
291  q, SLOT(slotGetPostCategories(QList<QVariant>,QVariant)),
292  q, SLOT(slotError(int,QString,QVariant)),
293  QVariant( i ) );
294  } else {
295  kDebug() << "Emitting fetchedPost()";
296  post->setStatus( KBlog::BlogPost::Fetched );
297  emit q->fetchedPost( post );
298  }
299 }
300 
301 void MovableTypePrivate::slotModifyPost( const QList<QVariant> &result, const QVariant &id )
302 {
303  Q_Q( MovableType );
304  // reimplement from Blogger1
305  kDebug();
306  KBlog::BlogPost *post = mCallMap[ id.toInt() ];
307  mCallMap.remove( id.toInt() );
308 
309  //array of structs containing ISO.8601
310  // dateCreated, String userid, String postid, String content;
311  kDebug() << "TOP:" << result[0].typeName();
312  if ( result[0].type() != QVariant::Bool && result[0].type() != QVariant::Int ) {
313  kError() << "Could not read the result, not a boolean.";
314  emit q->errorPost( Blogger1::ParsingError,
315  i18n( "Could not read the result, not a boolean." ),
316  post );
317  return;
318  }
319  if ( mSilentCreationList.contains( post ) ) {
320  post->setStatus( KBlog::BlogPost::Created );
321  mSilentCreationList.removeOne( post );
322  emit q->createdPost( post );
323  } else {
324  if( !post->categories().isEmpty() ){
325  setPostCategories( post, false );
326  }
327  }
328 }
329 
330 void MovableTypePrivate::setPostCategories( BlogPost *post, bool publishAfterCategories )
331 {
332  kDebug();
333  Q_Q( MovableType );
334 
335  unsigned int i = mCallCounter++;
336  mCallMap[ i ] = post;
337  mPublishAfterCategories[ i ] = publishAfterCategories;
338  QList<QVariant> catList;
339  QList<QVariant> args( defaultArgs( post->postId() ) );
340 
341  // map the categoryId of the server to the name
342  QStringList categories = post->categories();
343  for( int j=0; j<categories.count(); j++ ){
344  for( int k=0; k<mCategoriesList.count(); k++ ){
345  if(mCategoriesList[k]["name"]==categories[j]){
346  kDebug() << "Matched category with name: " << categories[ j ] << " and id: " << mCategoriesList[ k ][ "categoryId" ];
347  QMap<QString,QVariant> category;
348  //the first in the QStringList of post->categories()
349  // is the primary category
350  category["categoryId"]=mCategoriesList[k]["categoryId"].toInt();
351  catList<<QVariant( category );
352  break;
353  }
354  if(k==mCategoriesList.count()){
355  kDebug() << "Couldn't find categoryId for: " << categories[j];
356  }
357  }
358  }
359  args<<QVariant( catList );
360 
361  mXmlRpcClient->call(
362  "mt.setPostCategories", args,
363  q, SLOT(slotSetPostCategories(QList<QVariant>,QVariant)),
364  q, SLOT(slotError(int,QString,QVariant)),
365  QVariant( i ) );
366 }
367 
368 void MovableTypePrivate::slotGetPostCategories(const QList<QVariant>& result,const QVariant& id)
369 {
370  kDebug();
371  Q_Q( MovableType );
372 
373  int i = id.toInt();
374  BlogPost* post = mCallMap[ i ];
375  mCallMap.remove(i);
376 
377  if ( result[ 0 ].type() != QVariant::List ) {
378  kError() << "Could not read the result, not a list. Category fetching failed! We will still emit fetched post now.";
379  emit q->errorPost( Blogger1::ParsingError,
380  i18n( "Could not read the result - is not a list. Category fetching failed." ), post );
381 
382  post->setStatus( KBlog::BlogPost::Fetched );
383  emit q->fetchedPost( post );
384  } else {
385  QList<QVariant> categoryList = result[ 0 ].toList();
386  QList<QString> newCatList;
387  QList<QVariant>::ConstIterator it = categoryList.constBegin();
388  QList<QVariant>::ConstIterator end = categoryList.constEnd();
389  for ( ;it!=end;it++ ) {
390  newCatList << ( *it ).toMap()[ "categoryName" ].toString();
391  }
392  kDebug()<< "categories list: " << newCatList;
393  post->setCategories( newCatList );
394  post->setStatus( KBlog::BlogPost::Fetched );
395  emit q->fetchedPost( post );
396  }
397 }
398 
399 void MovableTypePrivate::slotSetPostCategories(const QList<QVariant>& result,const QVariant& id)
400 {
401  kDebug();
402  Q_Q( MovableType );
403 
404  int i = id.toInt();
405  BlogPost* post = mCallMap[ i ];
406  bool publish = mPublishAfterCategories[ i ];
407  mCallMap.remove(i);
408  mPublishAfterCategories.remove(i);
409 
410  if ( result[0].type() != QVariant::Bool ) {
411  kError() << "Could not read the result, not a boolean. Category setting failed! We will still publish if now if necessary. ";
412  emit q->errorPost( Blogger1::ParsingError,
413  i18n( "Could not read the result - is not a boolean value. Category setting failed. Will still publish now if necessary." ),
414  post );
415  }
416  // Finally publish now, if the post was meant to be published in the beginning.
417  // The first boolean is necessary to only publish if the post is created, not
418  // modified.
419  if( publish && !post->isPrivate() ){
420  q->modifyPost( post );
421  }
422 
423  // this is the end of the chain then
424  if ( !publish ) {
425  if ( mSilentCreationList.contains( post ) ) {
426  kDebug() << "emitting createdPost() for title: \""
427  << post->title() << "\"";
428  post->setStatus( KBlog::BlogPost::Created );
429  mSilentCreationList.removeOne( post );
430  emit q->createdPost( post );
431  } else {
432  kDebug() << "emitting modifiedPost() for title: \""
433  << post->title() << "\"";
434  post->setStatus( KBlog::BlogPost::Modified );
435  emit q->modifiedPost( post );
436  }
437  }
438 }
439 
440 QList<QVariant> MovableTypePrivate::defaultArgs( const QString &id )
441 {
442  Q_Q( MovableType );
443  QList<QVariant> args;
444  if( !id.isEmpty() ) {
445  args << QVariant( id );
446  }
447  args << QVariant( q->username() )
448  << QVariant( q->password() );
449  return args;
450 }
451 
452 bool MovableTypePrivate::readPostFromMap( BlogPost *post, const QMap<QString, QVariant> &postInfo )
453 {
454 
455  // FIXME: integrate error handling
456  kDebug() << "readPostFromMap()";
457  if ( !post ) {
458  return false;
459  }
460  QStringList mapkeys = postInfo.keys();
461  kDebug() << endl << "Keys:" << mapkeys.join( ", " );
462  kDebug() << endl;
463 
464  KDateTime dt =
465  KDateTime( postInfo["dateCreated"].toDateTime(), KDateTime::UTC );
466  if ( dt.isValid() && !dt.isNull() ) {
467  post->setCreationDateTime( dt.toLocalZone() );
468  }
469 
470  dt =
471  KDateTime( postInfo["lastModified"].toDateTime(), KDateTime::UTC );
472  if ( dt.isValid() && !dt.isNull() ) {
473  post->setModificationDateTime( dt.toLocalZone() );
474  }
475 
476  post->setPostId( postInfo["postid"].toString().isEmpty() ? postInfo["postId"].toString() :
477  postInfo["postid"].toString() );
478 
479  QString title( postInfo["title"].toString() );
480  QString description( postInfo["description"].toString() );
481  QStringList categoryIdList = postInfo["categories"].toStringList();
482  QStringList categories;
483  // since the metaweblog definition is ambigious, we try different
484  // category mappings
485  for ( int i=0; i<categoryIdList.count(); i++ ) {
486  for ( int k=0; k<mCategoriesList.count(); k++ ) {
487  if ( mCategoriesList[ k ][ "name" ]==categoryIdList[ i ] ){
488  categories << mCategoriesList[ k ][ "name" ];
489  } else if ( mCategoriesList[ k ][ "categoryId" ]==categoryIdList[ i ]) {
490  categories << mCategoriesList[ k ][ "name" ];
491  }
492  }
493  }
494 
495  //TODO 2 new keys are:
496  // String mt_convert_breaks, the value for the convert_breaks field
497  post->setSlug( postInfo["wp_slug"].toString() );
498  post->setAdditionalContent( postInfo["mt_text_more"].toString() );
499  post->setTitle( title );
500  post->setContent( description );
501  post->setCommentAllowed( (bool)postInfo["mt_allow_comments"].toInt() );
502  post->setTrackBackAllowed( (bool)postInfo["mt_allow_pings"].toInt() );
503  post->setSummary( postInfo["mt_excerpt"].toString() );
504  post->setTags( postInfo["mt_keywords"].toStringList() );
505  post->setLink( postInfo["link"].toString() );
506  post->setPermaLink( postInfo["permaLink"].toString() );
507  QString postStatus = postInfo["post_status"].toString();
508  if( postStatus != "publish" && !postStatus.isEmpty() ){
514  post->setPrivate(true);
515  }
516  if ( !categories.isEmpty() ){
517  kDebug() << "Categories:" << categories;
518  post->setCategories( categories );
519  }
520  return true;
521 }
522 
523 void MovableTypePrivate::slotListTrackBackPings(
524  const QList<QVariant> &result, const QVariant &id )
525 {
526  Q_Q( MovableType );
527  kDebug() << "slotTrackbackPings()";
528  BlogPost *post = mCallMap[ id.toInt() ];
529  mCallMap.remove( id.toInt() );
530  QList<QMap<QString,QString> > trackBackList;
531  if ( result[0].type() != QVariant::List ) {
532  kError() << "Could not fetch list of trackback pings out of the"
533  << "result from the server.";
534  emit q->error( MovableType::ParsingError,
535  i18n( "Could not fetch list of trackback pings out of the "
536  "result from the server." ) );
537  return;
538  }
539  const QList<QVariant> trackBackReceived = result[0].toList();
540  QList<QVariant>::ConstIterator it = trackBackReceived.begin();
541  QList<QVariant>::ConstIterator end = trackBackReceived.end();
542  for ( ; it != end; ++it ) {
543  QMap<QString,QString> tping;
544  kDebug() << "MIDDLE:" << ( *it ).typeName();
545  const QMap<QString, QVariant> trackBackInfo = ( *it ).toMap();
546  tping[ "title" ] = trackBackInfo[ "pingTitle"].toString();
547  tping[ "url" ] = trackBackInfo[ "pingURL"].toString();
548  tping[ "ip" ] = trackBackInfo[ "pingIP"].toString();
549  trackBackList << tping;
550  }
551  kDebug() << "Emitting listedTrackBackPings()";
552  emit q->listedTrackBackPings( post, trackBackList );
553 }
554 
555 bool MovableTypePrivate::readArgsFromPost( QList<QVariant> *args, const BlogPost &post )
556 {
557  //TODO 2 new keys are:
558  // String mt_convert_breaks, the value for the convert_breaks field
559  // array mt_tb_ping_urls, the list of TrackBack ping URLs for this entry
560  if ( !args ) {
561  return false;
562  }
563  QMap<QString, QVariant> map;
564  map["categories"] = post.categories();
565  map["description"] = post.content();
566  if( !post.additionalContent().isEmpty() )
567  map["mt_text_more"] = post.additionalContent();
568  map["title"] = post.title();
569  map["dateCreated"] = post.creationDateTime().dateTime().toUTC();
570  map["mt_allow_comments"] = (int)post.isCommentAllowed();
571  map["mt_allow_pings"] = (int)post.isTrackBackAllowed();
572  map["mt_excerpt"] = post.summary();
573  map["mt_keywords"] = post.tags().join(",");
574  //map["mt_tb_ping_urls"] check for that, i think this should only be done on the server.
575  *args << map;
576  *args << QVariant( !post.isPrivate() );
577  return true;
578 }
579 
580 #include "movabletype.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2013 The KDE developers.
Generated on Sat Jan 5 2013 19:43:51 by doxygen 1.8.1.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KBlog Client Library

Skip menu "KBlog Client Library"
  • Main Page
  • Namespace List
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • Related Pages

kdepimlibs-4.9.5 API Reference

Skip menu "kdepimlibs-4.9.5 API Reference"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal