blokkalpostentryqueue.cpp
00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "blokkalpostentryqueue.h"
00021 #include "blokkalpostentryqueue.moc"
00022
00023 #include "blokkalentry.h"
00024 #include "blokkalblog.h"
00025 #include "blokkalaccount.h"
00026 #include "blokkalaccountmanager.h"
00027 #include "blokkalio/jobs.h"
00028
00029 #include <kdebug.h>
00030 #include <klocale.h>
00031 #include <kstandarddirs.h>
00032
00033 #include <QMap>
00034 #include <QTimer>
00035 #include <QDir>
00036 #include <QLinkedList>
00037
00038 class Blokkal::PostEntryStatus::Private
00039 {
00040 public:
00041 Private( void ) {}
00042
00043 Status status;
00044 int error;
00045 QString errorString;
00046 Io::EntryJob * job;
00047
00048 long saveNumber;
00049 };
00050
00051 Blokkal::PostEntryStatus::PostEntryStatus( void ) :
00052 d( new Private() )
00053 {
00054 d->status = StatusNormal;
00055 d->error = 0;
00056 d->errorString = QString::null;
00057 d->job = 0;
00058
00059 d->saveNumber = -1;
00060 }
00061
00062 Blokkal::PostEntryStatus::~PostEntryStatus( void )
00063 {
00064 delete d;
00065 }
00066
00067 int Blokkal::PostEntryStatus::error( void ) const
00068 {
00069 return d->error;
00070 }
00071
00072 QString Blokkal::PostEntryStatus::errorString( void ) const
00073 {
00074 return d->errorString;
00075 }
00076
00077 Blokkal::PostEntryStatus::Status Blokkal::PostEntryStatus::status( void ) const
00078 {
00079 return d->status;
00080 }
00081
00082 const Blokkal::Io::EntryJob * Blokkal::PostEntryStatus::job( void ) const
00083 {
00084 return d->job;
00085 }
00086
00087
00088
00089
00090 class Blokkal::PostEntryQueuePrivate
00091 {
00092 public:
00093 PostEntryQueuePrivate( void ) :
00094 entryStatus( QMap<Blokkal::Entry*, PostEntryStatus*>() ),
00095 entryList( QLinkedList<Blokkal::Entry*>() ),
00096
00097 isProcessing( TRUE ),
00098 saveNumber( 0 ),
00099 isSyncing( TRUE ) {}
00100
00101 Blokkal::PostEntryQueue instance;
00102
00103 QMap<Blokkal::Entry*, PostEntryStatus*> entryStatus;
00104 QLinkedList<Blokkal::Entry*> entryList;
00105
00106
00107 bool isProcessing;
00108 long saveNumber;
00109 bool isSyncing;
00110 };
00111
00112 K_GLOBAL_STATIC( Blokkal::PostEntryQueuePrivate, _bpeqp );
00113
00114 Blokkal::PostEntryQueue::PostEntryQueue( void ):
00115 QObject( 0 )
00116 {
00117 QList< Blokkal::Account * > accounts = Blokkal::AccountManager::self()->accounts();
00118 if( !accounts.isEmpty() ) {
00119 for( QList< Blokkal::Account * >::ConstIterator it = accounts.begin();
00120 it != accounts.end();
00121 ++it )
00122 {
00123 slotAccountAdded( *it );
00124 }
00125 }
00126
00127 connect( Blokkal::AccountManager::self(),
00128 SIGNAL( accountRegistered( Blokkal::Account * ) ),
00129 SLOT( slotAccountAdded( Blokkal::Account * ) ) );
00130 }
00131
00132 Blokkal::PostEntryQueue::~PostEntryQueue( void )
00133 {
00134 }
00135
00136 Blokkal::PostEntryQueue * Blokkal::PostEntryQueue::self( void )
00137 {
00138 return &_bpeqp->instance;
00139 }
00140
00141 void Blokkal::PostEntryQueue::queue( Blokkal::Entry * entry )
00142 {
00143 if( !entry ) {
00144 return;
00145 }
00146
00147 entry->ref();
00148
00149
00150 connect( entry,
00151 SIGNAL( entryDestroyed( Blokkal::Entry * ) ),
00152 SLOT( slotEntryDestroyed( Blokkal::Entry * ) ) );
00153 _bpeqp->entryStatus.insert( entry, new PostEntryStatus() );
00154 _bpeqp->entryList.append( entry );
00155
00156
00157 if( _bpeqp->isSyncing ) {
00158 entry->saveAs( KStandardDirs::locateLocal( "appdata", QString::fromLatin1( "queuedentries/%1" ).arg( _bpeqp->saveNumber ) ) );
00159 _bpeqp->entryStatus[entry]->d->saveNumber = _bpeqp->saveNumber;
00160 _bpeqp->saveNumber++;
00161 }
00162
00163 emit entryAdded( entry );
00164
00165 QTimer::singleShot( 0, this, SLOT( processQueue( void ) ) );
00166 }
00167
00168 bool Blokkal::PostEntryQueue::unqueue( Blokkal::Entry * entry, bool force )
00169 {
00170
00171 bool removed = FALSE;
00172 bool contains = _bpeqp->entryList.contains( entry );
00173 long saveNumber = -1;
00174
00175 if( contains ) {
00176 if( _bpeqp->entryStatus.contains( entry) ) {
00177 PostEntryStatus * status = _bpeqp->entryStatus[entry];
00178 if( status->status() != PostEntryStatus::StatusPosting
00179 || force )
00180 {
00181 saveNumber = status->d->saveNumber;
00182 delete status->d->job;
00183 delete status;
00184 _bpeqp->entryStatus.remove( entry );
00185 removed = TRUE;
00186 }
00187 }
00188 else {
00189 removed = TRUE;
00190 }
00191
00192 if( removed ) {
00193 _bpeqp->entryList.removeAll( entry );
00194 disconnect( entry,
00195 SIGNAL( entryDestroyed( Blokkal::Entry* ) ),
00196 this,
00197 SLOT( slotEntryDestroyed( Blokkal::Entry* ) ) );
00198 }
00199 }
00200
00201
00202 if( removed ) {
00203 if( _bpeqp->isSyncing && saveNumber >= 0 ) {
00204 QDir entrydir( KStandardDirs::locateLocal( "appdata", QString::fromLatin1( "queuedentries/" ) ) );
00205 QString fileName = QString::number( saveNumber );
00206 if( !entrydir.remove( fileName ) ) {
00207 kWarning() << "could not delete entry file: " << fileName << endl;
00208 }
00209 }
00210
00211 if( isEmpty() ) {
00212 _bpeqp->saveNumber = 0;
00213 }
00214
00215 emit entryRemoved( entry );
00216 }
00217
00218 return removed;
00219 }
00220
00221 int Blokkal::PostEntryQueue::count( void ) const
00222 {
00223 return _bpeqp->entryStatus.count();
00224 }
00225
00226 bool Blokkal::PostEntryQueue::isEmpty( void ) const
00227 {
00228 return !count();
00229 }
00230
00231 QLinkedList< Blokkal::Entry * > Blokkal::PostEntryQueue::entries( void ) const
00232 {
00233 return _bpeqp->entryList;
00234 }
00235
00236 void Blokkal::PostEntryQueue::processQueue( void )
00237 {
00238 if( !_bpeqp->isProcessing || isEmpty() ) {
00239 return;
00240 }
00241
00242
00243 QStringList postingAccounts;
00244 for( QLinkedList< Blokkal::Entry * >::Iterator it = _bpeqp->entryList.begin();
00245 it != _bpeqp->entryList.end();
00246 ++it )
00247 {
00248 if( !(*it)->blog()->account()->isConnected() ) {
00249 continue;
00250 }
00251
00252 if( postingAccounts.contains( (*it)->blog()->account()->id() ) ) {
00253 continue;
00254 }
00255
00256 PostEntryStatus * entryStatus = _bpeqp->entryStatus[ *it ];
00257
00258 if( entryStatus->status() != PostEntryStatus::StatusNormal ) {
00259 if( entryStatus->status() == PostEntryStatus::StatusPosting
00260 && !postingAccounts.contains( (*it)->blog()->account()->id() ) )
00261 {
00262 postingAccounts.append( (*it)->blog()->account()->id() );
00263 }
00264 continue;
00265 }
00266
00267 if( entryStatus->error() ) {
00268 continue;
00269 }
00270
00271 entryStatus->d->job = (*it)->createJob( Blokkal::Entry::PostJob );
00272 if( entryStatus->d->job ) {
00273 connect( entryStatus->d->job,
00274 SIGNAL( result( KJob * ) ),
00275 SLOT( slotJobFinished( KJob * ) ) );
00276 entryStatus->d->status = PostEntryStatus::StatusPosting;
00277 emit entryStatusChanged( *it, entryStatus );
00278 entryStatus->d->job->start();
00279 break;
00280 }
00281 else {
00282
00283
00284
00285
00286 entryStatus->d->status = PostEntryStatus::StatusError;
00287 emit entryStatusChanged( *it, entryStatus );
00288
00289 continue;
00290 }
00291 }
00292
00293
00294 }
00295
00296 void Blokkal::PostEntryQueue::slotJobFinished( KJob * _job )
00297 {
00298 Io::EntryJob * job = dynamic_cast<Io::EntryJob *>( _job );
00299 if( !job ) {
00300 kError() << "job is 0!" << endl;
00301 return;
00302 }
00303
00304 if( !_bpeqp->entryStatus.contains( job->entry() ) ) {
00305 kError() << "job is belongs to unknown entry!" << endl;
00306 return;
00307 }
00308
00309 if( job->error() ) {
00310
00311 PostEntryStatus * entryStatus = _bpeqp->entryStatus[ job->entry() ];
00312 entryStatus->d->status = PostEntryStatus::StatusError;
00313 entryStatus->d->errorString = job->errorString();
00314 entryStatus->d->error = job->error();
00315 entryStatus->d->job = 0;
00316
00317
00318 emit entryStatusChanged( job->entry(), entryStatus );
00319 emit entryFailing( job->entry() );
00320 }
00321 else {
00322 PostEntryStatus * entryStatus = _bpeqp->entryStatus[ job->entry() ];
00323 entryStatus->d->status = PostEntryStatus::StatusNormal;
00324 entryStatus->d->job = 0;
00325
00326 job->entry()->setDirty( FALSE );
00327 unqueue( job->entry() );
00328 emit entryPosted( job->entry() );
00329 job->entry()->deref();
00330 }
00331
00332 QTimer::singleShot( 0, this, SLOT( processQueue( void ) ) );
00333 }
00334
00335 void Blokkal::PostEntryQueue::slotConnectionsChanged( Blokkal::Account * account )
00336 {
00337 if( account->connectionStatus() == Blokkal::Account::Connected ) {
00338 QTimer::singleShot( 0, this, SLOT( processQueue( void ) ) );
00339 }
00340 }
00341
00342 void Blokkal::PostEntryQueue::slotAccountAdded( Blokkal::Account * account )
00343 {
00344 connect( account,
00345 SIGNAL( connectionStatusChanged( Blokkal::Account * ) ),
00346 SLOT(slotConnectionsChanged( Blokkal::Account * ) ) );
00347 }
00348
00349 const Blokkal::PostEntryStatus * Blokkal::PostEntryQueue::entryStatus( Blokkal::Entry * entry ) const
00350 {
00351 if( _bpeqp->entryStatus.contains( entry ) ) {
00352 return _bpeqp->entryStatus[ entry ];
00353 }
00354 return 0;
00355 }
00356
00357 void Blokkal::PostEntryQueue::clearError( Blokkal::Entry * entry )
00358 {
00359 if( !entry ) {
00360 return;
00361 }
00362
00363 if( !_bpeqp->entryStatus.contains( entry ) ) {
00364 return;
00365 }
00366
00367 if( !_bpeqp->entryStatus[ entry ]->error() ) {
00368 return;
00369 }
00370
00371
00372 PostEntryStatus * entryStatus = _bpeqp->entryStatus[ entry ];
00373 entryStatus->d->error = 0;
00374 entryStatus->d->errorString = QString::null;
00375 entryStatus->d->status = PostEntryStatus::StatusNormal;
00376
00377 emit entryStatusChanged( entry, entryStatus );
00378
00379 QTimer::singleShot( 0, this, SLOT( processQueue( void ) ) );
00380 }
00381
00382 void Blokkal::PostEntryQueue::slotEntryDestroyed( Blokkal::Entry * entry )
00383 {
00384 unqueue( entry, TRUE );
00385 }
00386
00387 void Blokkal::PostEntryQueue::suspendProcessing( void )
00388 {
00389 _bpeqp->isProcessing = FALSE;
00390 }
00391
00392 void Blokkal::PostEntryQueue::resumeProcessing( void )
00393 {
00394 if( _bpeqp->isProcessing ) {
00395 return;
00396 }
00397
00398 _bpeqp->isProcessing = TRUE;
00399
00400 QTimer::singleShot( 0, this, SLOT( processQueue( void ) ) );
00401 }
00402
00403 bool Blokkal::PostEntryQueue::isProcessing( void ) const
00404 {
00405 return _bpeqp->isProcessing;
00406 }
00407
00408 void Blokkal::PostEntryQueue::restoreState( void )
00409 {
00410 QDir entrydir( KStandardDirs::locateLocal( "appdata", QString::fromLatin1( "queuedentries/" ) ) );
00411 QStringList files = entrydir.entryList( QDir::Files );
00412 if( files.isEmpty() ) {
00413 return;
00414 }
00415
00416 suspendProcessing();
00417
00418 long minNumber = -1;
00419 long maxNumber = -1;
00420
00421 for( QStringList::ConstIterator it = files.begin();
00422 it != files.end();
00423 ++it )
00424 {
00425 bool ok;
00426 long currentNumber = (*it).toLong( &ok );
00427 if( !ok ) {
00428 kWarning() << "encountered invalid file in queue dir: " << *it << endl;
00429 continue;
00430 }
00431
00432 if( currentNumber < minNumber || minNumber < 0 ) {
00433 minNumber = currentNumber;
00434 }
00435
00436 if( currentNumber > maxNumber ) {
00437 maxNumber = currentNumber;
00438 }
00439 }
00440
00441
00442 _bpeqp->isSyncing = FALSE;
00443 for( long i = minNumber; i <= maxNumber; ++i ) {
00444 if( !files.contains( QString::number( i ) ) ) {
00445 continue;
00446 }
00447
00448 Entry * entry = Entry::open( entrydir.absolutePath() + QString::fromLatin1("/%1").arg( i ) , 0, FALSE );
00449 if( entry ) {
00450 entry->setDirty( TRUE );
00451 queue( entry );
00452 _bpeqp->entryStatus[entry]->d->saveNumber = i;
00453 }
00454 else {
00455 kWarning() << "failed to restore entry file: " << i << endl;
00456 }
00457 }
00458 _bpeqp->isSyncing = TRUE;
00459 _bpeqp->saveNumber = maxNumber + 1;
00460
00461 resumeProcessing();
00462 }
00463
00464 void Blokkal::PostEntryQueue::stopSyncing( void )
00465 {
00466 _bpeqp->isSyncing = FALSE;
00467 }