diff --git a/akonadi/CMakeLists.txt b/akonadi/CMakeLists.txt index ca2bb3990..20585fa8d 100644 --- a/akonadi/CMakeLists.txt +++ b/akonadi/CMakeLists.txt @@ -1,235 +1,236 @@ project(akonadi-kde) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${KDE4_ENABLE_EXCEPTIONS}" ) if(CMAKE_COMPILE_GCOV) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") endif(CMAKE_COMPILE_GCOV) if (KDE4_BUILD_TESTS) # only with this macro the AKONADI_TESTS_EXPORT macro will do something add_definitions(-DCOMPILING_TESTS) add_subdirectory( tests ) endif (KDE4_BUILD_TESTS) add_definitions( -DQT_NO_CAST_FROM_ASCII ) add_definitions( -DQT_NO_CAST_TO_ASCII ) add_subdirectory( kabc ) add_subdirectory( kmime ) include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${QT_QTDBUS_INCLUDE_DIR} ${Boost_INCLUDE_DIR} ${KDE4_INCLUDE_DIR} ${AKONADI_INCLUDE_DIR} ${AKONADI_INCLUDE_DIR}/akonadi/private ) # libakonadi-kde set( akonadikde_LIB_SRC entity.cpp # keep it at top to not break enable-final agentbase.cpp agentfilterproxymodel.cpp agentinstance.cpp agentinstancecreatejob.cpp agentinstancemodel.cpp agentinstancewidget.cpp agentmanager.cpp agenttype.cpp agenttypemodel.cpp agenttypewidget.cpp agenttypedialog.cpp attribute.cpp attributefactory.cpp cachepolicy.cpp cachepolicypage.cpp changerecorder.cpp collection.cpp collectioncopyjob.cpp collectioncreatejob.cpp collectiondeletejob.cpp collectiondialog.cpp collectionfilterproxymodel.cpp collectiongeneralpropertiespage.cpp collectionfetchjob.cpp collectionmodel.cpp collectionmodel_p.cpp collectionmodifyjob.cpp + collectionmovejob.cpp collectionpathresolver.cpp collectionpropertiesdialog.cpp collectionpropertiespage.cpp collectionrequester.cpp collectionrightsattribute.cpp collectionselectjob.cpp collectionstatistics.cpp collectionstatisticsdelegate.cpp collectionstatisticsjob.cpp collectionstatisticsmodel.cpp collectionsync.cpp collectionview.cpp control.cpp entitydisplayattribute.cpp #entitysortfilterproxymodel.cpp erroroverlay.cpp exception.cpp expungejob.cpp firstrun.cpp flatcollectionproxymodel.cpp item.cpp itemcreatejob.cpp itemcopyjob.cpp itemdeletejob.cpp itemfetchjob.cpp itemfetchscope.cpp itemmodel.cpp itemmonitor.cpp itemmovejob.cpp itemserializer.cpp itemserializerplugin.cpp itemmodifyjob.cpp itemsync.cpp itemview.cpp job.cpp linkjob.cpp mimetypechecker.cpp monitor.cpp monitor_p.cpp pastehelper.cpp protocolhelper.cpp resourcebase.cpp resourcescheduler.cpp resourceselectjob.cpp searchcreatejob.cpp selftestdialog.cpp session.cpp servermanager.cpp standardactionmanager.cpp subscriptionjob.cpp subscriptionchangeproxymodel.cpp subscriptiondialog.cpp subscriptionmodel.cpp transactionjobs.cpp transactionsequence.cpp unlinkjob.cpp # Temporary until ported to Qt-plugin framework pluginloader.cpp ) # DBus interfaces and adaptors set(akonadi_xml ${AKONADI_DBUS_INTERFACES_DIR}/org.freedesktop.Akonadi.NotificationManager.xml) set_source_files_properties(${akonadi_xml} PROPERTIES INCLUDE "notificationmessage_p.h") qt4_add_dbus_interface( akonadikde_LIB_SRC ${akonadi_xml} notificationmanagerinterface ) qt4_add_dbus_interfaces( akonadikde_LIB_SRC ${AKONADI_DBUS_INTERFACES_DIR}/org.freedesktop.Akonadi.AgentManager.xml ) qt4_add_dbus_interfaces( akonadikde_LIB_SRC ${AKONADI_DBUS_INTERFACES_DIR}/org.freedesktop.Akonadi.Tracer.xml ) qt4_add_dbus_adaptor( akonadikde_LIB_SRC ${AKONADI_DBUS_INTERFACES_DIR}/org.freedesktop.Akonadi.Resource.xml resourcebase.h Akonadi::ResourceBase ) qt4_add_dbus_adaptor( akonadikde_LIB_SRC ${AKONADI_DBUS_INTERFACES_DIR}/org.freedesktop.Akonadi.Agent.Status.xml agentbase.h Akonadi::AgentBase ) qt4_add_dbus_adaptor( akonadikde_LIB_SRC ${AKONADI_DBUS_INTERFACES_DIR}/org.freedesktop.Akonadi.Agent.Control.xml agentbase.h Akonadi::AgentBase ) kde4_add_ui_files( akonadikde_LIB_SRC cachepolicypage.ui collectiongeneralpropertiespage.ui subscriptiondialog.ui controlprogressindicator.ui selftestdialog.ui ) kde4_add_library( akonadi-kde SHARED ${akonadikde_LIB_SRC} ) macro_ensure_version( "4.2.0" ${KDE_VERSION} KDE_IS_AT_LEAST_42 ) target_link_libraries( akonadi-kde ${KDE4_SOLID_LIBS} ${QT_QTNETWORK_LIBRARY} ${QT_QTDBUS_LIBRARY} ${QT_QTSQL_LIBRARY} ${KDE4_KDEUI_LIBS} ${KDE4_KIO_LIBS} ${AKONADI_COMMON_LIBRARIES} ) set( AKONADI_KDE_DEPS ${KDE4_KDEUI_LIBS} ${QT_QTDBUS_LIBRARY} ${QT_QTCORE_LIBRARY} ) if(${KDE_IS_AT_LEAST_42}) target_link_libraries( akonadi-kde LINK_INTERFACE_LIBRARIES ${AKONADI_KDE_DEPS}) else(${KDE_IS_AT_LEAST_42}) target_link_libraries( akonadi-kde ${AKONADI_KDE_DEPS}) endif(${KDE_IS_AT_LEAST_42}) set_target_properties( akonadi-kde PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION} ) install( TARGETS akonadi-kde EXPORT kdepimlibsLibraryTargets ${INSTALL_TARGETS_DEFAULT_ARGS} ) ########### install files ############### install( FILES akonadi_export.h agentbase.h agentfilterproxymodel.h agentinstance.h agentinstancecreatejob.h agentinstancemodel.h agentinstancewidget.h agentmanager.h agenttype.h agenttypemodel.h agenttypewidget.h agenttypedialog.h attribute.h attributefactory.h cachepolicy.h changerecorder.h collection.h collectioncopyjob.h collectioncreatejob.h collectiondeletejob.h collectiondialog.h collectionfilterproxymodel.h collectionfetchjob.h collectionmodel.h collectionmodifyjob.h collectionpropertiesdialog.h collectionpropertiespage.h collectionrequester.h collectionstatisticsdelegate.h collectionstatisticsmodel.h collectionstatistics.h collectionstatisticsjob.h collectionview.h control.h entity.h entitydisplayattribute.h # entitysortfilterproxymodel.h exception.h item.h itemcreatejob.h itemcopyjob.h itemdeletejob.h itemfetchjob.h itemfetchscope.h itemmodel.h itemmodifyjob.h itemmonitor.h itemmovejob.h itempayloadinternals_p.h itemserializerplugin.h itemsync.h itemview.h job.h linkjob.h mimetypechecker.h monitor.h qtest_akonadi.h resourcebase.h searchcreatejob.h session.h servermanager.h standardactionmanager.h transactionjobs.h transactionsequence.h unlinkjob.h DESTINATION ${INCLUDE_INSTALL_DIR}/akonadi COMPONENT Devel ) install( FILES collectionpathresolver_p.h DESTINATION ${INCLUDE_INSTALL_DIR}/akonadi/private COMPONENT Devel ) install( FILES kcfg2dbus.xsl DESTINATION ${DATA_INSTALL_DIR}/akonadi-kde ) diff --git a/akonadi/collectionmodifyjob.cpp b/akonadi/collectionmodifyjob.cpp index 639810d96..5d31fe331 100644 --- a/akonadi/collectionmodifyjob.cpp +++ b/akonadi/collectionmodifyjob.cpp @@ -1,84 +1,92 @@ /* Copyright (c) 2006 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 "collectionmodifyjob.h" #include "imapparser_p.h" #include "job_p.h" #include "protocolhelper_p.h" #include "collectionstatistics.h" #include "collection_p.h" using namespace Akonadi; class Akonadi::CollectionModifyJobPrivate : public JobPrivate { public: CollectionModifyJobPrivate( CollectionModifyJob *parent ) : JobPrivate( parent ) { } Collection mCollection; }; CollectionModifyJob::CollectionModifyJob( const Collection &collection, QObject * parent ) : Job( new CollectionModifyJobPrivate( this ), parent ) { Q_D( CollectionModifyJob ); - - Q_ASSERT( collection.isValid() ); d->mCollection = collection; } CollectionModifyJob::~CollectionModifyJob() { } void CollectionModifyJob::doStart() { Q_D( CollectionModifyJob ); + if ( !d->mCollection.isValid() && d->mCollection.remoteId().isEmpty() ) { + setError( Unknown ); + setErrorText( QLatin1String( "Invalid collection" ) ); + emitResult(); + return; + } - QByteArray command = d->newTag() + " MODIFY " + QByteArray::number( d->mCollection.id() ); + QByteArray command = d->newTag(); + if ( d->mCollection.isValid() ) + command += " MODIFY " + QByteArray::number( d->mCollection.id() ); + else + command += " RID MODIFY " + ImapParser::quote( d->mCollection.remoteId().toUtf8() ); QByteArray changes; if ( d->mCollection.d_func()->contentTypesChanged ) { QList bList; foreach( const QString &s, d->mCollection.contentMimeTypes() ) bList << s.toLatin1(); changes += " MIMETYPE (" + ImapParser::join( bList, " " ) + ')'; } if ( d->mCollection.parent() >= 0 ) changes += " PARENT " + QByteArray::number( d->mCollection.parent() ); if ( !d->mCollection.name().isEmpty() ) changes += " NAME " + ImapParser::quote( d->mCollection.name().toUtf8() ); if ( !d->mCollection.remoteId().isNull() ) changes += " REMOTEID " + ImapParser::quote( d->mCollection.remoteId().toUtf8() ); if ( d->mCollection.d_func()->cachePolicyChanged ) changes += ' ' + ProtocolHelper::cachePolicyToByteArray( d->mCollection.cachePolicy() ); if ( d->mCollection.attributes().count() > 0 ) changes += ' ' + ProtocolHelper::attributesToByteArray( d->mCollection ); foreach ( const QByteArray &b, d->mCollection.d_func()->mDeletedAttributes ) changes += " -" + b; if ( changes.isEmpty() ) { emitResult(); return; } command += changes + '\n'; d->writeData( command ); } #include "collectionmodifyjob.moc" diff --git a/akonadi/collectionmodifyjob.h b/akonadi/collectionmodifyjob.h index fa156766e..d7f42823b 100644 --- a/akonadi/collectionmodifyjob.h +++ b/akonadi/collectionmodifyjob.h @@ -1,80 +1,80 @@ /* Copyright (c) 2006 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_COLLECTIONMODIFYJOB_H #define AKONADI_COLLECTIONMODIFYJOB_H #include "akonadi_export.h" #include namespace Akonadi { class CachePolicy; class Collection; class CollectionModifyJobPrivate; /** * @short Job that modifies a collection in the Akonadi storage. * * This job modifies the properties of an existing collection. * * @code * * Akonadi::Collection collection = ... * * Akonadi::CollectionModifyJob *job = new Akonadi::CollectionModifyJob( collection ); - * - * if ( job->exec() ) - * qDebug() << "Modified successfully"; - * else - * qDebug() << "Error occurred"; + * connect( job, SIGNAL(result(KJob*)), this, SLOT(modifyResult(KJob*)) ); * * @endcode * * @author Volker Krause */ class AKONADI_EXPORT CollectionModifyJob : public Job { Q_OBJECT public: /** - * Creates a new collection modify job for the given collection. + * Creates a new collection modify job for the given collection. The collection can be + * identified either by its unique identifier or its remote identifier. Since the remote + * identifier is not necessarily globally unique, identification by remote identifier only + * works inside a resource context (that is from within ResourceBase) and is therefore + * limited to one resource. * * @param collection The collection to modify. * @param parent The parent object. */ explicit CollectionModifyJob( const Collection &collection, QObject *parent = 0 ); /** * Destroys the collection modify job. */ ~CollectionModifyJob(); protected: virtual void doStart(); private: Q_DECLARE_PRIVATE( CollectionModifyJob ) }; } #endif diff --git a/akonadi/collectionmovejob.cpp b/akonadi/collectionmovejob.cpp new file mode 100644 index 000000000..e6427259d --- /dev/null +++ b/akonadi/collectionmovejob.cpp @@ -0,0 +1,63 @@ +/* + Copyright (c) 2009 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 "collectionmovejob.h" +#include "collection.h" +#include "job_p.h" + +using namespace Akonadi; + +class Akonadi::CollectionMoveJobPrivate : public JobPrivate +{ + public: + CollectionMoveJobPrivate( CollectionMoveJob *parent ) + : JobPrivate( parent ) + { + } + + Collection collection; + Collection destination; +}; + +CollectionMoveJob::CollectionMoveJob( const Collection &collection, const Collection &destination, QObject * parent ) + : Job( new CollectionMoveJobPrivate( this ), parent ) +{ + Q_D( CollectionMoveJob ); + + Q_ASSERT( collection.isValid() ); + d->collection = collection; + d->destination = destination; +} + +void CollectionMoveJob::doStart() +{ + Q_D( CollectionMoveJob ); + if ( !d->collection.isValid() || !d->destination.isValid() ) { + setError( Unknown ); + setErrorText( QLatin1String("Invalid collection specified") ); + emitResult(); + return; + } + + const QByteArray command = d->newTag() + " COLMOVE " + QByteArray::number( d->collection.id() ) + + ' ' + QByteArray::number( d->destination.id() ) + '\n'; + d->writeData( command ); +} + +#include "collectionmovejob.moc" diff --git a/akonadi/collectionmodifyjob.h b/akonadi/collectionmovejob.h similarity index 50% copy from akonadi/collectionmodifyjob.h copy to akonadi/collectionmovejob.h index fa156766e..f81bacd10 100644 --- a/akonadi/collectionmodifyjob.h +++ b/akonadi/collectionmovejob.h @@ -1,80 +1,60 @@ /* - Copyright (c) 2006 Volker Krause + Copyright (c) 2009 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_COLLECTIONMODIFYJOB_H -#define AKONADI_COLLECTIONMODIFYJOB_H +#ifndef AKONADI_COLLECTIONMOVEJOB_H +#define AKONADI_COLLECTIONMOVEJOB_H #include "akonadi_export.h" #include namespace Akonadi { -class CachePolicy; class Collection; -class CollectionModifyJobPrivate; +class CollectionMoveJobPrivate; /** - * @short Job that modifies a collection in the Akonadi storage. + * @short Job that moves a collection in the Akonadi storage to a new parent collection. * - * This job modifies the properties of an existing collection. - * - * @code - * - * Akonadi::Collection collection = ... - * - * Akonadi::CollectionModifyJob *job = new Akonadi::CollectionModifyJob( collection ); - * - * if ( job->exec() ) - * qDebug() << "Modified successfully"; - * else - * qDebug() << "Error occurred"; - * - * @endcode - * - * @author Volker Krause + * This job moves an existing collection to a new parent collection. */ -class AKONADI_EXPORT CollectionModifyJob : public Job +class AKONADI_EXPORT CollectionMoveJob : public Job { Q_OBJECT public: /** - * Creates a new collection modify job for the given collection. + * Creates a new collection move job for the given collection and destination * - * @param collection The collection to modify. + * @param collection The collection to move. + * @param destination The destination collection where @p collection should be moved to. * @param parent The parent object. */ - explicit CollectionModifyJob( const Collection &collection, QObject *parent = 0 ); - - /** - * Destroys the collection modify job. - */ - ~CollectionModifyJob(); + CollectionMoveJob( const Collection &collection, const Collection &destination, QObject *parent = 0 ); protected: virtual void doStart(); private: - Q_DECLARE_PRIVATE( CollectionModifyJob ) + Q_DECLARE_PRIVATE( CollectionMoveJob ) }; } #endif diff --git a/akonadi/pastehelper.cpp b/akonadi/pastehelper.cpp index a54bd9ed6..a35930649 100644 --- a/akonadi/pastehelper.cpp +++ b/akonadi/pastehelper.cpp @@ -1,114 +1,113 @@ /* Copyright (c) 2008 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 "pastehelper_p.h" #include "collectioncopyjob.h" -#include "collectionmodifyjob.h" +#include "collectionmovejob.h" #include "item.h" #include "itemcreatejob.h" #include "itemcopyjob.h" #include "itemmodifyjob.h" #include "itemmovejob.h" #include "transactionsequence.h" #include #include #include #include #include using namespace Akonadi; bool PasteHelper::canPaste(const QMimeData * mimeData, const Collection & collection) { if ( !mimeData || !collection.isValid() ) return false; // TODO check acls depending on mimetype if ( (collection.rights() & (Collection::CanCreateItem | Collection::CanCreateCollection) ) == 0 ) return false; if ( KUrl::List::canDecode( mimeData ) ) return true; foreach ( const QString &format, mimeData->formats() ) if ( collection.contentMimeTypes().contains( format ) ) return true; return false; } KJob* PasteHelper::paste(const QMimeData * mimeData, const Collection & collection, bool copy) { if ( !canPaste( mimeData, collection ) ) return 0; // we try to drop data not coming with the akonadi:// url // find a type the target collection supports foreach ( const QString &type, mimeData->formats() ) { if ( !collection.contentMimeTypes().contains( type ) ) continue; QByteArray item = mimeData->data( type ); // HACK for some unknown reason the data is sometimes 0-terminated... if ( !item.isEmpty() && item.at( item.size() - 1 ) == 0 ) item.resize( item.size() - 1 ); Item it; it.setMimeType( type ); it.setPayloadFromData( item ); ItemCreateJob *job = new ItemCreateJob( it, collection ); return job; } if ( !KUrl::List::canDecode( mimeData ) ) return 0; // data contains an url list TransactionSequence *transaction = new TransactionSequence(); KUrl::List urls = KUrl::List::fromMimeData( mimeData ); foreach ( const KUrl &url, urls ) { if ( Collection::fromUrl( url ).isValid() ) { Collection col = Collection::fromUrl( url ); if ( !copy ) { - col.setParent( collection ); - CollectionModifyJob *job = new CollectionModifyJob( col, transaction ); + new CollectionMoveJob( col, collection, transaction ); } else { new CollectionCopyJob( col, collection, transaction ); } } else if ( Item::fromUrl( url ).isValid() ) { // TODO Extract mimetype from url and check if collection accepts it const Item item = Item::fromUrl( url ); if ( !copy ) { new ItemMoveJob( item, collection, transaction ); } else { new ItemCopyJob( item, collection, transaction ); } } else { // non-akonadi URL // TODO kDebug() << "Implement me!"; } } return transaction; } diff --git a/akonadi/session_p.h b/akonadi/session_p.h index 77ad8b2fe..4b667b5f6 100644 --- a/akonadi/session_p.h +++ b/akonadi/session_p.h @@ -1,115 +1,115 @@ /* Copyright (c) 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_SESSION_P_H #define AKONADI_SESSION_P_H #include "session.h" #include "imapparser_p.h" #include #include #include #include class QLocalSocket; namespace Akonadi { /** * @internal */ class SessionPrivate { public: SessionPrivate( Session *parent ) : mParent( parent ), mConnectionSettings( 0 ), protocolVersion( 0 ) { parser = new ImapParser(); } ~SessionPrivate() { delete parser; delete mConnectionSettings; } void startNext(); void reconnect(); void socketDisconnected(); void socketError( QLocalSocket::LocalSocketError error ); void dataReceived(); void doStartNext(); void startJob( Job* job ); void jobDone( KJob* job ); void jobWriteFinished( Akonadi::Job* job ); void jobDestroyed( QObject *job ); bool canPipelineNext(); /** * Creates a new default session for this thread with * the given @p sessionId. The session can be accessed * later by defaultSession(). * * You only need to call this method if you want that the * default session has a special custom id, otherwise a random unique * id is used automatically. */ static void createDefaultSession( const QByteArray &sessionId ); /** Associates the given Job object with this session. */ void addJob( Job* job ); /** Returns the next IMAP tag. */ int nextTag(); /** Sends the given raw data. */ void writeData( const QByteArray &data ); - static int minimumProtocolVersion() { return 13; } + static int minimumProtocolVersion() { return 14; } Session *mParent; QByteArray sessionId; QSettings *mConnectionSettings; QLocalSocket* socket; bool connected; int theNextTag; int protocolVersion; // job management QQueue queue; QQueue pipeline; Job* currentJob; bool jobRunning; // parser stuff ImapParser *parser; }; } #endif diff --git a/akonadi/tests/collectionjobtest.cpp b/akonadi/tests/collectionjobtest.cpp index 2ced54826..744154d7d 100644 --- a/akonadi/tests/collectionjobtest.cpp +++ b/akonadi/tests/collectionjobtest.cpp @@ -1,553 +1,578 @@ /* Copyright (c) 2006 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 #include "collectionjobtest.h" #include +#include "test_utils.h" #include "agentmanager.h" #include "agentinstance.h" #include "cachepolicy.h" #include "collection.h" #include "collectioncreatejob.h" #include "collectiondeletejob.h" #include "collectionfetchjob.h" #include "collectionmodifyjob.h" #include "collectionselectjob_p.h" #include "collectionstatistics.h" #include "collectionstatisticsjob.h" #include "collectionpathresolver_p.h" #include "collectionutils_p.h" #include "control.h" #include "item.h" #include "kmime/messageparts.h" #include "resourceselectjob_p.h" #include #include using namespace Akonadi; QTEST_AKONADIMAIN( CollectionJobTest, NoGUI ) void CollectionJobTest::initTestCase() { qRegisterMetaType(); Control::start(); // switch all resources offline to reduce interference from them foreach ( Akonadi::AgentInstance agent, Akonadi::AgentManager::self()->instances() ) agent.setIsOnline( false ); } static Collection findCol( const Collection::List &list, const QString &name ) { foreach ( const Collection &col, list ) if ( col.name() == name ) return col; return Collection(); } // list compare which ignores the order template static void compareLists( const QList &l1, const QList &l2 ) { QCOMPARE( l1.count(), l2.count() ); foreach ( const T entry, l1 ) { QVERIFY( l2.contains( entry ) ); } } template static T* extractAttribute( QList attrs ) { T dummy; foreach ( Attribute* attr, attrs ) { if ( attr->type() == dummy.type() ) return dynamic_cast( attr ); } return 0; } static Collection::Id res1ColId = 6; // -1; static Collection::Id res2ColId = 7; //-1; static Collection::Id res3ColId = -1; static Collection::Id searchColId = -1; void CollectionJobTest::testTopLevelList( ) { // non-recursive top-level list CollectionFetchJob *job = new CollectionFetchJob( Collection::root(), CollectionFetchJob::FirstLevel ); QVERIFY( job->exec() ); Collection::List list = job->collections(); // check if everything is there and has the correct types and attributes QCOMPARE( list.count(), 4 ); Collection col; col = findCol( list, "res1" ); QVERIFY( col.isValid() ); res1ColId = col.id(); // for the next test QVERIFY( res1ColId > 0 ); QVERIFY( CollectionUtils::isResource( col ) ); QCOMPARE( col.parent(), Collection::root().id() ); QCOMPARE( col.resource(), QLatin1String("akonadi_knut_resource_0") ); QVERIFY( findCol( list, "res2" ).isValid() ); res2ColId = findCol( list, "res2" ).id(); QVERIFY( res2ColId > 0 ); QVERIFY( findCol( list, "res3" ).isValid() ); res3ColId = findCol( list, "res3" ).id(); QVERIFY( res3ColId > 0 ); col = findCol( list, "Search" ); searchColId = col.id(); QVERIFY( col.isValid() ); QVERIFY( CollectionUtils::isVirtualParent( col ) ); QCOMPARE( col.resource(), QLatin1String("akonadi_search_resource") ); } void CollectionJobTest::testFolderList( ) { // recursive list of physical folders CollectionFetchJob *job = new CollectionFetchJob( Collection( res1ColId ), CollectionFetchJob::Recursive ); QSignalSpy spy( job, SIGNAL(collectionsReceived(Akonadi::Collection::List)) ); QVERIFY( spy.isValid() ); QVERIFY( job->exec() ); Collection::List list = job->collections(); int count = 0; for ( int i = 0; i < spy.count(); ++i ) { Collection::List l = spy[i][0].value(); for ( int j = 0; j < l.count(); ++j ) { QVERIFY( list.count() > count + j ); QCOMPARE( list[count + j].id(), l[j].id() ); } count += l.count(); } QCOMPARE( count, list.count() ); // check if everything is there QCOMPARE( list.count(), 4 ); Collection col; QStringList contentTypes; col = findCol( list, "foo" ); QVERIFY( col.isValid() ); QCOMPARE( col.parent(), res1ColId ); QVERIFY( CollectionUtils::isFolder( col ) ); contentTypes << "message/rfc822" << "text/calendar" << "text/directory" << "application/octet-stream" << "inode/directory"; compareLists( col.contentMimeTypes(), contentTypes ); QVERIFY( findCol( list, "bar" ).isValid() ); QCOMPARE( findCol( list, "bar" ).parent(), col.id() ); QVERIFY( findCol( list, "bla" ).isValid() ); } void CollectionJobTest::testNonRecursiveFolderList( ) { CollectionFetchJob *job = new CollectionFetchJob( Collection( res1ColId ), CollectionFetchJob::Base ); QVERIFY( job->exec() ); Collection::List list = job->collections(); QCOMPARE( list.count(), 1 ); QVERIFY( findCol( list, "res1" ).isValid() ); } void CollectionJobTest::testEmptyFolderList( ) { CollectionFetchJob *job = new CollectionFetchJob( Collection( res3ColId ), CollectionFetchJob::FirstLevel ); QVERIFY( job->exec() ); Collection::List list = job->collections(); QCOMPARE( list.count(), 0 ); } void CollectionJobTest::testSearchFolderList( ) { CollectionFetchJob *job = new CollectionFetchJob( Collection( searchColId ), CollectionFetchJob::FirstLevel ); QVERIFY( job->exec() ); Collection::List list = job->collections(); QCOMPARE( list.count(), 0 ); } void CollectionJobTest::testResourceFolderList() { // non-existing resource CollectionFetchJob *job = new CollectionFetchJob( Collection::root(), CollectionFetchJob::FirstLevel ); job->setResource( "i_dont_exist" ); QVERIFY( !job->exec() ); // recursive listing of all collections of an existing resource job = new CollectionFetchJob( Collection::root(), CollectionFetchJob::Recursive ); job->setResource( "akonadi_knut_resource_0" ); QVERIFY( job->exec() ); Collection::List list = job->collections(); QCOMPARE( list.count(), 5 ); QVERIFY( findCol( list, "res1" ).isValid() ); QVERIFY( findCol( list, "foo" ).isValid() ); QVERIFY( findCol( list, "bar" ).isValid() ); QVERIFY( findCol( list, "bla" ).isValid() ); int fooId = findCol( list, "foo" ).id(); // limited listing of a resource job = new CollectionFetchJob( Collection( fooId ), CollectionFetchJob::Recursive ); job->setResource( "akonadi_knut_resource_0" ); QVERIFY( job->exec() ); list = job->collections(); QCOMPARE( list.count(), 3 ); QVERIFY( findCol( list, "bar" ).isValid() ); QVERIFY( findCol( list, "bla" ).isValid() ); } void CollectionJobTest::testCreateDeleteFolder_data() { QTest::addColumn("collection"); QTest::addColumn("creatable"); Collection col; QTest::newRow("empty") << col << false; col.setName( "new folder" ); col.setParent( res3ColId ); QTest::newRow("simple") << col << true; col.setParent( res3ColId ); col.setName( "foo" ); QTest::newRow( "existing in different resource" ) << col << true; col.setName( "mail folder" ); QStringList mimeTypes; mimeTypes << "inode/directory" << "message/rfc822"; col.setContentMimeTypes( mimeTypes ); col.setRemoteId( "remote id" ); CachePolicy policy; policy.setInheritFromParent( false ); policy.setIntervalCheckTime( 60 ); policy.setLocalParts( QStringList( MessagePart::Envelope ) ); policy.setSyncOnDemand( true ); policy.setCacheTimeout( 120 ); col.setCachePolicy( policy ); QTest::newRow( "complex" ) << col << true; col = Collection(); col.setName( "New Folder" ); col.setParent( searchColId ); QTest::newRow( "search folder" ) << col << false; col.setParent( res2ColId ); col.setName( "foo2" ); QTest::newRow( "already existing" ) << col << false; col.setName( "Bla" ); col.setParent( 2 ); QTest::newRow( "already existing with different case" ) << col << true; CollectionPathResolver *resolver = new CollectionPathResolver( "res2/foo2", this ); QVERIFY( resolver->exec() ); col.setParent( resolver->collection() ); col.setName( "new folder" ); QTest::newRow( "parent noinferior" ) << col << false; col.setParent( INT_MAX ); QTest::newRow( "missing parent" ) << col << false; col = Collection(); col.setName( "rid parent" ); col.setParentRemoteId( "8" ); QTest::newRow( "rid parent" ) << col << false; // missing resource context } void CollectionJobTest::testCreateDeleteFolder() { QFETCH( Collection, collection ); QFETCH( bool, creatable ); CollectionCreateJob *createJob = new CollectionCreateJob( collection, this ); QCOMPARE( createJob->exec(), creatable ); if ( !creatable ) return; Collection createdCol = createJob->collection(); QVERIFY( createdCol.isValid() ); QCOMPARE( createdCol.name(), collection.name() ); QCOMPARE( createdCol.parent(), collection.parent() ); QCOMPARE( createdCol.remoteId(), collection.remoteId() ); QCOMPARE( createdCol.cachePolicy(), collection.cachePolicy() ); CollectionFetchJob *listJob = new CollectionFetchJob( Collection( collection.parent() ), CollectionFetchJob::FirstLevel, this ); AKVERIFYEXEC( listJob ); Collection listedCol = findCol( listJob->collections(), collection.name() ); QCOMPARE( listedCol, createdCol ); QCOMPARE( listedCol.remoteId(), collection.remoteId() ); QCOMPARE( listedCol.cachePolicy(), collection.cachePolicy() ); // fetch parent to compare inherited collection properties Collection parentCol = Collection::root(); if ( collection.parent() > 0 ) { CollectionFetchJob *listJob = new CollectionFetchJob( Collection( collection.parent() ), CollectionFetchJob::Base, this ); AKVERIFYEXEC( listJob ); QCOMPARE( listJob->collections().count(), 1 ); parentCol = listJob->collections().first(); } if ( collection.contentMimeTypes().isEmpty() ) compareLists( listedCol.contentMimeTypes(), parentCol.contentMimeTypes() ); else compareLists( listedCol.contentMimeTypes(), collection.contentMimeTypes() ); if ( collection.resource().isEmpty() ) QCOMPARE( listedCol.resource(), parentCol.resource() ); else QCOMPARE( listedCol.resource(), collection.resource() ); CollectionDeleteJob *delJob = new CollectionDeleteJob( createdCol, this ); AKVERIFYEXEC( delJob ); listJob = new CollectionFetchJob( Collection( collection.parent() ), CollectionFetchJob::FirstLevel, this ); AKVERIFYEXEC( listJob ); QVERIFY( !findCol( listJob->collections(), collection.name() ).isValid() ); } void CollectionJobTest::testIllegalDeleteFolder() { // non-existing folder CollectionDeleteJob *del = new CollectionDeleteJob( Collection( INT_MAX ), this ); QVERIFY( !del->exec() ); // root del = new CollectionDeleteJob( Collection::root(), this ); QVERIFY( !del->exec() ); } void CollectionJobTest::testStatistics() { // empty folder CollectionStatisticsJob *statistics = new CollectionStatisticsJob( Collection( res1ColId ), this ); QVERIFY( statistics->exec() ); CollectionStatistics s = statistics->statistics(); QCOMPARE( s.count(), 0ll ); QCOMPARE( s.unreadCount(), 0ll ); // folder with attributes and content CollectionPathResolver *resolver = new CollectionPathResolver( "res1/foo", this );; QVERIFY( resolver->exec() ); statistics = new CollectionStatisticsJob( Collection( resolver->collection() ), this ); QVERIFY( statistics->exec() ); s = statistics->statistics(); QCOMPARE( s.count(), 15ll ); QCOMPARE( s.unreadCount(), 14ll ); } +void CollectionJobTest::testModify_data() +{ + QTest::addColumn( "uid" ); + QTest::addColumn( "rid" ); + + QTest::newRow( "uid" ) << collectionIdFromPath( "res1/foo" ) << QString(); + QTest::newRow( "rid" ) << -1ll << QString( "10" ); +} + +#define RESET_COLLECTION_ID \ + col.setId( uid ); \ + if ( !rid.isEmpty() ) col.setRemoteId( rid ) + void CollectionJobTest::testModify() { + QFETCH( qint64, uid ); + QFETCH( QString, rid ); + + if ( !rid.isEmpty() ) { + ResourceSelectJob *rjob = new ResourceSelectJob( "akonadi_knut_resource_0" ); + AKVERIFYEXEC( rjob ); + } + QStringList reference; reference << "text/calendar" << "text/directory" << "message/rfc822" << "application/octet-stream" << "inode/directory"; Collection col; - CollectionFetchJob *ljob = new CollectionFetchJob( Collection( res1ColId ), CollectionFetchJob::FirstLevel ); - QVERIFY( ljob->exec() ); - col = findCol( ljob->collections(), "foo" ); - QVERIFY( col.isValid() ); + RESET_COLLECTION_ID; // test noop modify CollectionModifyJob *mod = new CollectionModifyJob( col, this ); - QVERIFY( mod->exec() ); + AKVERIFYEXEC( mod ); - ljob = new CollectionFetchJob( col, CollectionFetchJob::Base, this ); - QVERIFY( ljob->exec() ); + CollectionFetchJob* ljob = new CollectionFetchJob( col, CollectionFetchJob::Base, this ); + AKVERIFYEXEC( ljob ); QCOMPARE( ljob->collections().count(), 1 ); col = ljob->collections().first(); compareLists( col.contentMimeTypes(), reference ); // test clearing content types + RESET_COLLECTION_ID; col.setContentMimeTypes( QStringList() ); mod = new CollectionModifyJob( col, this ); - QVERIFY( mod->exec() ); + AKVERIFYEXEC( mod ); ljob = new CollectionFetchJob( col, CollectionFetchJob::Base, this ); - QVERIFY( ljob->exec() ); + AKVERIFYEXEC( ljob ); QCOMPARE( ljob->collections().count(), 1 ); col = ljob->collections().first(); QVERIFY( col.contentMimeTypes().isEmpty() ); // test setting contnet types + RESET_COLLECTION_ID; col.setContentMimeTypes( reference ); mod = new CollectionModifyJob( col, this ); - QVERIFY( mod->exec() ); + AKVERIFYEXEC( mod ); ljob = new CollectionFetchJob( col, CollectionFetchJob::Base, this ); - QVERIFY( ljob->exec() ); + AKVERIFYEXEC( ljob ); QCOMPARE( ljob->collections().count(), 1 ); col = ljob->collections().first(); compareLists( col.contentMimeTypes(), reference ); // renaming + RESET_COLLECTION_ID; col.setName( "foo (renamed)" ); mod = new CollectionModifyJob( col, this ); - QVERIFY( mod->exec() ); + AKVERIFYEXEC( mod ); ljob = new CollectionFetchJob( col, CollectionFetchJob::Base, this ); - QVERIFY( ljob->exec() ); + AKVERIFYEXEC( ljob ); QCOMPARE( ljob->collections().count(), 1 ); col = ljob->collections().first(); QCOMPARE( col.name(), QString( "foo (renamed)" ) ); + RESET_COLLECTION_ID; col.setName( "foo" ); mod = new CollectionModifyJob( col, this ); - QVERIFY( mod->exec() ); + AKVERIFYEXEC( mod ); } +#undef RESET_COLLECTION_ID + void CollectionJobTest::testIllegalModify() { // non-existing collection Collection col( INT_MAX ); col.setParent( res1ColId ); CollectionModifyJob *mod = new CollectionModifyJob( col, this ); QVERIFY( !mod->exec() ); // rename to already existing name col = Collection( res1ColId ); col.setName( "res2" ); mod = new CollectionModifyJob( col, this ); QVERIFY( !mod->exec() ); } void CollectionJobTest::testUtf8CollectionName() { QString folderName = QString::fromUtf8( "รค" ); // create collection Collection col; col.setParent( res3ColId ); col.setName( folderName ); CollectionCreateJob *create = new CollectionCreateJob( col, this ); QVERIFY( create->exec() ); col = create->collection(); QVERIFY( col.isValid() ); // list parent CollectionFetchJob *list = new CollectionFetchJob( Collection( res3ColId ), CollectionFetchJob::Recursive, this ); QVERIFY( list->exec() ); QCOMPARE( list->collections().count(), 1 ); QCOMPARE( col, list->collections().first() ); QCOMPARE( col.name(), folderName ); // modify collection col.setContentMimeTypes( QStringList( "message/rfc822'" ) ); CollectionModifyJob *modify = new CollectionModifyJob( col, this ); QVERIFY( modify->exec() ); // collection statistics CollectionStatisticsJob *statistics = new CollectionStatisticsJob( col, this ); QVERIFY( statistics->exec() ); CollectionStatistics s = statistics->statistics(); QCOMPARE( s.count(), 0ll ); QCOMPARE( s.unreadCount(), 0ll ); // delete collection CollectionDeleteJob *del = new CollectionDeleteJob( col, this ); QVERIFY( del->exec() ); } void CollectionJobTest::testMultiList() { Collection::List req; req << Collection( res1ColId ) << Collection( res2ColId ); CollectionFetchJob* job = new CollectionFetchJob( req, this ); QVERIFY( job->exec() ); Collection::List res; res = job->collections(); compareLists( res, req ); } void CollectionJobTest::testSelect() { CollectionPathResolver *resolver = new CollectionPathResolver( "res1/foo", this );; QVERIFY( resolver->exec() ); Collection col( resolver->collection() ); CollectionSelectJob *job = new CollectionSelectJob( col, this ); QVERIFY( job->exec() ); QCOMPARE( job->unseen(), -1 ); job = new CollectionSelectJob( col, this ); job->setRetrieveStatus( true ); QVERIFY( job->exec() ); QVERIFY( job->unseen() > -1 ); job = new CollectionSelectJob( Collection::root(), this ); QVERIFY( job->exec() ); job = new CollectionSelectJob( Collection( INT_MAX ), this ); QVERIFY( !job->exec() ); } void CollectionJobTest::testRidFetch() { Collection col; col.setRemoteId( "10" ); CollectionFetchJob *job = new CollectionFetchJob( col, CollectionFetchJob::Base, this ); job->setResource( "akonadi_knut_resource_0" ); QVERIFY( job->exec() ); QCOMPARE( job->collections().count(), 1 ); col = job->collections().first(); QVERIFY( col.isValid() ); QCOMPARE( col.remoteId(), QString::fromLatin1( "10" ) ); } void CollectionJobTest::testRidCreate() { Collection collection; collection.setName( "rid create" ); collection.setParentRemoteId( "8" ); ResourceSelectJob *resSel = new ResourceSelectJob( "akonadi_knut_resource_2" ); AKVERIFYEXEC( resSel ); CollectionCreateJob *createJob = new CollectionCreateJob( collection, this ); AKVERIFYEXEC( createJob ); Collection createdCol = createJob->collection(); QVERIFY( createdCol.isValid() ); QCOMPARE( createdCol.name(), collection.name() ); CollectionFetchJob *listJob = new CollectionFetchJob( Collection( res3ColId ), CollectionFetchJob::FirstLevel, this ); AKVERIFYEXEC( listJob ); Collection listedCol = findCol( listJob->collections(), collection.name() ); QCOMPARE( listedCol, createdCol ); QCOMPARE( listedCol.name(), collection.name() ); CollectionDeleteJob *delJob = new CollectionDeleteJob( createdCol, this ); AKVERIFYEXEC( delJob ); listJob = new CollectionFetchJob( Collection( res3ColId ), CollectionFetchJob::FirstLevel, this ); AKVERIFYEXEC( listJob ); QVERIFY( !findCol( listJob->collections(), collection.name() ).isValid() ); } #include "collectionjobtest.moc" diff --git a/akonadi/tests/collectionjobtest.h b/akonadi/tests/collectionjobtest.h index bd6989ce9..8def92d2d 100644 --- a/akonadi/tests/collectionjobtest.h +++ b/akonadi/tests/collectionjobtest.h @@ -1,50 +1,51 @@ /* Copyright (c) 2006 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 COLLECTIONJOBTEST_H #define COLLECTIONJOBTEST_H #include class CollectionJobTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void testTopLevelList(); void testFolderList(); void testNonRecursiveFolderList(); void testEmptyFolderList(); void testSearchFolderList(); void testResourceFolderList(); void testCreateDeleteFolder_data(); void testCreateDeleteFolder(); void testIllegalDeleteFolder(); void testStatistics(); + void testModify_data(); void testModify(); void testIllegalModify(); void testUtf8CollectionName(); void testMultiList(); void testSelect(); void testRidFetch(); void testRidCreate(); }; #endif diff --git a/akonadi/tests/collectionmovetest.cpp b/akonadi/tests/collectionmovetest.cpp index f990d515b..be21e5f46 100644 --- a/akonadi/tests/collectionmovetest.cpp +++ b/akonadi/tests/collectionmovetest.cpp @@ -1,151 +1,147 @@ /* Copyright (c) 2006, 2009 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 "test_utils.h" #include "collection.h" #include "collectionfetchjob.h" -#include "collectionmodifyjob.h" +#include "collectionmovejob.h" #include "item.h" #include "itemfetchjob.h" #include "itemfetchscope.h" #include using namespace Akonadi; class CollectionMoveTest : public QObject { Q_OBJECT private slots: void testIllegalMove_data() { QTest::addColumn( "source" ); QTest::addColumn( "destination" ); const Collection res1( collectionIdFromPath( "res1" ) ); const Collection res1foo( collectionIdFromPath( "res1/foo" ) ); const Collection res1bla( collectionIdFromPath( "res1/foo/bar/bla" ) ); QTest::newRow( "non-existing-target" ) << res1 << Collection( INT_MAX ); QTest::newRow( "root" ) << Collection::root() << res1; QTest::newRow( "move-into-child" ) << res1 << res1foo; QTest::newRow( "same-name-in-target" ) << res1bla << res1foo; QTest::newRow( "non-existing-source" ) << Collection( INT_MAX ) << res1; } void testIllegalMove() { QFETCH( Collection, source ); QFETCH( Collection, destination ); QVERIFY( source.isValid() ); QVERIFY( destination.isValid() ); - source.setParent( destination ); - CollectionModifyJob* mod = new CollectionModifyJob( source, this ); + CollectionMoveJob* mod = new CollectionMoveJob( source, destination, this ); QVERIFY( !mod->exec() ); } void testMove_data() { QTest::addColumn( "source" ); QTest::addColumn( "destination" ); QTest::addColumn( "crossResource" ); QTest::newRow( "inter-resource" ) << Collection( collectionIdFromPath( "res1" ) ) << Collection( collectionIdFromPath( "res2" ) ) << true; QTest::newRow( "intra-resource" ) << Collection( collectionIdFromPath( "res1/foo/bla" ) ) << Collection( collectionIdFromPath( "res1/foo/bar/bla" ) ) << false; } // TODO: test signals void testMove() { QFETCH( Collection, source ); QFETCH( Collection, destination ); QFETCH( bool, crossResource ); QVERIFY( source.isValid() ); QVERIFY( destination.isValid() ); CollectionFetchJob *fetch = new CollectionFetchJob( source, CollectionFetchJob::Base, this ); - QVERIFY( fetch->exec() ); + AKVERIFYEXEC( fetch ); QCOMPARE( fetch->collections().count(), 1 ); source = fetch->collections().first(); // obtain reference listing fetch = new CollectionFetchJob( source, CollectionFetchJob::Recursive ); - QVERIFY( fetch->exec() ); + AKVERIFYEXEC( fetch ); QHash referenceData; foreach ( const Collection c, fetch->collections() ) { ItemFetchJob *job = new ItemFetchJob( c, this ); - QVERIFY( job->exec() ); + AKVERIFYEXEC( job ); referenceData.insert( c, job->items() ); } // move collection - Collection col( source ); - col.setParent( destination ); - CollectionModifyJob *mod = new CollectionModifyJob( col, this ); - QVERIFY( mod->exec() ); + CollectionMoveJob *mod = new CollectionMoveJob( source, destination, this ); + AKVERIFYEXEC( mod ); // check if source was modified correctly CollectionFetchJob *ljob = new CollectionFetchJob( source, CollectionFetchJob::Base ); - QVERIFY( ljob->exec() ); + AKVERIFYEXEC( ljob ); Collection::List list = ljob->collections(); QCOMPARE( list.count(), 1 ); - col = list.first(); + Collection col = list.first(); QCOMPARE( col.name(), source.name() ); QCOMPARE( col.parent(), destination.id() ); // list destination and check if everything is still there ljob = new CollectionFetchJob( destination, CollectionFetchJob::Recursive ); - QVERIFY( ljob->exec() ); + AKVERIFYEXEC( ljob ); list = ljob->collections(); QVERIFY( list.count() >= referenceData.count() ); for ( QHash::ConstIterator it = referenceData.constBegin(); it != referenceData.constEnd(); ++it ) { QVERIFY( list.contains( it.key() ) ); if ( crossResource ) { QVERIFY( list[ list.indexOf( it.key() ) ].resource() != it.key().resource() ); } else { QCOMPARE( list[ list.indexOf( it.key() ) ].resource(), it.key().resource() ); } ItemFetchJob *job = new ItemFetchJob( it.key(), this ); job->fetchScope().fetchFullPayload(); - QVERIFY( job->exec() ); + AKVERIFYEXEC( job ); QCOMPARE( job->items().count(), it.value().count() ); foreach ( const Item item, job->items() ) { QVERIFY( it.value().contains( item ) ); QVERIFY( item.hasPayload() ); } } // cleanup - col.setParent( source.parent() ); - mod = new CollectionModifyJob( col, this ); - QVERIFY( mod->exec() ); + mod = new CollectionMoveJob( col, Collection( source.parent() ), this ); + AKVERIFYEXEC( mod ); } }; QTEST_AKONADIMAIN( CollectionMoveTest, NoGUI ) #include "collectionmovetest.moc" diff --git a/kpimidentities/signatureconfigurator.cpp b/kpimidentities/signatureconfigurator.cpp index c2562530b..10a082445 100644 --- a/kpimidentities/signatureconfigurator.cpp +++ b/kpimidentities/signatureconfigurator.cpp @@ -1,411 +1,421 @@ /* -*- c++ -*- Copyright 2008 Thomas McGuire Copyright 2008 Edwin Schepers Copyright 2004 Marc Mutz This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "signatureconfigurator.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KPIMIdentities; namespace KPIMIdentities { /** Private class that helps to provide binary compatibility between releases. @internal */ //@cond PRIVATE class KPIMIdentities::SignatureConfigurator::Private { public: bool inlinedHtml; }; //@endcond SignatureConfigurator::SignatureConfigurator( QWidget * parent ) : QWidget( parent ), d( new Private ) { // tmp. vars: QLabel * label; QWidget * page; QHBoxLayout * hlay; QVBoxLayout * vlay; QVBoxLayout * page_vlay; vlay = new QVBoxLayout( this ); vlay->setObjectName( "main layout" ); vlay->setSpacing( KDialog::spacingHint() ); vlay->setMargin( 0 ); // "enable signatue" checkbox: mEnableCheck = new QCheckBox( i18n("&Enable signature"), this ); mEnableCheck->setWhatsThis( i18n("Check this box if you want KMail to append a signature to mails " "written with this identity.")); vlay->addWidget( mEnableCheck ); // "obtain signature text from" combo and label: hlay = new QHBoxLayout(); // inherits spacing vlay->addLayout( hlay ); mSourceCombo = new KComboBox( this ); mSourceCombo->setEditable( false ); mSourceCombo->setWhatsThis( i18n("Click on the widgets below to obtain help on the input methods.")); mSourceCombo->setEnabled( false ); // since !mEnableCheck->isChecked() mSourceCombo->addItems( QStringList() << i18nc("continuation of \"obtain signature text from\"", "Input Field Below") << i18nc("continuation of \"obtain signature text from\"", "File") << i18nc("continuation of \"obtain signature text from\"", "Output of Command") ); label = new QLabel( i18n("Obtain signature &text from:"), this ); label->setBuddy( mSourceCombo ); label->setEnabled( false ); // since !mEnableCheck->isChecked() hlay->addWidget( label ); hlay->addWidget( mSourceCombo, 1 ); // widget stack that is controlled by the source combo: QStackedWidget * widgetStack = new QStackedWidget( this ); widgetStack->setEnabled( false ); // since !mEnableCheck->isChecked() vlay->addWidget( widgetStack, 1 ); connect( mSourceCombo, SIGNAL(currentIndexChanged(int)), widgetStack, SLOT(setCurrentIndex (int)) ); connect( mSourceCombo, SIGNAL(highlighted(int)), widgetStack, SLOT(setCurrentIndex (int)) ); // connects for the enabling of the widgets depending on // signatureEnabled: connect( mEnableCheck, SIGNAL(toggled(bool)), mSourceCombo, SLOT(setEnabled(bool)) ); connect( mEnableCheck, SIGNAL(toggled(bool)), widgetStack, SLOT(setEnabled(bool)) ); connect( mEnableCheck, SIGNAL(toggled(bool)), label, SLOT(setEnabled(bool)) ); // The focus might be still in the widget that is disabled connect( mEnableCheck, SIGNAL(clicked()), mEnableCheck, SLOT(setFocus()) ); int pageno = 0; // page 0: input field for direct entering: page = new QWidget( widgetStack ); widgetStack->insertWidget( pageno, page ); page_vlay = new QVBoxLayout( page ); mEditToolBar = new KToolBar( this ); mEditToolBar->setToolButtonStyle( Qt::ToolButtonIconOnly ); page_vlay->addWidget( mEditToolBar, 0 ); mFormatToolBar = new KToolBar( this ); mFormatToolBar->setToolButtonStyle( Qt::ToolButtonIconOnly ); page_vlay->addWidget( mFormatToolBar, 1 ); mTextEdit = new KPIMTextEdit::TextEdit( this ); page_vlay->addWidget( mTextEdit, 2 ); mTextEdit->setWhatsThis( i18n("Use this field to enter an arbitrary static signature.")); // exclude SupportToPlainText. mTextEdit->setRichTextSupport( KRichTextWidget::FullTextFormattingSupport | KRichTextWidget::FullListSupport | KRichTextWidget::SupportAlignment | KRichTextWidget::SupportRuleLine | KRichTextWidget::SupportHyperlinks | KRichTextWidget::SupportFormatPainting ); // Fill the toolbars. KActionCollection *actionCollection = new KActionCollection(this); mTextEdit->createActions( actionCollection ); mEditToolBar->addAction( actionCollection->action( "format_text_bold" ) ); mEditToolBar->addAction( actionCollection->action( "format_text_italic" ) ); mEditToolBar->addAction( actionCollection->action( "format_text_underline" ) ); mEditToolBar->addAction( actionCollection->action( "format_text_strikeout" ) ); mEditToolBar->addAction( actionCollection->action( "format_text_foreground_color" ) ); mEditToolBar->addAction( actionCollection->action( "format_text_background_color" ) ); mEditToolBar->addAction( actionCollection->action( "format_font_family" ) ); mEditToolBar->addAction( actionCollection->action( "format_font_size" ) ); mFormatToolBar->addAction( actionCollection->action( "format_list_style" ) ); mFormatToolBar->addAction( actionCollection->action( "format_list_indent_more" ) ); mFormatToolBar->addAction( actionCollection->action( "format_list_indent_less" ) ); mFormatToolBar->addAction( actionCollection->action( "format_list_indent_less" ) ); mFormatToolBar->addSeparator(); mFormatToolBar->addAction( actionCollection->action( "format_align_left" ) ); mFormatToolBar->addAction( actionCollection->action( "format_align_center" ) ); mFormatToolBar->addAction( actionCollection->action( "format_align_right" ) ); mFormatToolBar->addAction( actionCollection->action( "format_align_justify" ) ); mFormatToolBar->addSeparator(); mFormatToolBar->addAction( actionCollection->action( "insert_horizontal_rule" ) ); mFormatToolBar->addAction( actionCollection->action( "manage_link" ) ); mFormatToolBar->addAction( actionCollection->action( "format_painter" ) ); hlay = new QHBoxLayout(); // inherits spacing page_vlay->addLayout( hlay ); mHtmlCheck = new QCheckBox( i18n("&Use HTML"), page ); connect( mHtmlCheck, SIGNAL(clicked()), this, SLOT(slotSetHtml()) ); hlay->addWidget( mHtmlCheck ); d->inlinedHtml = true; widgetStack->setCurrentIndex( 0 ); // since mSourceCombo->currentItem() == 0 // page 1: "signature file" requester, label, "edit file" button: ++pageno; page = new QWidget( widgetStack ); widgetStack->insertWidget( pageno, page ); // force sequential numbers (play safe) page_vlay = new QVBoxLayout( page ); page_vlay->setMargin( 0 ); page_vlay->setSpacing( KDialog::spacingHint() ); hlay = new QHBoxLayout(); // inherits spacing page_vlay->addLayout( hlay ); mFileRequester = new KUrlRequester( page ); mFileRequester->setWhatsThis( i18n("Use this requester to specify a text file that contains your " "signature. It will be read every time you create a new mail or " "append a new signature.")); label = new QLabel( i18n("S&pecify file:"), page ); label->setBuddy( mFileRequester ); hlay->addWidget( label ); hlay->addWidget( mFileRequester, 1 ); mFileRequester->button()->setAutoDefault( false ); connect( mFileRequester, SIGNAL(textChanged(const QString &)), this, SLOT(slotEnableEditButton(const QString &)) ); mEditButton = new QPushButton( i18n("Edit &File"), page ); mEditButton->setWhatsThis( i18n("Opens the specified file in a text editor.")); connect( mEditButton, SIGNAL(clicked()), SLOT(slotEdit()) ); mEditButton->setAutoDefault( false ); mEditButton->setEnabled( false ); // initially nothing to edit hlay->addWidget( mEditButton ); page_vlay->addStretch( 1 ); // spacer // page 2: "signature command" requester and label: ++pageno; page = new QWidget( widgetStack ); widgetStack->insertWidget( pageno,page ); page_vlay = new QVBoxLayout( page ); page_vlay->setMargin( 0 ); page_vlay->setSpacing( KDialog::spacingHint() ); hlay = new QHBoxLayout(); // inherits spacing page_vlay->addLayout( hlay ); mCommandEdit = new KLineEdit( page ); mCommandEdit->setCompletionObject( new KShellCompletion() ); mCommandEdit->setAutoDeleteCompletionObject( true ); mCommandEdit->setWhatsThis( i18n("You can add an arbitrary command here, either with or without path " "depending on whether or not the command is in your Path. For every " "new mail, KMail will execute the command and use what it outputs (to " "standard output) as a signature. Usual commands for use with this " "mechanism are \"fortune\" or \"ksig -random\".")); label = new QLabel( i18n("S&pecify command:"), page ); label->setBuddy( mCommandEdit ); hlay->addWidget( label ); hlay->addWidget( mCommandEdit, 1 ); page_vlay->addStretch( 1 ); // spacer } SignatureConfigurator::~SignatureConfigurator() { delete d; } bool SignatureConfigurator::isSignatureEnabled() const { return mEnableCheck->isChecked(); } void SignatureConfigurator::setSignatureEnabled( bool enable ) { mEnableCheck->setChecked( enable ); } Signature::Type SignatureConfigurator::signatureType() const { if ( !isSignatureEnabled() ) return Signature::Disabled; switch ( mSourceCombo->currentIndex() ) { case 0: return Signature::Inlined; case 1: return Signature::FromFile; case 2: return Signature::FromCommand; default: return Signature::Disabled; } } void SignatureConfigurator::setSignatureType( Signature::Type type ) { setSignatureEnabled( type != Signature::Disabled ); int idx = 0; switch( type ) { case Signature::Inlined: idx = 0; break; case Signature::FromFile: idx = 1; break; case Signature::FromCommand: idx = 2; break; default: idx = 0; break; }; mSourceCombo->setCurrentIndex( idx ); } void SignatureConfigurator::setInlineText( const QString & text ) { mTextEdit->setTextOrHtml( text ); } QString SignatureConfigurator::fileURL() const { QString file = mFileRequester->url().path(); // Force the filename to be relative to ~ instead of $PWD depending // on the rest of the code (KRun::run in Edit and KFileItem on save) if ( !file.isEmpty() && QFileInfo( file ).isRelative() ) file = QDir::home().absolutePath() + QDir::separator() + file; return file; } void SignatureConfigurator::setFileURL( const QString & url ) { mFileRequester->setUrl( url ); } QString SignatureConfigurator::commandURL() const { return mCommandEdit->text(); } void SignatureConfigurator::setCommandURL( const QString & url ) { mCommandEdit->setText( url ); } Signature SignatureConfigurator::signature() const { Signature sig; - sig.setType( signatureType() ); - sig.setInlinedHtml( d->inlinedHtml ); - sig.setText( d->inlinedHtml ? asCleanedHTML() : mTextEdit->textOrHtml() ); - if ( signatureType() == Signature::FromCommand ) + const Signature::Type sigType = signatureType(); + switch ( sigType ) { + case Signature::Inlined: + sig.setInlinedHtml( d->inlinedHtml ); + sig.setText( d->inlinedHtml ? asCleanedHTML() : mTextEdit->textOrHtml() ); + break; + case Signature::FromCommand: sig.setUrl( commandURL(), true ); - if ( signatureType() == Signature::FromFile ) + break; + case Signature::FromFile: sig.setUrl( fileURL(), false ); + break; + case Signature::Disabled: + /* do nothing */ + break; + } + sig.setType( sigType ); return sig; } void SignatureConfigurator::setSignature( const Signature & sig ) { setSignatureType( sig.type() ); if ( sig.isInlinedHtml() ) mHtmlCheck->setCheckState( Qt::Checked ); else mHtmlCheck->setCheckState( Qt::Unchecked ); slotSetHtml(); setInlineText( sig.text() ); if ( sig.type() == Signature::FromFile ) setFileURL( sig.url() ); else setFileURL( QString() ); if ( sig.type() == Signature::FromCommand ) setCommandURL( sig.url() ); else setCommandURL( QString() ); } void SignatureConfigurator::slotEnableEditButton( const QString & url ) { mEditButton->setDisabled( url.trimmed().isEmpty() ); } void SignatureConfigurator::slotEdit() { QString url = fileURL(); // slotEnableEditButton should prevent this assert from being hit: assert( !url.isEmpty() ); (void)KRun::runUrl( KUrl( url ), QString::fromLatin1("text/plain"), this ); } QString SignatureConfigurator::asCleanedHTML() const { QString text = mTextEdit->toHtml(); // Beautiful little hack to find the html headers produced by Qt. QTextDocument textDocument; QString html = textDocument.toHtml(); // Now remove each line from the text, the result is clean html. foreach( const QString& line, html.split( '\n' ) ){ text.remove( line + '\n' ); } return text; } // "use HTML"-checkbox (un)checked void SignatureConfigurator::slotSetHtml() { if ( mHtmlCheck->checkState() == Qt::Unchecked ) { mHtmlCheck->setText( i18n("&Use HTML") ); mEditToolBar->setVisible( false ); mEditToolBar->setEnabled( false ); mFormatToolBar->setVisible( false ); mFormatToolBar->setEnabled( false ); mTextEdit->switchToPlainText(); d->inlinedHtml = false; } else { mHtmlCheck->setText( i18n("&Use HTML (disabling removes formatting)") ); d->inlinedHtml = true; mEditToolBar->setVisible( true ); mEditToolBar->setEnabled( true ); mFormatToolBar->setVisible( true ); mFormatToolBar->setEnabled( true ); mTextEdit->enableRichTextMode(); } } } #include "signatureconfigurator.moc"