diff --git a/akonadi/itemcreatejob.cpp b/akonadi/itemcreatejob.cpp index 440613c11..74cf39325 100644 --- a/akonadi/itemcreatejob.cpp +++ b/akonadi/itemcreatejob.cpp @@ -1,168 +1,175 @@ /* Copyright (c) 2006 - 2007 Volker Krause Copyright (c) 2007 Robert Zwerus This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "itemcreatejob.h" #include "collection.h" #include "imapparser_p.h" #include "item.h" #include "itemserializer_p.h" #include "job_p.h" #include "protocolhelper_p.h" #include #include using namespace Akonadi; class Akonadi::ItemCreateJobPrivate : public JobPrivate { public: ItemCreateJobPrivate( ItemCreateJob *parent ) : JobPrivate( parent ) { } Collection mCollection; Item mItem; QSet mParts; Item::Id mUid; QDateTime mDatetime; QByteArray mData; }; ItemCreateJob::ItemCreateJob( const Item &item, const Collection &collection, QObject * parent ) : Job( new ItemCreateJobPrivate( this ), parent ) { Q_D( ItemCreateJob ); Q_ASSERT( !item.mimeType().isEmpty() ); d->mItem = item; d->mParts = d->mItem.loadedPayloadParts(); d->mCollection = collection; } ItemCreateJob::~ItemCreateJob() { } void ItemCreateJob::doStart() { Q_D( ItemCreateJob ); QByteArray remoteId; QList flags; flags.append( "\\MimeType[" + d->mItem.mimeType().toLatin1() + ']' ); if ( !d->mItem.remoteId().isEmpty() ) flags.append( ImapParser::quote( "\\RemoteId[" + d->mItem.remoteId().toUtf8() + ']' ) ); flags += d->mItem.flags().toList(); // switch between a normal APPEND and a multipart X-AKAPPEND, based on the number of parts if ( d->mItem.attributes().isEmpty() && ( d->mParts.isEmpty() || (d->mParts.size() == 1 && d->mParts.contains( Item::FullPayload )) ) ) { if ( d->mItem.hasPayload() ) { int version = 0; ItemSerializer::serialize( d->mItem, Item::FullPayload, d->mData, version ); } int dataSize = d->mData.size(); d->writeData( d->newTag() + " APPEND " + QByteArray::number( d->mCollection.id() ) + ' ' + QByteArray::number( d->mItem.size() ) + " (" + ImapParser::join( flags, " " ) + ") {" + QByteArray::number( dataSize ) + "}\n" ); } else { // do a multipart X-AKAPPEND QByteArray command = d->newTag() + " X-AKAPPEND " + QByteArray::number( d->mCollection.id() ) + ' ' + QByteArray::number( d->mItem.size() ) + " (" + ImapParser::join( flags, " " ) + ") "; QList partSpecs; int totalSize = 0; foreach( const QByteArray &partName, d->mParts ) { QByteArray partData; int version = 0; ItemSerializer::serialize( d->mItem, partName, partData, version ); totalSize += partData.size(); const QByteArray partId = ProtocolHelper::encodePartIdentifier( ProtocolHelper::PartPayload, partName, version ); partSpecs.append( ImapParser::quote( partId ) + ':' + QByteArray::number( partData.size() ) ); d->mData += partData; } foreach ( const Attribute* attr, d->mItem.attributes() ) { const QByteArray data = attr->serialized(); totalSize += data.size(); const QByteArray partId = ProtocolHelper::encodePartIdentifier( ProtocolHelper::PartAttribute, attr->type() ); partSpecs.append( ImapParser::quote( partId ) + ':' + QByteArray::number( data.size() ) ); d->mData += data; } command += '(' + ImapParser::join( partSpecs, "," ) + ") " + '{' + QByteArray::number( totalSize ) + "}\n"; d->writeData( command ); } } void ItemCreateJob::doHandleResponse( const QByteArray & tag, const QByteArray & data ) { Q_D( ItemCreateJob ); if ( tag == "+" ) { // ready for literal data d->writeData( d->mData ); if ( !d->mData.endsWith( '\n' ) ) d->writeData( "\n" ); return; } if ( tag == d->tag() ) { int uidNextPos = data.indexOf( "UIDNEXT" ); if ( uidNextPos != -1 ) { bool ok = false; ImapParser::parseNumber( data, d->mUid, &ok, uidNextPos + 7 ); if ( !ok ) { kDebug( 5250 ) << "Invalid UIDNEXT response to APPEND command: " << tag << data; } } int dateTimePos = data.indexOf( "DATETIME" ); if ( dateTimePos != -1 ) { int resultPos = ImapParser::parseDateTime( data, d->mDatetime, dateTimePos + 8 ); if ( resultPos == (dateTimePos + 8) ) { kDebug( 5250 ) << "Invalid DATETIME response to APPEND command: " << tag << data; } } } } Item ItemCreateJob::item() const { Q_D( const ItemCreateJob ); if ( d->mUid == 0 ) return Item(); Item item( d->mItem ); item.setId( d->mUid ); item.setRevision( 0 ); item.setModificationTime( d->mDatetime ); return item; } +Collection ItemCreateJob::collection() const +{ + Q_D( const ItemCreateJob ); + + return d->mCollection; +} + #include "itemcreatejob.moc" diff --git a/akonadi/itemcreatejob.h b/akonadi/itemcreatejob.h index c7560002b..1d89b578e 100644 --- a/akonadi/itemcreatejob.h +++ b/akonadi/itemcreatejob.h @@ -1,98 +1,104 @@ /* Copyright (c) 2006 - 2007 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef AKONADI_ITEMCREATEJOB_H #define AKONADI_ITEMCREATEJOB_H #include namespace Akonadi { class Collection; class Item; class ItemCreateJobPrivate; /** * @short Job that creates a new item in the Akonadi storage. * * This job creates a new item with all the set properties in the * given target collection. * * Example: * * @code * * // Create a contact item in the root collection * * KABC::Addressee addr; * addr.setNameFromString( "Joe Jr. Miller" ); * * Akonadi::Item item; * item.setMimeType( "text/directory" ); * item.setPayload( addr ); * * Akonadi::Collection collection = Akonadi::Collection::root(); * * Akonadi::ItemCreateJob *job = new Akonadi::ItemCreateJob( item, collection ); * * if ( job->exec() ) * qDebug() << "Contact item created successfully"; * else * qDebug() << "Error occurred"; * * @endcode * * @author Volker Krause */ class AKONADI_EXPORT ItemCreateJob : public Job { Q_OBJECT public: /** * Creates a new item create job. * * @param item The item to create. * @note It must have a mime type set. * @param collection The parent collection where the new item shall be located in. * @param parent The parent object. */ ItemCreateJob( const Item &item, const Collection &collection, QObject *parent = 0 ); /** * Destroys the item create job. */ ~ItemCreateJob(); /** * Returns the created item with the new unique id, or an invalid item if the job failed. */ Item item() const; + /** + * Returns the collection the created item is child of. This is the same collection + * passed in on in the constructor. + */ + Collection collection() const; + protected: virtual void doStart(); virtual void doHandleResponse( const QByteArray &tag, const QByteArray &data ); private: Q_DECLARE_PRIVATE( ItemCreateJob ) }; } #endif diff --git a/akonadi/itemfetchjob.cpp b/akonadi/itemfetchjob.cpp index ae2fe53ac..52fe3df34 100644 --- a/akonadi/itemfetchjob.cpp +++ b/akonadi/itemfetchjob.cpp @@ -1,319 +1,334 @@ /* Copyright (c) 2006 - 2007 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "itemfetchjob.h" #include "attributefactory.h" #include "collection.h" #include "collectionselectjob_p.h" #include "imapparser_p.h" #include "itemfetchscope.h" #include "itemserializer_p.h" #include "itemserializerplugin.h" #include "job_p.h" #include "entity_p.h" #include "protocol_p.h" #include "protocolhelper_p.h" #include #include #include #include #include using namespace Akonadi; class Akonadi::ItemFetchJobPrivate : public JobPrivate { public: ItemFetchJobPrivate( ItemFetchJob *parent ) : JobPrivate( parent ) { } void timeout() { Q_Q( ItemFetchJob ); mEmitTimer->stop(); // in case we are called by result() if ( !mPendingItems.isEmpty() ) { emit q->itemsReceived( mPendingItems ); mPendingItems.clear(); } } void startFetchJob(); void selectDone( KJob * job ); Q_DECLARE_PUBLIC( ItemFetchJob ) Collection mCollection; Item mItem; Item::List mItems; ItemFetchScope mFetchScope; Item::List mPendingItems; // items pending for emitting itemsReceived() QTimer* mEmitTimer; }; void ItemFetchJobPrivate::startFetchJob() { QByteArray command = newTag(); if ( mItem.isValid() ) command += " " AKONADI_CMD_UID " " AKONADI_CMD_ITEMFETCH " " + QByteArray::number( mItem.id() ); else if ( !mItem.remoteId().isEmpty() ) command += " " AKONADI_CMD_RID " " AKONADI_CMD_ITEMFETCH " " + mItem.remoteId().toUtf8(); else command += " " AKONADI_CMD_ITEMFETCH " 1:*"; if ( mFetchScope.fullPayload() ) command += " " AKONADI_PARAM_FULLPAYLOAD; if ( mFetchScope.allAttributes() ) command += " " AKONADI_PARAM_ALLATTRIBUTES; if ( mFetchScope.cacheOnly() ) command += " " AKONADI_PARAM_CACHEONLY; //TODO: detect somehow if server supports external payload attribute command += " " AKONADI_PARAM_EXTERNALPAYLOAD; command += " (UID REMOTEID COLLECTIONID FLAGS SIZE DATETIME"; foreach ( const QByteArray &part, mFetchScope.payloadParts() ) command += ' ' + ProtocolHelper::encodePartIdentifier( ProtocolHelper::PartPayload, part ); foreach ( const QByteArray &part, mFetchScope.attributes() ) command += ' ' + ProtocolHelper::encodePartIdentifier( ProtocolHelper::PartAttribute, part ); command += ")\n"; writeData( command ); } void ItemFetchJobPrivate::selectDone( KJob * job ) { if ( !job->error() ) // the collection is now selected, fetch the message(s) startFetchJob(); } ItemFetchJob::ItemFetchJob( const Collection &collection, QObject * parent ) : Job( new ItemFetchJobPrivate( this ), parent ) { Q_D( ItemFetchJob ); d->mEmitTimer = new QTimer( this ); d->mEmitTimer->setSingleShot( true ); d->mEmitTimer->setInterval( 100 ); connect( d->mEmitTimer, SIGNAL(timeout()), this, SLOT(timeout()) ); connect( this, SIGNAL(result(KJob*)), this, SLOT(timeout()) ); d->mCollection = collection; } ItemFetchJob::ItemFetchJob( const Item & item, QObject * parent) : Job( new ItemFetchJobPrivate( this ), parent ) { Q_D( ItemFetchJob ); d->mEmitTimer = new QTimer( this ); d->mEmitTimer->setSingleShot( true ); d->mEmitTimer->setInterval( 100 ); connect( d->mEmitTimer, SIGNAL(timeout()), this, SLOT(timeout()) ); connect( this, SIGNAL(result(KJob*)), this, SLOT(timeout()) ); d->mCollection = Collection::root(); d->mItem = item; } ItemFetchJob::~ItemFetchJob() { } void ItemFetchJob::doStart() { Q_D( ItemFetchJob ); if ( !d->mItem.isValid() ) { // collection content listing if ( d->mCollection == Collection::root() ) { setErrorText( QLatin1String("Cannot list root collection.") ); setError( Unknown ); emitResult(); } CollectionSelectJob *job = new CollectionSelectJob( d->mCollection, this ); connect( job, SIGNAL(result(KJob*)), SLOT(selectDone(KJob*)) ); addSubjob( job ); } else d->startFetchJob(); } void ItemFetchJob::doHandleResponse( const QByteArray & tag, const QByteArray & data ) { Q_D( ItemFetchJob ); if ( tag == "*" ) { int begin = data.indexOf( "FETCH" ); if ( begin >= 0 ) { // split fetch response into key/value pairs QList fetchResponse; ImapParser::parseParenthesizedList( data, fetchResponse, begin + 6 ); // create a new item object Item::Id uid = -1; int rev = -1; QString rid; QString mimeType; Entity::Id cid = -1; for ( int i = 0; i < fetchResponse.count() - 1; i += 2 ) { const QByteArray key = fetchResponse.value( i ); const QByteArray value = fetchResponse.value( i + 1 ); if ( key == "UID" ) uid = value.toLongLong(); else if ( key == "REV" ) rev = value.toInt(); else if ( key == "REMOTEID" ) { if ( !value.isEmpty() ) rid = QString::fromUtf8( value ); else rid.clear(); } else if ( key == "COLLECTIONID" ) { cid = value.toInt(); } else if ( key == "MIMETYPE" ) mimeType = QString::fromLatin1( value ); } if ( uid < 0 || rev < 0 || mimeType.isEmpty() ) { kWarning( 5250 ) << "Broken fetch response: UID, RID, REV or MIMETYPE missing!"; return; } Item item( uid ); item.setRemoteId( rid ); item.setRevision( rev ); item.setMimeType( mimeType ); item.setStorageCollectionId( cid ); if ( !item.isValid() ) return; // parse fetch response fields for ( int i = 0; i < fetchResponse.count() - 1; i += 2 ) { const QByteArray key = fetchResponse.value( i ); // skip stuff we dealt with already if ( key == "UID" || key == "REV" || key == "REMOTEID" || key == "MIMETYPE" || key == "COLLECTIONID") continue; // flags if ( key == "FLAGS" ) { QList flags; ImapParser::parseParenthesizedList( fetchResponse[i + 1], flags ); foreach ( const QByteArray &flag, flags ) { item.setFlag( flag ); } } else if ( key == "SIZE" ) { const quint64 size = fetchResponse[i + 1].toLongLong(); item.setSize( size ); } else if ( key == "DATETIME" ) { QDateTime datetime; ImapParser::parseDateTime( fetchResponse[i + 1], datetime ); item.setModificationTime( datetime ); } else { int version = 0; QByteArray plainKey( key ); ProtocolHelper::PartNamespace ns; ImapParser::splitVersionedKey( key, plainKey, version ); plainKey = ProtocolHelper::decodePartIdentifier( plainKey, ns ); switch ( ns ) { case ProtocolHelper::PartPayload: { bool isExternal = false; QByteArray fileKey = fetchResponse.value( i + 1 ); if (fileKey == "[FILE]") { isExternal = true; i++; kDebug( 5250 ) << "Payload is external: " << isExternal << " filename: " << fetchResponse.value( i + 1 ); } ItemSerializer::deserialize( item, plainKey, fetchResponse.value( i + 1 ), version, isExternal ); break; } case ProtocolHelper::PartAttribute: { Attribute* attr = AttributeFactory::createAttribute( plainKey ); Q_ASSERT( attr ); if ( fetchResponse.value( i + 1 ) == "[FILE]" ) { ++i; QFile f( QString::fromUtf8( fetchResponse.value( i + 1 ) ) ); if ( f.open( QFile::ReadOnly ) ) attr->deserialize( f.readAll() ); else { kWarning() << "Failed to open attribute file: " << fetchResponse.value( i + 1 ); delete attr; } } else { attr->deserialize( fetchResponse.value( i + 1 ) ); } item.addAttribute( attr ); break; } case ProtocolHelper::PartGlobal: default: kWarning() << "Unknown item part type:" << key; } } } item.d_ptr->resetChangeLog(); d->mItems.append( item ); d->mPendingItems.append( item ); if ( !d->mEmitTimer->isActive() ) d->mEmitTimer->start(); return; } } kDebug( 5250 ) << "Unhandled response: " << tag << data; } Item::List ItemFetchJob::items() const { Q_D( const ItemFetchJob ); return d->mItems; } void ItemFetchJob::setFetchScope( ItemFetchScope &fetchScope ) { Q_D( ItemFetchJob ); d->mFetchScope = fetchScope; } ItemFetchScope &ItemFetchJob::fetchScope() { Q_D( ItemFetchJob ); return d->mFetchScope; } +Item ItemFetchJob::item() const +{ + Q_D( const ItemFetchJob ); + + return d->mItem; +} + +Collection ItemFetchJob::collection() const +{ + Q_D( const ItemFetchJob ); + + return d->mCollection; +} + void ItemFetchJob::setCollection(const Akonadi::Collection& collection) { Q_D( ItemFetchJob ); + d->mCollection = collection; } #include "itemfetchjob.moc" diff --git a/akonadi/itemfetchjob.h b/akonadi/itemfetchjob.h index d4faed9d9..e9ccaf4e4 100644 --- a/akonadi/itemfetchjob.h +++ b/akonadi/itemfetchjob.h @@ -1,157 +1,170 @@ /* Copyright (c) 2006 - 2007 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef AKONADI_ITEMFETCHJOB_H #define AKONADI_ITEMFETCHJOB_H #include #include namespace Akonadi { class Collection; class ItemFetchJobPrivate; class ItemFetchScope; /** * @short Job that fetches items from the Akonadi storage. * * This class is used to fetch items from the Akonadi storage. * Which parts of the items (e.g. headers only, attachments or all) * can be specified by the ItemFetchScope. * * Example: * * @code * * // Fetch all items with full payload from the root collection * Akonadi::ItemFetchJob *job = new Akonadi::ItemFetchJob( Akonadi::Collection::root() ); * job->fetchScope().fetchFullPayload(); * * if ( job->exec() ) { * Akonadi::Item::List items = job->items(); * foreach( const Akonadi::Item &item, items ) { * qDebug() << "Item ID:" << item.id(); * } * } else { * qDebug() << "Error occurred"; * } * * @endcode * * @author Volker Krause */ class AKONADI_EXPORT ItemFetchJob : public Job { Q_OBJECT public: /** * Creates a new item fetch job that retrieves all items inside the given collection. * * @param collection The parent collection to fetch all items from. * @param parent The parent object. */ explicit ItemFetchJob( const Collection &collection, QObject *parent = 0 ); /** * Creates a new item fetch job that retrieves the specified item. * If the item has an uid set, this is used to identify the item on the Akonadi * server. If only a remote identifier is available, that one is used. * However, as remote identifier are not necessarily globally unique, you * need to specify the resource and/or collection to search in in that case, * using setCollection() or Akonadi::ResourceSelectJob. * * @param item The item to fetch. * @param parent The parent object. */ explicit ItemFetchJob( const Item &item, QObject *parent = 0 ); /** * Destroys the item fetch job. */ virtual ~ItemFetchJob(); /** * Returns the fetched item. * * @note The items are invalid before the result( KJob* ) * signal has been emitted or if an error occurred. */ Item::List items() const; /** * Sets the item fetch scope. * * The ItemFetchScope controls how much of an item's data is fetched * from the server, e.g. whether to fetch the full item payload or * only meta data. * * @param fetchScope The new scope for item fetch operations. * * @see fetchScope() */ void setFetchScope( ItemFetchScope &fetchScope ); /** * Returns the item fetch scope. * * Since this returns a reference it can be used to conveniently modify the * current scope in-place, i.e. by calling a method on the returned reference * without storing it in a local variable. See the ItemFetchScope documentation * for an example. * * @return a reference to the current item fetch scope * * @see setFetchScope() for replacing the current item fetch scope */ ItemFetchScope &fetchScope(); + /** + * Returns the item passed in on in the constructor or an invalid + * item if another constructor, e.g. those with the collection, + * was called. + */ + Item item() const; + + /** + * Returns the collection passed in on in the constructor or set with the + * setCollection method. + */ + Collection collection() const; + /** Specifies the collection the item is in. This is only required when retrieving an item based on its remote id which might not be unique globally. @see Akonadi::ResourceSelectJob */ void setCollection( const Collection &collection ); Q_SIGNALS: /** * This signal is emitted when the items are fetched completely. * * @param items The fetched items. */ void itemsReceived( const Akonadi::Item::List &items ); protected: virtual void doStart(); virtual void doHandleResponse( const QByteArray &tag, const QByteArray &data ); private: Q_DECLARE_PRIVATE( ItemFetchJob ) //@cond PRIVATE Q_PRIVATE_SLOT( d_func(), void selectDone( KJob* ) ) Q_PRIVATE_SLOT( d_func(), void timeout() ) //@endcond }; } #endif diff --git a/akonadi/monitor.h b/akonadi/monitor.h index 0fcac9143..7b8f82970 100644 --- a/akonadi/monitor.h +++ b/akonadi/monitor.h @@ -1,339 +1,340 @@ /* Copyright (c) 2006 - 2007 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef AKONADI_MONITOR_H #define AKONADI_MONITOR_H #include #include #include namespace Akonadi { class CollectionStatistics; class Item; class ItemFetchScope; class MonitorPrivate; class Session; /** * @short Monitors an item or collection for changes. * * The Monitor emits signals if some of these objects are changed or * removed or new ones are added to the Akonadi storage. * * Optionally, the changed objects can be fetched automatically from the server. * To enable this, see fetchCollection(), fetchItemMetaData(), fetchItemData(). * * @todo: distinguish between monitoring collection properties and collection content. * @todo: special case for collection content counts changed * * @author Volker Krause */ class AKONADI_EXPORT Monitor : public QObject { Q_OBJECT public: /** * Creates a new monitor. * * @param parent The parent object. */ explicit Monitor( QObject *parent = 0 ); /** * Destroys the monitor. */ virtual ~Monitor(); /** * Sets whether the specified collection shall be monitored for changes. * * @param collection The collection to monitor. * If this collection is Collection::root(), all collections * in the Akonadi storage will be monitored. */ void setCollectionMonitored( const Collection &collection, bool monitored = true ); /** * Sets whether the specified item shall be monitored for changes. * * @param item The item to monitor. */ void setItemMonitored( const Item &item, bool monitored = true ); /** * Sets whether the specified resource shall be monitored for changes. * * @param resource The resource identifier. */ void setResourceMonitored( const QByteArray &resource, bool monitored = true ); /** * Sets whether objects of the specified mime type shall be monitored for changes. * * @param mimetype The mime type to monitor. */ void setMimeTypeMonitored( const QString &mimetype, bool monitored = true ); /** * Sets whether all items shall be monitored. */ void setAllMonitored( bool monitored = true ); /** * Ignores all change notifications caused by the given session. * * @param session The session you want to ignore. */ void ignoreSession( Session *session ); /** * Enables automatic fetching of changed collections from the Akonadi storage. * * @param enable @c true enables automatic fetching, @c false disables automatic fetching. */ void fetchCollection( bool enable ); /** * Enables automatic fetching of changed collection statistics information from * the Akonadi storage. * * @param enable @c true to enables automatic fetching, @c false disables automatic fetching. */ void fetchCollectionStatistics( bool enable ); /** * Sets the item fetch scope. * * Controls how much of an item's data is fetched from the server, e.g. * whether to fetch the full item payload or only meta data. * * @param fetchScope The new scope for item fetch operations. * * @see itemFetchScope() */ void setItemFetchScope( const ItemFetchScope &fetchScope ); /** * Returns the item fetch scope. * * Since this returns a reference it can be used to conveniently modify the * current scope in-place, i.e. by calling a method on the returned reference * without storing it in a local variable. See the ItemFetchScope documentation * for an example. * * @return a reference to the current item fetch scope * * @see setItemFetchScope() for replacing the current item fetch scope */ ItemFetchScope &itemFetchScope(); /** * Returns the list of collections being monitored. * * @since 4.3 */ Collection::List collectionsMonitored() const; /** * Returns the set of items being monitored. * * @since 4.3 */ QList itemsMonitored() const; /** * Returns the set of mimetypes being monitored. * * @since 4.3 */ QStringList mimeTypesMonitored() const; /** * Returns the set of identifiers for resources being monitored. * * @since 4.3 */ QList resourcesMonitored() const; /** * Returns true if everything is being monitored. * * @since 4.3 */ bool isAllMonitored() const; Q_SIGNALS: /** * This signal is emitted if a monitored item has changed, e.g. item parts have been modified. * * @param item The changed item. * @param partIdentifiers The identifiers of the item parts that has been changed. */ void itemChanged( const Akonadi::Item &item, const QSet &partIdentifiers ); /** * This signal is emitted if a monitored item has been moved between two collections * * @param item The moved item. * @param collectionSource The collection the item has been moved from. * @param collectionDestination The collection the item has been moved to. */ void itemMoved( const Akonadi::Item &item, const Akonadi::Collection &collectionSource, const Akonadi::Collection &collectionDestination ); /** * This signal is emitted if an item has been added to a monitored collection in the Akonadi storage. * * @param item The new item. * @param collection The collection the item has been added to. */ void itemAdded( const Akonadi::Item &item, const Akonadi::Collection &collection ); /** * This signal is emitted if * - a monitored item has been removed from the Akonadi storage * or * - a item has been removed from a monitored collection. * * @param item The removed item. */ - void itemRemoved( const Akonadi::Item &item ); + void itemRemoved( const Akonadi::Item &item ); //TODO remove if we are sure nobody uses it any longer + void itemRemoved( const Akonadi::Item &item, const Akonadi::Collection &collection ); /** * This signal is emitted if a reference to an item is added to a virtual collection. * @param item The linked item. * @param collection The collection the item is linked to. * * @since 4.2 */ void itemLinked( const Akonadi::Item &item, const Akonadi::Collection &collection ); /** * This signal is emitted if a reference to an item is removed from a virtual collection. * @param item The unlinked item. * @param collection The collection the item is unlinked from. * * @since 4.2 */ void itemUnlinked( const Akonadi::Item &item, const Akonadi::Collection &collection ); /** * This signal is emitted if a new collection has been added to a monitored collection in the Akonadi storage. * * @param collection The new collection. * @param parent The parent collection. */ void collectionAdded( const Akonadi::Collection &collection, const Akonadi::Collection &parent ); /** * This signal is emitted if a monitored collection has been changed (properties or content) * or has been reparented. * * @param collection The changed collection. */ void collectionChanged( const Akonadi::Collection &collection ); /** * This signal is emitted if a monitored collection has been removed from the Akonadi storage. * * @param collection The removed collection. */ void collectionRemoved( const Akonadi::Collection &collection ); /** * This signal is emitted if the statistics information of a monitored collection * has changed. * * @param id The collection identifier of the changed collection. * @param statistics The updated collection statistics, invalid if automatic * fetching of statistics changes is disabled. */ void collectionStatisticsChanged( Akonadi::Collection::Id id, const Akonadi::CollectionStatistics &statistics ); /** * This signal is emitted if the Monitor starts or stops monitoring @p collection explicitly. * @param collection The collection * @param monitored Whether the collection is now being monitored or not. * * @since 4.3 */ void collectionMonitored( const Akonadi::Collection &collection, bool monitored ); /** * This signal is emitted if the Monitor starts or stops monitoring @p item explicitly. * @param item The item * @param monitored Whether the item is now being monitored or not. * * @since 4.3 */ void itemMonitored( const Akonadi::Item &item, bool monitored ); /** * This signal is emitted if the Monitor starts or stops monitoring the resource with the identifier @p identifier explicitly. * @param identifier The identifier of the resource. * @param monitored Whether the resource is now being monitored or not. * * @since 4.3 */ void resourceMonitored( const QByteArray &identifier, bool monitored ); /** * This signal is emitted if the Monitor starts or stops monitoring @p mimeType explicitly. * @param mimeType The mimeType. * @param monitored Whether the mimeType is now being monitored or not. * * @since 4.3 */ void mimeTypeMonitored( const QString &mimeType, bool monitored ); /** * This signal is emitted if the Monitor starts or stops monitoring everything. * @param monitored Whether everything is now being monitored or not. * * @since 4.3 */ void allMonitored( bool monitored ); protected: //@cond PRIVATE MonitorPrivate *d_ptr; explicit Monitor( MonitorPrivate *d, QObject *parent = 0 ); //@endcond private: Q_DECLARE_PRIVATE( Monitor ) //@cond PRIVATE Q_PRIVATE_SLOT( d_ptr, void slotSessionDestroyed( QObject* ) ) Q_PRIVATE_SLOT( d_ptr, void slotStatisticsChangedFinished( KJob* ) ) Q_PRIVATE_SLOT( d_ptr, void slotFlushRecentlyChangedCollections() ) Q_PRIVATE_SLOT( d_ptr, void slotNotify( const Akonadi::NotificationMessage::List& ) ) Q_PRIVATE_SLOT( d_ptr, void slotItemJobFinished( KJob* ) ) Q_PRIVATE_SLOT( d_ptr, void slotCollectionJobFinished( KJob* ) ) //@endcond }; } #endif diff --git a/akonadi/monitor_p.cpp b/akonadi/monitor_p.cpp index ed94fefd8..e2b1adcd6 100644 --- a/akonadi/monitor_p.cpp +++ b/akonadi/monitor_p.cpp @@ -1,390 +1,391 @@ /* Copyright (c) 2007 Tobias Koenig This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // @cond PRIVATE #include "monitor_p.h" #include "collectionfetchjob.h" #include "collectionstatistics.h" #include "itemfetchjob.h" #include "notificationmessage_p.h" #include "session.h" #include using namespace Akonadi; MonitorPrivate::MonitorPrivate(Monitor * parent) : q_ptr( parent ), nm( 0 ), monitorAll( false ), fetchCollection( false ), fetchCollectionStatistics( false ) { } bool MonitorPrivate::connectToNotificationManager() { NotificationMessage::registerDBusTypes(); if ( !nm ) nm = new org::freedesktop::Akonadi::NotificationManager( QLatin1String( "org.freedesktop.Akonadi" ), QLatin1String( "/notifications" ), QDBusConnection::sessionBus(), q_ptr ); else return true; if ( !nm ) { kWarning( 5250 ) << "Unable to connect to notification manager"; } else { QObject::connect( nm, SIGNAL(notify(Akonadi::NotificationMessage::List)), q_ptr, SLOT(slotNotify(Akonadi::NotificationMessage::List)) ); return true; } return false; } bool MonitorPrivate::acceptNotification(const NotificationMessage & msg) { if ( isSessionIgnored( msg.sessionId() ) ) return false; switch ( msg.type() ) { case NotificationMessage::InvalidType: kWarning( 5250 ) << "Received invalid change notification!"; return false; case NotificationMessage::Item: return isItemMonitored( msg.uid(), msg.parentCollection(), msg.parentDestCollection(), msg.mimeType(), msg.resource() ) || isCollectionMonitored( msg.parentCollection(), msg.resource() ) || isCollectionMonitored( msg.parentDestCollection(), msg.resource() ); case NotificationMessage::Collection: return isCollectionMonitored( msg.uid(), msg.resource() ) || isCollectionMonitored( msg.parentCollection(), msg.resource() ) || isCollectionMonitored( msg.parentDestCollection(), msg.resource() ); } Q_ASSERT( false ); return false; } bool MonitorPrivate::processNotification(const NotificationMessage & msg) { if ( !acceptNotification( msg ) ) return false; if ( msg.type() == NotificationMessage::Item ) { notifyCollectionStatisticsWatchers( msg.parentCollection(), msg.resource() ); if ( !mItemFetchScope.isEmpty() && ( msg.operation() == NotificationMessage::Add || msg.operation() == NotificationMessage::Move || msg.operation() == NotificationMessage::Link ) ) { Item item( msg.uid() ); item.setRemoteId( msg.remoteId() ); ItemCollectionFetchJob *job = new ItemCollectionFetchJob( item, msg.parentCollection(), msg.parentDestCollection(), q_ptr ); job->setFetchScope( mItemFetchScope ); pendingJobs.insert( job, msg ); QObject::connect( job, SIGNAL(result(KJob*)), q_ptr, SLOT(slotItemJobFinished(KJob*)) ); return true; } if ( !mItemFetchScope.isEmpty() && msg.operation() == NotificationMessage::Modify ) { Item item( msg.uid() ); item.setRemoteId( msg.remoteId() ); ItemFetchJob *job = new ItemFetchJob( item, q_ptr ); job->setFetchScope( mItemFetchScope ); pendingJobs.insert( job, msg ); QObject::connect( job, SIGNAL(result(KJob*)), q_ptr, SLOT(slotItemJobFinished(KJob*)) ); return true; } emitItemNotification( msg ); return true; } else if ( msg.type() == NotificationMessage::Collection ) { if ( msg.operation() != NotificationMessage::Remove && fetchCollection ) { Collection::List list; list << Collection( msg.uid() ); if ( msg.operation() == NotificationMessage::Add ) list << Collection( msg.parentCollection() ); CollectionFetchJob *job = new CollectionFetchJob( list, q_ptr ); pendingJobs.insert( job, msg ); QObject::connect( job, SIGNAL(result(KJob*)), q_ptr, SLOT(slotCollectionJobFinished(KJob*)) ); return true; } if ( msg.operation() == NotificationMessage::Remove ) { // no need for statistics updates anymore recentlyChangedCollections.remove( msg.uid() ); } emitCollectionNotification( msg ); return true; } else { kWarning( 5250 ) << "Received unknown change notification!"; } return false; } void MonitorPrivate::slotSessionDestroyed( QObject * object ) { Session* session = qobject_cast( object ); if ( session ) sessions.removeAll( session->sessionId() ); } void MonitorPrivate::slotStatisticsChangedFinished( KJob* job ) { if ( job->error() ) { kWarning( 5250 ) << "Error on fetching collection statistics: " << job->errorText(); } else { CollectionStatisticsJob *statisticsJob = static_cast( job ); emit q_ptr->collectionStatisticsChanged( statisticsJob->collection().id(), statisticsJob->statistics() ); } } void MonitorPrivate::slotFlushRecentlyChangedCollections() { foreach( Collection::Id collection, recentlyChangedCollections ) { if ( fetchCollectionStatistics ) { fetchStatistics( collection ); } else { static const CollectionStatistics dummyStatistics; emit q_ptr->collectionStatisticsChanged( collection, dummyStatistics ); } } recentlyChangedCollections.clear(); } void MonitorPrivate::slotNotify( const NotificationMessage::List &msgs ) { foreach ( const NotificationMessage &msg, msgs ) processNotification( msg ); } void MonitorPrivate::emitItemNotification( const NotificationMessage &msg, const Item &item, const Collection &collection, const Collection &collectionDest ) { Q_ASSERT( msg.type() == NotificationMessage::Item ); Collection col = collection; Collection colDest = collectionDest; if ( !col.isValid() ) { col = Collection( msg.parentCollection() ); col.setResource( QString::fromUtf8( msg.resource() ) ); } if ( !colDest.isValid() ) { colDest = Collection( msg.parentDestCollection() ); // FIXME setResource here required ? } Item it = item; if ( !it.isValid() ) { it = Item( msg.uid() ); it.setRemoteId( msg.remoteId() ); it.setMimeType( msg.mimeType() ); } switch ( msg.operation() ) { case NotificationMessage::Add: emit q_ptr->itemAdded( it, col ); break; case NotificationMessage::Modify: emit q_ptr->itemChanged( it, msg.itemParts() ); break; case NotificationMessage::Move: emit q_ptr->itemMoved( it, col, colDest ); break; case NotificationMessage::Remove: emit q_ptr->itemRemoved( it ); + emit q_ptr->itemRemoved( it, col ); break; case NotificationMessage::Link: emit q_ptr->itemLinked( it, col ); break; case NotificationMessage::Unlink: emit q_ptr->itemUnlinked( it, col ); break; default: kDebug() << "Unknown operation type" << msg.operation() << "in item change notification"; break; } } void MonitorPrivate::emitCollectionNotification( const NotificationMessage &msg, const Collection &col, const Collection &par ) { Q_ASSERT( msg.type() == NotificationMessage::Collection ); Collection collection = col; if ( !collection.isValid() ) { collection = Collection( msg.uid() ); collection.setParent( msg.parentCollection() ); collection.setResource( QString::fromUtf8( msg.resource() ) ); collection.setRemoteId( msg.remoteId() ); } Collection parent = par; if ( !parent.isValid() ) parent = Collection( msg.parentCollection() ); switch ( msg.operation() ) { case NotificationMessage::Add: emit q_ptr->collectionAdded( collection, parent ); break; case NotificationMessage::Modify: emit q_ptr->collectionChanged( collection ); break; case NotificationMessage::Remove: emit q_ptr->collectionRemoved( collection ); break; default: Q_ASSERT_X( false, "MonitorPrivate::emitCollectionNotification", "Invalid enum value" ); } } void MonitorPrivate::slotItemJobFinished( KJob* job ) { if ( !pendingJobs.contains( job ) ) { kWarning( 5250 ) << "Unknown job - wtf is going on here?"; return; } NotificationMessage msg = pendingJobs.take( job ); Item item; Collection col; Collection destCol; if ( job->error() ) { kWarning( 5250 ) << "Error on fetching item:" << job->errorText(); } else { ItemFetchJob *fetchJob = qobject_cast( job ); if ( fetchJob && fetchJob->items().count() > 0 ) item = fetchJob->items().first(); ItemCollectionFetchJob *cfjob = qobject_cast( job ); if ( cfjob ) { item = cfjob->item(); col = cfjob->collection(); destCol = cfjob->destCollection(); } } emitItemNotification( msg, item, col, destCol ); } void MonitorPrivate::slotCollectionJobFinished( KJob* job ) { if ( !pendingJobs.contains( job ) ) { kWarning( 5250 ) << "Unknown job - wtf is going on here?"; return; } NotificationMessage msg = pendingJobs.take( job ); if ( job->error() ) { kWarning( 5250 ) << "Error on fetching collection:" << job->errorText(); } else { Collection col, parent; CollectionFetchJob *listJob = qobject_cast( job ); if ( listJob && listJob->collections().count() > 0 ) col = listJob->collections().first(); if ( listJob && listJob->collections().count() > 1 && msg.operation() == NotificationMessage::Add ) { parent = listJob->collections().at( 1 ); if ( col.id() != msg.uid() ) qSwap( col, parent ); } emitCollectionNotification( msg, col, parent ); } } ItemCollectionFetchJob::ItemCollectionFetchJob( const Item &item, Collection::Id collectionId, Collection::Id destCollectionId, QObject *parent ) : Job( parent ), mReferenceItem( item ), mCollectionId( collectionId ), mDestCollectionId( destCollectionId ) { } ItemCollectionFetchJob::~ItemCollectionFetchJob() { } Item ItemCollectionFetchJob::item() const { return mItem; } Collection ItemCollectionFetchJob::collection() const { return mCollection; } Collection ItemCollectionFetchJob::destCollection() const { return mDestCollection; } void ItemCollectionFetchJob::setFetchScope( const ItemFetchScope &fetchScope ) { mFetchScope = fetchScope; } void ItemCollectionFetchJob::doStart() { CollectionFetchJob *listJob = new CollectionFetchJob( Collection( mCollectionId ), CollectionFetchJob::Base, this ); connect( listJob, SIGNAL( result( KJob* ) ), SLOT( collectionJobDone( KJob* ) ) ); addSubjob( listJob ); if ( mDestCollectionId > 0 ) { CollectionFetchJob *destListJob = new CollectionFetchJob( Collection( mDestCollectionId ), CollectionFetchJob::Base, this ); connect( destListJob, SIGNAL( result( KJob* ) ), SLOT( destCollectionJobDone( KJob* ) ) ); addSubjob( destListJob ); } ItemFetchJob *fetchJob = new ItemFetchJob( mReferenceItem, this ); fetchJob->setFetchScope( mFetchScope ); connect( fetchJob, SIGNAL( result( KJob* ) ), SLOT( itemJobDone( KJob* ) ) ); addSubjob( fetchJob ); } void ItemCollectionFetchJob::collectionJobDone( KJob* job ) { if ( !job->error() ) { CollectionFetchJob *listJob = qobject_cast( job ); if ( listJob->collections().isEmpty() ) { setError( 1 ); setErrorText( QLatin1String( "No collection found" ) ); } else mCollection = listJob->collections().first(); } } void ItemCollectionFetchJob::destCollectionJobDone( KJob* job ) { if ( !job->error() ) { CollectionFetchJob *listJob = qobject_cast( job ); if ( listJob->collections().isEmpty() ) { setError( 1 ); setErrorText( QLatin1String( "No collection found" ) ); } else mDestCollection = listJob->collections().first(); } } void ItemCollectionFetchJob::itemJobDone( KJob* job ) { if ( !job->error() ) { ItemFetchJob *fetchJob = qobject_cast( job ); if ( fetchJob->items().isEmpty() ) { setError( 2 ); setErrorText( QLatin1String( "No item found" ) ); } else mItem = fetchJob->items().first(); emitResult(); } } // @endcond #include "monitor_p.moc"