Warning, /pim/akonadi/docs/client_libraries.md is written in an unsupported language. File is not indexed.
0001 # Akonadi client libraries # {#client_libraries}
0002
0003 [TOC]
0004
0005 Akonadi client libraries consist of three libraries that provide tools to access
0006 the Akonadi PIM data server: AkonadiCore, AkonadiWidgets and AkonadiAgentBase.
0007 All processes accessing Akonadi, including those which communicate with a remote
0008 server [agents](@ref agents), are considered clients.
0009
0010 <!--
0011 Additional information about Akonadi:
0012
0013 - <a href="https://api.kde.org/kdesupport-api/akonadi-apidocs/">Akonadi Server documentation</a>
0014 - \ref akonadi_history
0015 - <a href="https://community.kde.org/KDE_PIM/Akonadi">Website</a>
0016 - <a href="https://techbase.kde.org/KDE_PIM/Akonadi">Wiki</a>
0017
0018 Tools for developers:
0019
0020 - <a href="https://bugs.kde.org/buglist.cgi?query_format=advanced&product=Akonadi&component=libakonadi&bug_status=REPORTED&bug_status=CONFIRMED&bug_status=ASSIGNED&bug_status=REOPENED">Bugtracker</a>
0021 //-->
0022
0023 # Akonadi Objects # {#objects}
0024
0025 Akonadi works on two basic object types: collections and items.
0026
0027 Collections are comparable to folders in a file system and are represented by
0028 the class Akonadi::Collection. Every collection has an associated cache policy
0029 represented by the class Akonadi::CachePolicy which defines what part of its
0030 content is cached for how long. All available ways to work with collections are
0031 listed in the "[Collections](#collections)" section.
0032
0033 Akonadi items are comparable to files in a file system and are represented by
0034 the class Akonadi::Item. Each item represents a single PIM object such as a mail
0035 or a contact. The actual object it represents is its so-called payload. All
0036 available ways to work with items are listed in the "[Items](#items)"
0037 section.
0038
0039 Both items and collections are identified by a persistent unique identifier.
0040 Also, they can contain arbitrary attributes (derived from Akonadi::Attribute) to
0041 attach general or application specific meta data to them.
0042
0043 # Collection retrieval and manipulation # {#collections}
0044
0045 A collection is represented by the Akonadi::Collection class.
0046
0047 Classes to retrieve information about collections:
0048
0049 * Akonadi::CollectionFetchJob
0050 * Akonadi::CollectionStatisticsJob
0051
0052 Classes to manipulate collections:
0053
0054 * Akonadi::CollectionCreateJob
0055 * Akonadi::CollectionCopyJob
0056 * Akonadi::CollectionModifyJob
0057 * Akonadi::CollectionDeleteJob
0058
0059 There is also Akonadi::CollectionModel, which is a self-updating model class which can
0060 be used in combination with Akonadi::CollectionView. Akonadi::CollectionFilterProxyModel
0061 can be used to limit a displayed collection tree to collections supporting a certain
0062 type of PIM items. Akonadi::CollectionPropertiesDialog provides an extensible properties
0063 dialog for collections. Often needed QAction for collection operations are provided by
0064 Akonadi::StandardActionManager.
0065
0066 # PIM item retrieval and manipulation # {#items}
0067
0068 PIM items are represented by classes derived from Akonadi::Item.
0069 Items can be retrieved using Akonadi::ItemFetchJob.
0070
0071 The following classes are provided to manipulate PIM items:
0072
0073 * Akonadi::ItemCreateJob
0074 * Akonadi::ItemCopyJob
0075 * Akonadi::ItemModifyJob
0076 * Akonadi::ItemDeleteJob
0077
0078 Akonadi::ItemModel provides a self-updating model class which can be used to display the content
0079 of a collection. Akonadi::ItemView is the base class for a corresponding view. Often needed QAction
0080 for item operations are provided by Akonadi::StandardActionManager.
0081
0082 # Low-level access to the Akonadi server # {#jobs}
0083
0084 Accessing the Akonadi server is done using job classes derived from Akonadi::Job. The
0085 communication channel with the server is provided by Akonadi::Session.
0086
0087 To use server-side transactions, the following jobs are provided:
0088
0089 * Akonadi::TransactionBeginJob
0090 * Akonadi::TransactionCommitJob
0091 * Akonadi::TransactionRollbackJob
0092
0093 There also is Akonadi::TransactionSequence which can be used to automatically group
0094 a set of jobs into a single transaction.
0095
0096
0097 # Change notifications (Monitor) # {#monitor}
0098
0099 The Akonadi::Monitor class allows you to monitor specific resources,
0100 collections and PIM items for changes. Akonadi::ChangeRecorder augments this
0101 by providing a way to record and replay change notifications.
0102
0103 # PIM item serializer # {#serializer}
0104
0105 The class Akonadi::ItemSerializer is responsible for converting between the stored (binary) representation
0106 of a PIM item and the objects used to handle these items provided by the corresponding libraries (kabc, kcal, etc.).
0107
0108 Serializer plugins allow you to add support for new kinds of PIM items to Akonadi.
0109 Akonadi::ItemSerializerPlugin can be used as a base class for such a plugin.
0110
0111 # Agents and Resources # {#resource}
0112
0113 Agents are independent processes that watch the Akonadi store for changes and react to them if necessary.
0114 Example: The Akonadi Indexing Agent is an agent that watches Akonadi for new emails, calendar events, etc.,
0115 retrieves the new items and indexes them into a special database.
0116
0117 The class Akonadi::AgentBase is the common base class for all agents. It provides commonly needed
0118 functionality such as change monitoring and recording.
0119
0120 Resources are a special kind of agents. They are used as the actual backend for whatever data the resource represents.
0121 In this situation the akonadi server acts more like a proxy service. It caches data on behalf of its clients
0122 (client here being the Resource), not permanently storing it. The Akonadi server forwards item retrieval requests to the
0123 corresponding resource, if the item is not in the cache.
0124 Example: The imap resource is responsible for storing and fetching emails from an imap server.
0125
0126 Akonadi::ResourceBase is the base class for them. It provides the
0127 necessary interfaces to the server as well as many convenience functions to make implementing
0128 a new resource as easy as possible. Note that a collection contains items belonging to a single
0129 resource, although there are plans in the future for 'virtual' collections which will contain
0130 the results of a search query spanning multiple resources.
0131
0132 A resource can support multiple mimetypes. There are two places where a resource can specify
0133 mimetypes: in its desktop files, and in the content mimetypes field of
0134 collections created by it. The ones in the desktop file are used for
0135 filtering agent types, e.g. in the resource creation dialogs. The collection
0136 content mimetypes specify what you can actually put into a collection, which
0137 is not necessarily the same (e.g. the Kolab resource supports contacts and events, but not
0138 in the same folder).
0139
0140
0141 # Integration in your Application # {#integration}
0142
0143 Akonadi::Control provides ways to ensure that the Akonadi server is running, to monitor its availability
0144 and provide help on server-side errors. A more low-level interface to the Akonadi server is provided
0145 by Akonadi::ServerManager.
0146
0147 A set of standard actions is provided by Akonadi::StandardActionManager. These provide consistent
0148 look and feel across applications.
0149
0150
0151 This library provides classes for KDE applications to communicate with the Akonadi server. The most high-level interface to Akonadi is the Models and Views provided in this library. Ready to use models are provided for use with views to interact with a tree of collections, a list of items in a collection, or a combined tree of Collections and items.
0152
0153 ## Collections and Items ## {#collections_and_items}
0154
0155 In the Akonadi concept, Items are individual objects of PIM data, e.g. emails, contacts, events, notes etc. The data in an item is stored in a typed payload. For example, if an Akonadi Item holds a contact, the contact is available as a KABC::Addressee:
0156
0157 ~~~~~~~~~~~~~{.cpp}
0158 if (item.hasPayload<KABC::Addressee>()) {
0159 KABC::Addressee addr = item.payload<KABC::Addressee>();
0160 // use addr in some way...
0161 }
0162 ~~~~~~~~~~~~~
0163
0164 Additionally, an Item must have a mimetype which corresponds to the type of payload it holds.
0165
0166 Collections are simply containers of Items. A Collection has a name and a list of mimetypes that it may contain. A collection may for example contain events if it can contain the mimetype 'text/calendar'. A Collection itself (as opposed to its contents) has a mimetype, which is the same for all Collections. A Collection which can itself contain Collections must be able to contain the Collection mimetype.
0167
0168 ~~~~~~~~~~~~~{.cpp}
0169 Collection col;
0170 // This collection can contain events and nested collections.
0171 col.setContentMimetypes({ Akonadi::Collection::mimeType(),
0172 QStringLiteral("text/calendar") });
0173 ~~~~~~~~~~~~~
0174
0175 This system makes it simple to create PIM applications. For example, to create an application for viewing and editing events, you simply need to tell %Akonadi to retrieve all items matching the mimetype 'text/calendar'.
0176
0177 ## Convenience Mimetype Accessors ## {#convenience_mimetype_accessors}
0178
0179 In order to avoid typos, improve readability, and to encapsulate the correct mimetypes for particular pim items, many of the standard classes have an accessor for the kind of mimetype the can handle. For example, you can use KMime::Message::mimeType() for emails, KABC::Addressee::mimeType() for contacts etc. It makes sense to define a similar static function in your own types.
0180
0181 ~~~~~~~~~~~~~{.cpp}
0182 col.setContentMimetypes({ Akonadi::Collection::mimeType(),
0183 KABC::Addressee::mimeType(),
0184 KMime::Message::mimeType() });
0185 ~~~~~~~~~~~~~
0186
0187 ## Models and Views ## {#models_and_views}
0188 Akonadi models and views are a high level way to interact with the Akonadi server. Most applications will use these classes. See the EntityTreeModel documentation for more information.
0189
0190 Models provide an interface for viewing, deleting and moving Items and Collections. New Items can also be created by dropping data of the appropriate type on a model. Additionally, the models are updated automatically if another application changes the data or inserts or deletes items etc.
0191
0192 Akonadi provides several models for particular uses, e.g. the MailModel is used for emails and the ContactsModel is used for showing contacts. Additional specific models can be implemented using EntityTreeModel as a base class.
0193
0194 A typical use of these would be to create a model and use proxy models to make the view show different parts of the model. For example, show a collection tree in on one side and show items in a selected collection in another view.
0195
0196 ~~~~~~~~~~~~~{.cpp}
0197 mailModel = new MailModel(session, monitor, this);
0198
0199 collectionTree = new Akonadi::EntityMimeTypeFilterModel(this);
0200 collectionTree->setSourceModel(mailModel);
0201 // Filter out everything that is not a collection.
0202 collectionTree->addMimeTypeInclusionFilter(Akonadi::Collection::mimeType());
0203 collectionTree->setHeaderSet(Akonadi::EntityTreeModel::CollectionTreeHeaders);
0204
0205 collectionView = new Akonadi::EntityTreeView(this);
0206 collectionView->setModel(collectionTree);
0207
0208 itemList = new Akonadi::EntityMimeTypeFilterModel(this);
0209 itemList->setSourceModel(mailModel);
0210 // Filter out collections
0211 itemList->addMimeTypeExclusionFilter(Akonadi::Collection::mimeType());
0212 itemList->setHeaderSet(Akonadi::EntityTreeModel::ItemListHeaders);
0213
0214 itemView = new Akonadi::EntityTreeView(this);
0215 itemView->setModel(itemList);
0216 ~~~~~~~~~~~~~
0217
0218 
0219
0220 The content of the model is determined by the configuration of the Monitor passed into it. The examples below show a use of the EntityTreeModel and some proxy models for a simple hierarchical note collection. As the model is generic, the configuration and proxy models will also work with any other mimetype.
0221
0222 ~~~~~~~~~~~~~{.cpp}
0223 // Configure what should be shown in the model:
0224 Monitor *monitor = new Akonadi::Monitor(this);
0225 monitor->fetchCollection(true);
0226 monitor->setItemFetchScope(scope);
0227 monitor->setCollectionMonitored(Akonadi::Collection::root());
0228 monitor->setMimeTypeMonitored(MyEntity::mimeType());
0229
0230 Akonadi::Session *session = new Akonadi::Session(QByteArray("MyEmailApp-") + QByteArray::number(qrand()), this);
0231 monitor->setSession(session);
0232
0233 Akonadi::EntityTreeModel *entityTree = new Akonadi::EntityTreeModel(monitor, this);
0234 ~~~~~~~~~~~~~
0235
0236 
0237
0238 The EntityTreeModel can be further configured for certain behaviours such as fetching of collections and items.
0239
0240 To create a model of only a collection tree and no items, set that in the model. This is just like CollectionModel:
0241
0242 ~~~~~~~~~~~~~{.cpp}
0243 entityTree->setItemPopulationStrategy(Akonadi::EntityTreeModel::NoItemPopulation);
0244 ~~~~~~~~~~~~~
0245
0246 
0247
0248 Or, create a model of only items and not child collections. This is just like ItemModel:
0249
0250 ~~~~~~~~~~~~~{.cpp}
0251 entityTree->setRootCollection(myCollection);
0252 entityTree->setCollectionFetchStrategy(Akonadi::EntityTreeModel::FetchNoCollections);
0253 ~~~~~~~~~~~~~
0254
0255 Or, to create a model which includes items and first level collections:
0256
0257 ~~~~~~~~~~~~~{.cpp}
0258 entityTree->setCollectionFetchStrategy(Akonadi::EntityTreeModel::FetchFirstLevelCollections);
0259 ~~~~~~~~~~~~~
0260
0261 The items in the model can also be inserted lazily for performance reasons. The Collection tree is always built immediately.
0262
0263 Additionally, a KDescendantsProxyModel may be used to alter how the items in the tree are presented.
0264
0265 ~~~~~~~~~~~~~{.cpp}
0266 // ... Create an entityTreeModel
0267 KDescendantsProxyModel *descProxy = new KDescendantsProxyModel(this);
0268 descProxy->setSourceModel(entityTree);
0269 view->setModel(descProxy);
0270 ~~~~~~~~~~~~~
0271
0272 
0273
0274 KDescendantsProxyModel can also display ancestors of each Entity in the list.
0275
0276 ~~~~~~~~~~~~~{.cpp}
0277 // ... Create an entityTreeModel
0278 KDescendantsProxyModel *descProxy = new KDescendantsProxyModel(this);
0279 descProxy->setSourceModel(entityTree);
0280
0281 // #### This is new
0282 descProxy->setDisplayAncestorData(true, QLatin1StringView(" / "));
0283
0284 view->setModel(descProxy);
0285 ~~~~~~~~~~~~~
0286
0287 
0288
0289 This proxy can be combined with a filter to for example remove collections.
0290
0291 ~~~~~~~~~~~~~{.cpp}
0292 // ... Create an entityTreeModel
0293 KDescendantsProxyModel *descProxy = new KDescendantsProxyModel(this);
0294 descProxy->setSourceModel(entityTree);
0295
0296 // #### This is new.
0297 Akonadi::EntityMimeTypeFilterModel *filterModel = new Akonadi::EntityMimeTypeFilterModel(this);
0298 filterModel->setSourceModel(descProxy);
0299 filterModel->setExclusionFilter({ Akonadi::Collection::mimeType() });
0300
0301 view->setModel(filterModel);
0302 ~~~~~~~~~~~~~
0303
0304 
0305
0306 It is also possible to show the root item as part of the selectable model:
0307
0308 ~~~~~~~~~~~~~{.cpp}
0309 entityTree->setIncludeRootCollection(true);
0310 ~~~~~~~~~~~~~
0311
0312 
0313
0314 By default the displayed name of the root collection is '[*]', because it doesn't require i18n, and is generic. It can be changed too.
0315
0316 ~~~~~~~~~~~~~{.cpp}
0317 entityTree->setIncludeRootCollection(true);
0318 entityTree->setRootCollectionDisplayName(i18nc("Name of top level for all collections in the application", "[All]"))
0319 ~~~~~~~~~~~~~
0320
0321 
0322
0323 These can of course be combined to create an application which uses one EntityTreeModel along with several proxies and views.
0324
0325 ~~~~~~~~~~~~~{.cpp}
0326 // ... create an EntityTreeModel.
0327 Akonadi::EntityMimeTypeFilterModel *collectionTree = new Akonadi::EntityMimeTypeFilterModel(this);
0328 collectionTree->setSourceModel(entityTree);
0329 // Filter to include collections only:
0330 collectionTree->setInclusionFilter({ Akonadi:: Collection::mimeType() });
0331 Akonadi::EntityTreeView *treeView = new Akonadi::EntityTreeView(this);
0332 treeView->setModel(collectionTree);
0333
0334 Akonadi::EntityMimeTypeFilterModel *itemList = new Akonadi::EntityMimeTypeFilterModel(this);
0335 itemList->setSourceModel(entityTree);
0336 // Filter *out* collections
0337 itemList->setExclusionFilter({ Akonadi::Collection::mimeType() });
0338 Akonadi::EntityTreeView *listView = new Akonadi::EntityTreeView(this);
0339 listView->setModel(itemList);
0340 ~~~~~~~~~~~~~
0341
0342 
0343
0344 Or to also show items of child collections in the list:
0345
0346 ~~~~~~~~~~~~~{.cpp}
0347 // ... Create an entityTreeModel
0348 collectionTree = new Akonadi::EntityMimeTypeFilterModel(this);
0349 collectionTree->setSourceModel(entityTree);
0350
0351 // Include only collections in this proxy model.
0352 collectionTree->addMimeTypeInclusionFilter(Akonadi::Collection::mimeType());
0353
0354 treeview->setModel(collectionTree);
0355
0356 descendedList = new KDescendantsProxyModel(this);
0357 descendedList->setSourceModel(entityTree);
0358
0359 itemList = new Akonadi::EntityMimeTypeFilterModel(this);
0360 itemList->setSourceModel(descendedList);
0361
0362 // Exclude collections from the list view.
0363 itemList->addMimeTypeExclusionFilter(Akonadi::Collection::mimeType());
0364
0365 listView = new EntityTreeView(this);
0366 listView->setModel(itemList);
0367 ~~~~~~~~~~~~~
0368
0369 
0370
0371 Note that it is important in this case to use the DescendantEntitesProxyModel before the EntityMimeTypeFilterModel. Otherwise, by filtering out the collections first, you would also be filtering out their child items.
0372
0373 A SelectionProxyModel can be used to simplify managing selection in one view through multiple proxy models to a representation in another view. The selectionModel of the initial view is used to create a proxied model which includes only the selected indexes and their children.
0374
0375
0376 ~~~~~~~~~~~~~{.cpp}
0377 // ... Create an entityTreeModel
0378 collectionTree = new Akonadi::EntityMimeTypeFilterModel(this);
0379 collectionTree->setSourceModel(entityTree);
0380
0381 // Include only collections in this proxy model.
0382 collectionTree->addMimeTypeInclusionFilter(Akonadi::Collection::mimeType());
0383
0384 treeview->setModel(collectionTree);
0385
0386 // SelectionProxyModel can handle complex selections:
0387 treeview->setSelectionMode(QAbstractItemView::ExtendedSelection);
0388
0389 SelectionProxyModel *selProxy = new SelectionProxyModel(treeview->selectionModel(), this);
0390 selProxy->setSourceModel(entityTree);
0391
0392 Akonadi::EntityTreeView *selView = new Akonadi::EntityTreeView(splitter);
0393 selView->setModel(selProxy);
0394 ~~~~~~~~~~~~~
0395
0396 
0397
0398 The SelectionProxyModel can handle complex selections.
0399
0400 
0401
0402 If an index and one or more of its descendants are selected, only the top-most selected index (including all of its descendants) are included in the proxy model. (Though this is configurable. See below)
0403
0404 
0405
0406 SelectionProxyModel allows configuration using the methods setStartWithChildTrees, setOmitDescendants, setIncludeAllSelected. See testapp/proxymodeltestapp to try out the 5 valid configurations.
0407
0408 Obviously, the SelectionProxyModel may be used in a view, or further processed with other proxy models. See the example_contacts application for example which uses a further KDescendantsProxyModel and EntityMimeTypeFilterModel on top of a SelectionProxyModel.
0409
0410 The SelectionProxyModel orders its items in the same top-to-bottom order as they appear in the source model. Note that this order may be different to the order in the selection model if there is a QSortFilterProxyModel between the selection and the source model.
0411
0412 
0413
0414 Details on the actual implementation of lazy population are described on [this page](@ref internals).
0415
0416 # Jobs and Monitors # {#jobs_and_monitors}
0417
0418 The lower level way to interact with Akonadi is to use Jobs and Monitors (This is what models use internally). Jobs are used to make changes to akonadi, and in some cases (e.g., a fetch job) emit a signal with data resulting from the job. A Monitor reports changes made to the data stored in Akonadi (e.g., creating, updating, deleting or moving an item or collection ) via signals.
0419
0420 Typically, an application will configure a monitor to report changes to a particular Collection, mimetype or resource, and then connect to the signals it emits.
0421
0422 Most applications will use some of the low level api for actions unrelated to a model-tree view, such as creating new items and collections.
0423
0424 # Tricky details # {#tricky_details}
0425
0426 ## Change Conflicts ## {#change_conflicts}
0427 It is possible that while an application is editing an item, that item gets updated in akonadi. Akonadi will notify the application that that item has changed via a Monitor signal. It is the responsibility of the application to handle the conflict by for example offering the user a dialog to resolve it. Alternatively, the application could ignore the dataChanged signal for that item, and will get another chance to resolve the conflict when trying to save the result back to akonadi. In that case, the ItemModifyJob will fail and report that the revision number of the item on the server differs from its revision number as reported by the job. Again, it is up to the application to handle this case.
0428
0429 This is something that every application using akonadi will have to handle.
0430
0431 ## Using Item::Id or Collection::Id as an identifier ## {#using_id_as_an_identifier}
0432
0433 Items and Collections have a id() member which is a unique identifier used by akonadi. It can be useful to use the id() as an identifier when storing Collections or Items.
0434
0435 However, as an item and a collection can have the same id(), if you need to store both Collections and Items together by a simple identifier, conflicts can occur.
0436
0437 ~~~~~~~~~~~~~{.cpp}
0438 QString getRemoteIdById(Item::Id id)
0439 {
0440 // Note:
0441 // m_items is QHash<Item::Id, Item>
0442 // m_collections is QHash<Collection::Id, Collection>
0443 if (m_items.contains(id)) {
0444 // Oops, we could accidentally match a collection here.
0445 return m_items.value(id).remoteId();
0446 } else if (m_collections.contains(id)) {
0447 return m_collections.value(id).remoteId();
0448 }
0449 return QString();
0450 }
0451 ~~~~~~~~~~~~~
0452
0453 In this case, it makes more sense to use a normal qint64 as the internal identifier, and use the sign bit to determine if the identifier refers to a Collection or an Item. This is done in the implementation of EntityTreeModel to tell Collections and Items apart.
0454
0455 ~~~~~~~~~~~~~{.cpp}
0456 qstring getremoteidbyinternalidentifier(qint64 internalidentifier)
0457 {
0458 // note:
0459 // m_items is qhash<item::id, item>
0460 // m_collections is qhash<collection::id, collection>
0461
0462 // if the id is negative, it refers to an item
0463 // otherwise it refers to a collection.
0464
0465 if (internalidentifier < 0) {
0466 // reverse the sign of the id before using it.
0467 return m_items.value(-internalidentifier).remoteid();
0468 } else {
0469 return m_collections.value(internalidentifier).remoteid();
0470 }
0471 }
0472 ~~~~~~~~~~~~~
0473
0474
0475 ### Unordered Lists ### {#unordered_lists}
0476 Collection and Item both provide a ::List to represent groups of objects. However the objects in the list are usually not ordered in any particular way, even though the API provides methods to work with an ordered list. It makes more sense to think of it as a Set instead of a list in most cases.
0477
0478 For example, when using an ItemFetchJob to fetch the items in a collection, the items could be in any order when returned from the job. The order that a Monitor emits notices of changes is also indeterminate. By using a Transaction however, it is sometimes possible to retrieve objects in order. Additionally, using s constructor overload in the CollectionFetchJob it is possible to retrieve collections in a particular order.
0479
0480 ~~~~~~~~~~~~~{.cpp}
0481 Collection::List getCollections(const QList<Collection::Id> &idsToGet)
0482 {
0483 Collection::List getList;
0484 for (Collection::Id id : idsToGet) {
0485 getList << Collection(id);
0486 }
0487 CollectionFetchJob *job = CollectionFetchJob(getList);
0488 if (job->exec()) {
0489 // job->collections() is in the same order as the ids in idsToGet.
0490 }
0491 }
0492 ~~~~~~~~~~~~~
0493
0494 # Resources # {#resources}
0495 The KDEPIM module includes resources for handling many types of PIM data, such as imap email, vcard files and vcard directories, ical event files etc. These cover many of the sources for your PIM data, but in the case that you need to use data from another source (for example a website providing a contacts storage service and an api), you simply have to write a new resource.
0496
0497 https://techbase.kde.org/Development/Tutorials/Akonadi/Resources
0498
0499 # Serializers # {#serializers}
0500 Serializers provide the functionality of converting raw data, for example from a file, to a strongly typed object of PIM data. For example, the addressee serializer reads data from a file and creates a KABC::Addressee object.
0501
0502 New serializers can also easily be written if the data you are dealing with is not one of the standard PIM data types.
0503
0504 # Implementation details # {#implementation_details}
0505
0506 ## Updating Akonadi Models ## {#updating_models}
0507
0508 NOTE: The details here are only relevant if you are writing a new view using EntityTreeModel, or writing a new model.
0509
0510 Because communication with Akonadi happens asynchronously, and the models only hold a cached copy of the data on the akonadi server, some typical behaviours of models are not followed by Akonadi models.
0511
0512 For example, when setting data on a model via a view, most models synchronously update their internal store and notify akonadi to update its view of the data by returning <tt>true</tt>.
0513
0514 <!--
0515 TODO: Render this manually
0516 @dot
0517 digraph utmg {
0518 rankdir = LR;
0519 { node [label="",style=invis, height=0, width=0 ];
0520 V_Set_Data; V_Result; V_Data_Changed;
0521 M_Set_Data; M_Result;
0522 }
0523 { node [shape=box, fillcolor=lightyellow, style=filled,fontsize=26];
0524 View [label=":View"]; Model [label=":Model"];
0525 }
0526 { node [style=invis];
0527 View_EOL; Model_EOL;
0528 }
0529 {
0530 V_Set_Data -> M_Set_Data [label="Set Data"];
0531 M_Result -> V_Result [label="Success",arrowhead="vee", style="dashed"];
0532 V_Result -> V_Data_Changed [label="Update View \n[ Success = True ]"];
0533 }
0534
0535 // Dashed Vertical lines for object lifetimes.
0536 edge [style=dashed, arrowhead=none];
0537 { rank = same; View -> V_Set_Data -> V_Result -> V_Data_Changed -> View_EOL; }
0538 { rank = same; Model -> M_Set_Data -> M_Result -> Model_EOL; }
0539
0540 // Make sure top nodes are in a straight line.
0541 { View -> Model [style=invis]; }
0542 // And the bottom nodes.
0543 { View_EOL -> Model_EOL [style=invis]; }
0544 }
0545 @enddot
0546 //-->
0547
0548 Akonadi models only cache data from the Akonadi server. To update data on an Akonadi::Entity stored in a model, the model makes a request to the Akonadi server to update the model data. At that point the data cached internally in the model is not updated, so <tt>false</tt> is always returned from setData. If the request to update data on the Akonadi server is successful, an Akonadi::Monitor notifies the model that the data on that item has changed. The model then updates its internal data store and notifies the view that the data has changed. The details of how the Monitor communicates with akonadi are omitted for clarity.
0549
0550 <!--
0551 TODO: Render this into PNG
0552 @dot
0553 digraph utmg {
0554 rankdir = LR;
0555 { node [label="",style=invis, height=0, width=0 ];
0556 ETV_Set_Data; ETV_Result; ETV_Data_Changed;
0557 ETM_Set_Data; ETM_Result; ETM_Changed;
0558 M_Dummy_1; M_Changed;
0559 AS_Modify;
0560 }
0561 { node [shape=box, fillcolor=lightyellow, style=filled,fontsize=26];
0562 EntityTreeView [label=":View"]; EntityTreeModel [label=":Model"]; Monitor [label=":Monitor"]; Akonadi_Server [label=":Akonadi"];
0563 }
0564 { node [style=invis];
0565 EntityTreeView_EOL; EntityTreeModel_EOL; Monitor_EOL; Akonadi_Server_EOL;
0566 }
0567 {
0568 { rank = same; ETV_Set_Data -> ETM_Set_Data [label="Set Data"]; }
0569 { rank = same; ETM_Result -> ETV_Result [label="False",arrowhead="vee", style="dashed"]; }
0570 { rank = same; ETM_Result -> M_Dummy_1 [style=invis]; }
0571 { rank = same; ETM_Set_Data -> AS_Modify [arrowhead="vee",taillabel="Modify Item", labeldistance=14.0, labelangle=10]; }
0572 { rank = same; M_Changed -> ETM_Changed [arrowhead="vee",label="Item Changed"]; }
0573 { rank = same; ETM_Changed -> ETV_Data_Changed [arrowhead="vee",label="Update View"]; }
0574 }
0575
0576 // Dashed Vertical lines for object lifetimes.
0577 edge [style=dashed, arrowhead=none];
0578 { rank = same; EntityTreeView -> ETV_Set_Data -> ETV_Result -> ETV_Data_Changed -> EntityTreeView_EOL; }
0579 { rank = same; EntityTreeModel -> ETM_Set_Data -> ETM_Result -> ETM_Changed -> EntityTreeModel_EOL; }
0580 { rank = same; Monitor -> M_Dummy_1 -> M_Changed -> Monitor_EOL; }
0581 { rank = same; Akonadi_Server -> AS_Modify -> Akonadi_Server_EOL; }
0582
0583 // Make sure top nodes are in a straight line.
0584 { EntityTreeView -> EntityTreeModel -> Monitor -> Akonadi_Server [style=invis]; }
0585 // And the bottom nodes.
0586 { EntityTreeView_EOL -> EntityTreeModel_EOL -> Monitor_EOL -> Akonadi_Server_EOL [style=invis]; }
0587 }
0588 @enddot
0589 //-->
0590
0591 Similarly, in drag and drop operations, most models would update an internal data store and return <tt>true</tt> from dropMimeData if the drop is successful.
0592
0593 <!--
0594 TODO: Render this to PNG
0595 @dot
0596 digraph utmg {
0597 rankdir = LR;
0598 { node [label="",style=invis, height=0, width=0 ];
0599 Left_Phantom; Left_Phantom_DropEvent;
0600 V_DropEvent; V_Result; V_Data_Changed; V_Dummy_1;
0601 M_DropMimeData; M_Result;
0602 }
0603 { node [shape=box, fillcolor=lightyellow, style=filled,fontsize=26];
0604 View [label=":View"]; Model [label=":Model"];
0605 }
0606 { node [style=invis];
0607 Left_Phantom_EOL;
0608 View_EOL; Model_EOL;
0609 }
0610 {
0611 Left_Phantom_DropEvent -> V_DropEvent [label="DropEvent"];
0612 V_DropEvent -> M_DropMimeData [label="DropMimeData"];
0613 M_Result -> V_Result [label="Success",arrowhead="vee", style="dashed"];
0614 V_Result -> V_Data_Changed [label="Update View \n[Success = True]"];
0615 }
0616
0617 // Dashed Vertical lines for object lifetimes.
0618 edge [style=dashed, arrowhead=none];
0619 { rank = same; View -> V_DropEvent -> V_Result -> V_Dummy_1 -> V_Data_Changed -> View_EOL; }
0620 { rank = same; Model -> M_DropMimeData -> M_Result -> Model_EOL; }
0621
0622 //Phantom line
0623 { rank= same; Left_Phantom -> Left_Phantom_DropEvent -> Left_Phantom_EOL [style=invis]; }
0624
0625 // Make sure top nodes are in a straight line.
0626 { Left_Phantom -> View -> Model [style=invis]; }
0627 // And the bottom nodes.
0628 { Left_Phantom_EOL -> View_EOL -> Model_EOL [style=invis]; }
0629 }
0630 @enddot
0631 //--->
0632
0633 Akonadi models, for the same reason as above, always return false from dropMimeData. At the same time a suitable request is sent to the akonadi server to make the changes resulting from the drop (for example, moving or copying an entity, or adding a new entity to a collection etc). If that request is successful, the Akonadi::Monitor notifies the model that the data is changed and the model updates its internal store and notifies the view that the model data is changed.
0634
0635 <!--
0636 TODO: Render this to PNG
0637 @dot
0638 digraph utmg {
0639 rankdir = LR;
0640 { node [label="",style=invis, height=0, width=0 ];
0641 Left_Phantom; Left_Phantom_DropEvent;
0642 ETV_DropEvent; ETV_Result; ETV_Data_Changed;
0643 ETM_DropMimeData; ETM_Result; ETM_Changed;
0644 M_Dummy_1; M_Changed;
0645 AS_Modify;
0646 }
0647 { node [shape=box, fillcolor=lightyellow, style=filled,fontsize=26];
0648 EntityTreeView [label=":View"];
0649 EntityTreeModel [label=":Model"];
0650 Monitor [label=":Monitor"];
0651 Akonadi_Server [label=":Akonadi"];
0652 }
0653 { node [style=invis];
0654 Left_Phantom_EOL;
0655 EntityTreeView_EOL; EntityTreeModel_EOL; Monitor_EOL; Akonadi_Server_EOL;
0656 }
0657
0658 {
0659 { rank = same; Left_Phantom_DropEvent -> ETV_DropEvent [label="DropEvent"]; }
0660 { rank = same; ETV_DropEvent -> ETM_DropMimeData [label="Drop MimeData"]; }
0661 { rank = same; ETM_Result -> ETV_Result [label="False",arrowhead="vee", style="dashed"]; }
0662 { rank = same; ETM_Result -> M_Dummy_1 [style=invis]; }
0663 { rank = same; ETM_DropMimeData -> AS_Modify [arrowhead="vee",taillabel="Action", labeldistance=14.0, labelangle=10]; }
0664 { rank = same; M_Changed -> ETM_Changed [arrowhead="vee",label="Item Changed"]; }
0665 { rank = same; ETM_Changed -> ETV_Data_Changed [arrowhead="vee",label="Update View"]; }
0666 }
0667
0668 // Dashed Vertical lines for object lifetimes.
0669 edge [style=dashed, arrowhead=none];
0670 { rank = same; EntityTreeView -> ETV_DropEvent -> ETV_Result -> ETV_Data_Changed -> EntityTreeView_EOL; }
0671 { rank = same; EntityTreeModel -> ETM_DropMimeData -> ETM_Result -> ETM_Changed -> EntityTreeModel_EOL; }
0672 { rank = same; Monitor -> M_Dummy_1 -> M_Changed -> Monitor_EOL; }
0673 { rank = same; Akonadi_Server -> AS_Modify -> Akonadi_Server_EOL; }
0674
0675 //Phantom line
0676 { rank= same; Left_Phantom -> Left_Phantom_DropEvent -> Left_Phantom_EOL [style=invis]; }
0677
0678 // Make sure top nodes are in a straight line.
0679 { Left_Phantom -> EntityTreeView -> EntityTreeModel -> Monitor -> Akonadi_Server [style=invis]; }
0680 // And the bottom nodes.
0681 { Left_Phantom_EOL -> EntityTreeView_EOL -> EntityTreeModel_EOL -> Monitor_EOL -> Akonadi_Server_EOL [style=invis]; }
0682 }
0683 @enddot
0684 //-->