summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2012-06-27 08:22:11 (GMT)
committerChristian Mollekopf <chrigi_1@fastmail.fm>2012-06-27 08:22:11 (GMT)
commite51f456146457a444dab9c477c8e21bcd9faf9b4 (patch)
treeee4f5186d6e58c7a4136bf915e9c3b2efa4c5576
parentad466258dc5a568260c33808827b8870777a69f7 (diff)
downloadlibcalendaring-e51f456146457a444dab9c477c8e21bcd9faf9b4.tar.gz
akonadi notes
-rw-r--r--notes/CMakeLists.txt45
-rwxr-xr-xnotes/Messages.sh2
-rw-r--r--notes/akonadi-notes_export.h39
-rw-r--r--notes/noteutils.cpp546
-rw-r--r--notes/noteutils.h272
-rw-r--r--notes/tests/CMakeLists.txt6
-rw-r--r--notes/tests/notestest.cpp85
7 files changed, 995 insertions, 0 deletions
diff --git a/notes/CMakeLists.txt b/notes/CMakeLists.txt
new file mode 100644
index 0000000..941a324
--- /dev/null
+++ b/notes/CMakeLists.txt
@@ -0,0 +1,45 @@
+project(akonadi-notes)
+
+set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
+cmake_minimum_required(VERSION 2.6)
+
+# only available from cmake-2.8.0
+if(${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} GREATER 7)
+ cmake_policy(SET CMP0012 NEW)
+endif()
+
+# only available from cmake-2.8.4
+if(${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} GREATER 7 AND
+ ${CMAKE_PATCH_VERSION} GREATER 3)
+ cmake_policy(SET CMP0017 NEW)
+endif()
+
+find_package(Qt4 4.6.0 REQUIRED)
+
+include_directories(${QT_INCLUDES})
+
+include_directories(
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_BINARY_DIR}
+)
+
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -UQT_NO_CAST_FROM_ASCII -UQT_NO_CAST_TO_ASCII ${KDE4_ENABLE_EXCEPTIONS}")
+
+set(akonadinotes_LIB_SRC
+ noteutils.cpp
+)
+add_library(calendaring-akonadi-notes ${LIBRARY_TYPE} ${akonadinotes_LIB_SRC})
+
+target_link_libraries(calendaring-akonadi-notes calendaring-kmime ${QT_QTXML_LIBRARY})
+
+set_target_properties(calendaring-akonadi-notes PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION})
+
+install(TARGETS calendaring-akonadi-notes EXPORT kdepimlibsLibraryTargets ${INSTALL_TARGETS_DEFAULT_ARGS})
+
+install( FILES
+ noteutils.h
+ akonadi-notes_export.h
+ DESTINATION ${INCLUDE_INSTALL_DIR}/akonadi/notes COMPONENT Devel
+)
+
+# add_subdirectory(tests)
diff --git a/notes/Messages.sh b/notes/Messages.sh
new file mode 100755
index 0000000..5112a04
--- /dev/null
+++ b/notes/Messages.sh
@@ -0,0 +1,2 @@
+#! /bin/sh
+$XGETTEXT *.cpp -o $podir/akonadinotes.pot
diff --git a/notes/akonadi-notes_export.h b/notes/akonadi-notes_export.h
new file mode 100644
index 0000000..0a4f72c
--- /dev/null
+++ b/notes/akonadi-notes_export.h
@@ -0,0 +1,39 @@
+/* This file is part of the KDE project
+ Copyright (C) 2011 Christian Mollekopf <chrigi_1@fastmail.fm>
+
+ 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_NOTES_EXPORT_H
+#define AKONADI_NOTES_EXPORT_H
+
+/* needed for KDE_EXPORT and KDE_IMPORT macros */
+#include <kdemacros.h>
+
+#ifndef AKONADI_NOTES_EXPORT
+# if defined(KDEPIM_STATIC_LIBS)
+ /* No export/import for static libraries */
+# define AKONADI_NOTES_EXPORT
+# elif defined(MAKE_AKONADI_NOTES_LIB)
+ /* We are building this library */
+# define AKONADI_NOTES_EXPORT KDE_EXPORT
+# else
+ /* We are using this library */
+# define AKONADI_NOTES_EXPORT KDE_IMPORT
+# endif
+#endif
+
+#endif
diff --git a/notes/noteutils.cpp b/notes/noteutils.cpp
new file mode 100644
index 0000000..2ea47a5
--- /dev/null
+++ b/notes/noteutils.cpp
@@ -0,0 +1,546 @@
+/* This file is part of the KDE project
+ Copyright (C) 2011 Christian Mollekopf <chrigi_1@fastmail.fm>
+
+ 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 "noteutils.h"
+
+#include <klocalizedstring.h>
+#include <kdatetime.h>
+#include <kmime/kmime_message.h>
+#include <kdebug.h>
+
+#include <qstring.h>
+#include <quuid.h>
+#include <qdom.h>
+
+namespace Akonadi {
+namespace NoteUtils {
+
+#define X_NOTES_UID_HEADER "X-Akonotes-UID"
+#define X_NOTES_LASTMODIFIED_HEADER "X-Akonotes-LastModified"
+#define X_NOTES_CLASSIFICATION_HEADER "X-Akonotes-Classification"
+#define X_NOTES_CUSTOM_HEADER "X-Akonotes-Custom"
+
+#define CLASSIFICATION_PUBLIC "Public"
+#define CLASSIFICATION_PRIVATE "Private"
+#define CLASSIFICATION_CONFIDENTIAL "Confidential"
+
+#define X_NOTES_URL_HEADER "X-Akonotes-Url"
+#define X_NOTES_LABEL_HEADER "X-Akonotes-Label"
+#define X_NOTES_CONTENTTYPE_HEADER "X-Akonotes-Type"
+#define CONTENT_TYPE_CUSTOM "custom"
+#define CONTENT_TYPE_ATTACHMENT "attachment"
+
+#define ENCODING "utf-8"
+
+class Attachment::AttachmentPrivate
+{
+ public:
+ AttachmentPrivate( const QUrl& url, const QString& mimetype )
+ : mUrl( url ),
+ mMimetype( mimetype )
+ {}
+
+ AttachmentPrivate( const QByteArray& data, const QString& mimetype )
+ : mData( data ),
+ mMimetype( mimetype )
+ {}
+
+ AttachmentPrivate( const AttachmentPrivate &other )
+ {
+ *this = other;
+ }
+
+ QUrl mUrl;
+ QByteArray mData;
+ QString mMimetype;
+ QString mLabel;
+};
+
+
+Attachment::Attachment( const QUrl& url, const QString& mimetype )
+: d_ptr( new Attachment::AttachmentPrivate( url, mimetype ) )
+{
+}
+
+Attachment::Attachment( const QByteArray& data, const QString& mimetype )
+: d_ptr( new Attachment::AttachmentPrivate( data, mimetype ) )
+{
+}
+
+Attachment::Attachment( const Attachment &other )
+: d_ptr(new AttachmentPrivate(*other.d_func()) )
+{
+
+}
+
+Attachment::~Attachment()
+{
+ delete d_ptr;
+}
+
+
+bool Attachment::operator==( const Attachment &a ) const
+{
+ const Q_D( Attachment );
+ if ( d->mUrl.isEmpty() ) {
+ return d->mUrl == a.d_func()->mUrl &&
+ d->mMimetype == a.d_func()->mMimetype &&
+ d->mLabel == a.d_func()->mLabel;
+ }
+ return d->mData == a.d_func()->mData &&
+ d->mMimetype == a.d_func()->mMimetype &&
+ d->mLabel == a.d_func()->mLabel;
+}
+
+void Attachment::operator=( const Attachment &a )
+{
+ *d_ptr = *a.d_ptr;
+}
+
+QUrl Attachment::url() const
+{
+ const Q_D( Attachment );
+ return d->mUrl;
+}
+
+QByteArray Attachment::data() const
+{
+ const Q_D( Attachment );
+ return d->mData;
+}
+
+QString Attachment::mimetype() const
+{
+ const Q_D( Attachment );
+ return d->mMimetype;
+}
+
+void Attachment::setLabel( const QString& label )
+{
+ Q_D( Attachment );
+ d->mLabel = label;
+}
+
+QString Attachment::label() const
+{
+ const Q_D( Attachment );
+ return d->mLabel;
+}
+
+
+
+class NoteMessageWrapper::NoteMessageWrapperPrivate
+{
+ public:
+ NoteMessageWrapperPrivate()
+ : classification( Public )
+ {
+ }
+
+ NoteMessageWrapperPrivate( const KMime::Message::Ptr &msg )
+ : textFormat( Qt::PlainText ),
+ classification( Public )
+ {
+ readMimeMessage(msg);
+ }
+
+ void readMimeMessage(const KMime::Message::Ptr &msg );
+
+ KMime::Content* createCustomPart() const;
+ void parseCustomPart( KMime::Content * );
+
+ KMime::Content* createAttachmentPart( const Attachment & ) const;
+ void parseAttachmentPart( KMime::Content * );
+
+ QString uid;
+ QString title;
+ QString text;
+ Qt::TextFormat textFormat;
+ QString from;
+ KDateTime creationDate;
+ KDateTime lastModifiedDate;
+ QMap< QString, QString > custom;
+ QList<Attachment> attachments;
+ Classification classification;
+};
+
+void NoteMessageWrapper::NoteMessageWrapperPrivate::readMimeMessage(const KMime::Message::Ptr& msg)
+{
+ if (!msg.get()) {
+ kWarning() << "Empty message";
+ return;
+ }
+ title = msg->subject( true )->asUnicodeString();
+ text = msg->mainBodyPart()->decodedText( true ); //remove trailing whitespace, so we get rid of " " in empty notes
+ if ( msg->from( false ) )
+ from = msg->from( false )->asUnicodeString();
+ creationDate = msg->date( true )->dateTime();
+ if ( msg->contentType( false ) && msg->contentType( false )->asUnicodeString() == QLatin1String("text/html") ) {
+ textFormat = Qt::RichText;
+ }
+
+ if (KMime::Headers::Base *lastmod = msg->headerByType(X_NOTES_LASTMODIFIED_HEADER)) {
+ const QByteArray &s = lastmod->asUnicodeString().toLatin1();
+ const char *cursor = s.constData();
+ if (!KMime::HeaderParsing::parseDateTime( cursor, cursor + s.length(), lastModifiedDate)) {
+ kWarning() << "failed to parse lastModifiedDate";
+ }
+ }
+
+ if (KMime::Headers::Base *uidHeader = msg->headerByType(X_NOTES_UID_HEADER)) {
+ uid = uidHeader->asUnicodeString();
+ }
+
+ if (KMime::Headers::Base *classificationHeader = msg->headerByType(X_NOTES_CLASSIFICATION_HEADER)) {
+ const QString &c = classificationHeader->asUnicodeString();
+ if ( c == CLASSIFICATION_PRIVATE ) {
+ classification = Private;
+ } else if ( c == CLASSIFICATION_CONFIDENTIAL ) {
+ classification = Confidential;
+ }
+ }
+
+ const KMime::Content::List list = msg->contents();
+ Q_FOREACH(KMime::Content *c, msg->contents()) {
+ if (KMime::Headers::Base *typeHeader = c->headerByType(X_NOTES_CONTENTTYPE_HEADER)) {
+ const QString &type = typeHeader->asUnicodeString();
+ if ( type == CONTENT_TYPE_CUSTOM ) {
+ parseCustomPart(c);
+ } else if ( type == CONTENT_TYPE_ATTACHMENT ) {
+ parseAttachmentPart(c);
+ } else {
+ qWarning() << "unknown type " << type;
+ }
+ }
+ }
+}
+
+QDomDocument createXMLDocument()
+{
+ QDomDocument document;
+ QString p = "version=\"1.0\" encoding=\"UTF-8\"";
+ document.appendChild(document.createProcessingInstruction( "xml", p ) );
+ return document;
+}
+
+QDomDocument loadDocument(KMime::Content *part)
+{
+ QString errorMsg;
+ int errorLine, errorColumn;
+ QDomDocument document;
+ bool ok = document.setContent( part->body(), &errorMsg, &errorLine, &errorColumn );
+ if ( !ok ) {
+ kWarning() << part->body();
+ qWarning( "Error loading document: %s, line %d, column %d", qPrintable( errorMsg ), errorLine, errorColumn );
+ return QDomDocument();
+ }
+ return document;
+}
+
+KMime::Content* NoteMessageWrapper::NoteMessageWrapperPrivate::createCustomPart() const
+{
+ KMime::Content* content = new KMime::Content();
+ content->appendHeader( new KMime::Headers::Generic( X_NOTES_CONTENTTYPE_HEADER, content, CONTENT_TYPE_CUSTOM, ENCODING ) );
+ QDomDocument document = createXMLDocument();
+ QDomElement element = document.createElement( "custom" );
+ element.setAttribute( "version", "1.0" );
+ for ( QMap <QString, QString >::const_iterator it = custom.begin(); it != custom.end(); ++it ) {
+ QDomElement e = element.ownerDocument().createElement( it.key() );
+ QDomText t = element.ownerDocument().createTextNode( it.value() );
+ e.appendChild( t );
+ element.appendChild( e );
+ document.appendChild( element );
+ }
+ content->setBody( document.toString().toLatin1() );
+ return content;
+}
+
+void NoteMessageWrapper::NoteMessageWrapperPrivate::parseCustomPart( KMime::Content* part )
+{
+ QDomDocument document = loadDocument( part );
+ if (document.isNull()) {
+ return;
+ }
+ QDomElement top = document.documentElement();
+ if ( top.tagName() != "custom" ) {
+ qWarning( "XML error: Top tag was %s instead of the expected custom",
+ top.tagName().toAscii().data() );
+ return;
+ }
+
+ for ( QDomNode n = top.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isElement() ) {
+ QDomElement e = n.toElement();
+ custom.insert(e.tagName(), e.text());
+ } else {
+ kDebug() <<"Node is not an element";
+ Q_ASSERT(false);
+ }
+ }
+}
+
+KMime::Content* NoteMessageWrapper::NoteMessageWrapperPrivate::createAttachmentPart( const Attachment &a ) const
+{
+ KMime::Content* content = new KMime::Content();
+ content->appendHeader( new KMime::Headers::Generic( X_NOTES_CONTENTTYPE_HEADER, content, CONTENT_TYPE_ATTACHMENT, ENCODING ) );
+ if (a.url().isValid()) {
+ content->appendHeader( new KMime::Headers::Generic( X_NOTES_URL_HEADER, content, a.url().toString().toLatin1(), ENCODING ) );
+ } else {
+ content->setBody( a.data() );
+ }
+ content->contentType()->setMimeType( a.mimetype().toLatin1() );
+ if (!a.label().isEmpty()) {
+ content->appendHeader( new KMime::Headers::Generic( X_NOTES_LABEL_HEADER, content, a.label().toLatin1(), ENCODING ) );
+ }
+ content->contentTransferEncoding()->setEncoding( KMime::Headers::CEbase64 );
+ content->contentDisposition()->setDisposition( KMime::Headers::CDattachment );
+ content->contentDisposition()->setFilename( "attachment" );
+ return content;
+}
+
+void NoteMessageWrapper::NoteMessageWrapperPrivate::parseAttachmentPart( KMime::Content *part )
+{
+ QString label;
+ if ( KMime::Headers::Base *labelHeader = part->headerByType( X_NOTES_LABEL_HEADER ) ) {
+ label = labelHeader->asUnicodeString();
+ }
+ if ( KMime::Headers::Base *header = part->headerByType( X_NOTES_URL_HEADER ) ) {
+ Attachment attachment( QUrl( header->asUnicodeString() ), part->contentType()->mimeType() );
+ attachment.setLabel( label );
+ attachments.append(attachment);
+ } else {
+ Attachment attachment( part->decodedContent(), part->contentType()->mimeType() );
+ attachment.setLabel( label );
+ attachments.append(attachment);
+ }
+}
+
+NoteMessageWrapper::NoteMessageWrapper()
+: d_ptr( new NoteMessageWrapperPrivate() )
+{
+}
+
+NoteMessageWrapper::NoteMessageWrapper( const KMime::Message::Ptr &msg )
+: d_ptr( new NoteMessageWrapperPrivate(msg) )
+{
+}
+
+NoteMessageWrapper::~NoteMessageWrapper()
+{
+ delete d_ptr;
+}
+
+KMime::Message::Ptr NoteMessageWrapper::message() const
+{
+ const Q_D( NoteMessageWrapper );
+ KMime::Message::Ptr msg = KMime::Message::Ptr( new KMime::Message() );
+
+ QString title = i18nc( "The default name for new notes.", "New Note" );
+ if ( !d->title.isEmpty() ) {
+ title = d->title;
+ }
+ // Need a non-empty body part so that the serializer regards this as a valid message.
+ QString text = QLatin1String(" ");
+ if ( !d->text.isEmpty() ) {
+ text = d->text;
+ }
+
+ KDateTime creationDate = KDateTime::currentLocalDateTime();
+ if ( d->creationDate.isValid() ) {
+ creationDate = d->creationDate;
+ }
+
+ KDateTime lastModifiedDate = KDateTime::currentLocalDateTime();
+ if ( d->lastModifiedDate.isValid() ) {
+ lastModifiedDate = d->lastModifiedDate;
+ }
+
+ QString uid;
+ if ( !d->uid.isEmpty() ) {
+ uid = d->uid;
+ } else {
+ uid = QUuid::createUuid();
+ }
+
+ msg->subject( true )->fromUnicodeString( title, ENCODING );
+ msg->contentType( true )->setMimeType( d->textFormat == Qt::RichText ? "text/html" : "text/plain" );
+ msg->date( true )->setDateTime( creationDate );
+ msg->from( true )->fromUnicodeString( d->from, ENCODING );
+ msg->mainBodyPart()->fromUnicodeString( text );
+ msg->appendHeader( new KMime::Headers::Generic(X_NOTES_LASTMODIFIED_HEADER, msg.get(), lastModifiedDate.toString( KDateTime::RFCDateDay ).toLatin1(), ENCODING ) );
+ msg->appendHeader( new KMime::Headers::Generic( X_NOTES_UID_HEADER, msg.get(), uid, ENCODING ) );
+
+ QString classification = QString::fromLatin1(CLASSIFICATION_PUBLIC);
+ switch ( d->classification ) {
+ case Private:
+ classification = QString::fromLatin1(CLASSIFICATION_PRIVATE);
+ break;
+ case Confidential:
+ classification = QString::fromLatin1(CLASSIFICATION_CONFIDENTIAL);
+ break;
+ default:
+ //do nothing
+ break;
+ }
+ msg->appendHeader( new KMime::Headers::Generic( X_NOTES_CLASSIFICATION_HEADER, msg.get(), classification, ENCODING ) );
+
+ foreach (const Attachment &a, d->attachments) {
+ msg->addContent( d->createAttachmentPart(a) );
+ }
+
+ if ( !d->custom.isEmpty() ) {
+ msg->addContent( d->createCustomPart() );
+ }
+
+ msg->assemble();
+ return msg;
+}
+
+void NoteMessageWrapper::setUid( const QString &uid )
+{
+ Q_D( NoteMessageWrapper );
+ d->uid = uid;
+}
+
+QString NoteMessageWrapper::uid() const
+{
+ const Q_D( NoteMessageWrapper );
+ return d->uid;
+}
+
+void NoteMessageWrapper::setClassification( NoteMessageWrapper::Classification classification )
+{
+ Q_D( NoteMessageWrapper );
+ d->classification = classification;
+}
+
+NoteMessageWrapper::Classification NoteMessageWrapper::classification() const
+{
+ const Q_D( NoteMessageWrapper );
+ return d->classification;
+}
+
+void NoteMessageWrapper::setLastModifiedDate( const KDateTime& lastModifiedDate )
+{
+ Q_D( NoteMessageWrapper );
+ d->lastModifiedDate = lastModifiedDate;
+}
+
+KDateTime NoteMessageWrapper::lastModifiedDate() const
+{
+ const Q_D( NoteMessageWrapper );
+ return d->lastModifiedDate;
+}
+
+void NoteMessageWrapper::setCreationDate( const KDateTime &creationDate )
+{
+ Q_D( NoteMessageWrapper );
+ d->creationDate = creationDate;
+}
+
+KDateTime NoteMessageWrapper::creationDate() const
+{
+ const Q_D( NoteMessageWrapper );
+ return d->creationDate;
+}
+
+void NoteMessageWrapper::setFrom( const QString &from )
+{
+ Q_D( NoteMessageWrapper );
+ d->from = from;
+}
+
+QString NoteMessageWrapper::from() const
+{
+ const Q_D( NoteMessageWrapper );
+ return d->from;
+}
+
+void NoteMessageWrapper::setTitle( const QString &title )
+{
+ Q_D( NoteMessageWrapper );
+ d->title = title;
+}
+
+QString NoteMessageWrapper::title() const
+{
+ const Q_D( NoteMessageWrapper );
+ return d->title;
+}
+
+void NoteMessageWrapper::setText( const QString &text, Qt::TextFormat format )
+{
+ Q_D( NoteMessageWrapper );
+ d->text = text;
+ d->textFormat = format;
+}
+
+QString NoteMessageWrapper::text() const
+{
+ const Q_D( NoteMessageWrapper );
+ return d->text;
+}
+
+Qt::TextFormat NoteMessageWrapper::textFormat() const
+{
+ const Q_D( NoteMessageWrapper );
+ return d->textFormat;
+}
+
+QString NoteMessageWrapper::toPlainText() const
+{
+ const Q_D( NoteMessageWrapper );
+ if ( d->textFormat == Qt::PlainText ) {
+ return d->text;
+ }
+
+ //From cleanHtml in kdepimlibs/kcalutils/incidenceformatter.cpp
+ QRegExp rx( QLatin1String("<body[^>]*>(.*)</body>"), Qt::CaseInsensitive );
+ rx.indexIn( d->text );
+ QString body = rx.cap( 1 );
+
+ return Qt::escape( body.remove( QRegExp( QLatin1String("<[^>]*>") ) ).trimmed() );
+}
+
+QList<Attachment> &NoteMessageWrapper::attachments()
+{
+ Q_D( NoteMessageWrapper );
+ return d->attachments;
+}
+
+QMap< QString, QString > &NoteMessageWrapper::custom()
+{
+ Q_D( NoteMessageWrapper );
+ return d->custom;
+}
+
+
+
+QString noteIconName()
+{
+ return QString::fromLatin1( "text-plain" );
+}
+
+QString noteMimeType()
+{
+ return QString::fromLatin1( "text/x-vnd.akonadi.note" );
+}
+
+} //End Namepsace
+} //End Namepsace
diff --git a/notes/noteutils.h b/notes/noteutils.h
new file mode 100644
index 0000000..518e84e
--- /dev/null
+++ b/notes/noteutils.h
@@ -0,0 +1,272 @@
+/* This file is part of the KDE project
+ Copyright (C) 2011 Christian Mollekopf <chrigi_1@fastmail.fm>
+
+ 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 NOTEUTILS_H
+#define NOTEUTILS_H
+
+#include "akonadi-notes_export.h"
+
+#include <QtCore/QUrl>
+#include <QtGui/QTextEdit>
+
+class KDateTime;
+class QString;
+
+namespace boost {
+ template <typename T> class shared_ptr;
+}
+
+namespace KMime {
+ class Message;
+ typedef boost::shared_ptr<Message> MessagePtr;
+}
+namespace Akonadi {
+namespace NoteUtils {
+
+/**
+* @return mimetype for notes
+* @since 4.8
+*/
+AKONADI_NOTES_EXPORT QString noteMimeType();
+
+/**
+* @return icon for notes
+* @since 4.8
+*/
+AKONADI_NOTES_EXPORT QString noteIconName();
+
+/**
+* An attachment for a note
+* @since 4.9
+*/
+class AKONADI_NOTES_EXPORT Attachment
+{
+public:
+ /**
+ * Create an attachment referencing a url only
+ */
+ Attachment( const QUrl &url, const QString &mimetype );
+ /**
+ * Create an attachment with the content stored inline
+ */
+ Attachment( const QByteArray &data, const QString &mimetype );
+ Attachment( const Attachment & );
+ ~Attachment();
+
+ bool operator==( const Attachment & ) const;
+ void operator=( const Attachment & );
+
+ /**
+ * Returns the url for url-only attachments
+ */
+ QUrl url() const;
+
+ /**
+ * Returns the date for inline attachments
+ */
+ QByteArray data() const;
+
+ /**
+ * Returns the mimetype
+ */
+ QString mimetype() const;
+
+ /**
+ * Sets the label to be presented to the user
+ */
+ void setLabel( const QString &label );
+
+ /**
+ * Returns the label of the attachment
+ */
+ QString label() const;
+private:
+ //@cond PRIVATE
+ class AttachmentPrivate;
+ AttachmentPrivate * const d_ptr;
+ Q_DECLARE_PRIVATE( Attachment )
+ //@endcond
+};
+
+/**
+* A convenience wrapper around KMime::Message::Ptr for notes
+*
+* This is the format used by the Akonotes Resource
+*
+* A note has the following properties:
+* uid: globally unique identifier (generated if empty)
+* creationDate: timestamp when the note was created (generated if empty)
+* lastModified: lastModified (generated if empty)
+* classification: one of private, confidential, public. This is only meant as an indication to the user.
+* title: title of the note
+* text: textual content
+* from: author (generated if empty)
+* attachments: inline or url only
+* custom: key value pair for custom values
+*
+* Reading a note from an Akonotes akonadi item:
+* @code
+* if ( item.hasPayload<KMime::Message::Ptr>() ) {
+* NoteUtils::NoteMessageWrapper note(item.payload<KMime::Message::Ptr>());
+* kDebug() << note.text();
+* textIsRich = messageWrapper.textFormat() == Qt::RichText;
+* }
+* @endcode
+*
+* Setting the note as payload of an akonadi Item
+* @code
+* item.setMimeType(NoteUtils::noteMimeType());
+* NoteUtils::NoteMessageWrapper note;
+* note.setTitle( "title" );
+* note.setText( "text" );
+* note.setFrom( QString::fromLatin1( "MyApplication@kde4" ) );
+* item.setPayload( note.message() );
+* @endcode
+*
+* @author Christian Mollekopf <chrigi_1@fastmail.fm>
+* @since 4.8
+*/
+class AKONADI_NOTES_EXPORT NoteMessageWrapper
+{
+public:
+ NoteMessageWrapper();
+ explicit NoteMessageWrapper( const KMime::MessagePtr & );
+ ~NoteMessageWrapper();
+
+ /**
+ * Set the uid of the note
+ * @param uid should be globally unique
+ */
+ void setUid( const QString &uid );
+
+ /**
+ * Returns the uid of the note
+ */
+ QString uid() const;
+
+ enum Classification {
+ Public,
+ Private,
+ Confidential
+ };
+
+ /**
+ * Set the classification of the note
+ */
+ void setClassification( Classification );
+
+ /**
+ * Returns the classification of the note
+ */
+ Classification classification() const;
+
+ /**
+ * Set the title of the note
+ */
+ void setTitle( const QString &title );
+
+ /**
+ * Returns the title of the note
+ */
+ QString title() const;
+
+ /**
+ * Set the text of the note
+ *
+ * @param format only Qt::PlainText and Qt::RichText is supported
+ */
+ void setText( const QString &text, Qt::TextFormat format = Qt::PlainText );
+
+ /**
+ * Returns the text of the note
+ */
+ QString text() const;
+
+ /**
+ * @return Qt::PlainText or Qt::RichText
+ */
+ Qt::TextFormat textFormat() const;
+
+ /**
+ * @return plaintext version of the text (if richtext)
+ */
+ QString toPlainText() const;
+
+ /**
+ * Set the creation date of the note (stored in the mime header)
+ */
+ void setCreationDate( const KDateTime &creationDate );
+
+ /**
+ * Returns the creation date of the note
+ */
+ KDateTime creationDate() const;
+
+ /**
+ * Set the lastModified-date of the note
+ */
+ void setLastModifiedDate( const KDateTime &lastModifiedDate );
+
+ /**
+ * Returns the lastModified-date of the note
+ */
+ KDateTime lastModifiedDate() const;
+
+ /**
+ * Set the origin (creator) of the note (stored in the mime header)
+ * This is usually the application creating the note.
+ * @param from must be an address in the style of foo@kde.org.
+ */
+ void setFrom( const QString &from );
+
+ /**
+ * Returns the origin (creator) of the note
+ */
+ QString from() const;
+
+ /**
+ * Returns a reference to the list of attachments of the note
+ */
+ QList<Attachment> &attachments();
+
+ /**
+ * Returns a reference to the custom-value map
+ * @return key-value map containing all custom values
+ */
+ QMap<QString, QString> &custom();
+
+ /**
+ * Assemble a KMime message with the given values
+ *
+ * The message can then i.e. be stored inside an akonadi item
+ */
+ KMime::MessagePtr message() const;
+
+private:
+ //@cond PRIVATE
+ class NoteMessageWrapperPrivate;
+ NoteMessageWrapperPrivate * const d_ptr;
+ Q_DECLARE_PRIVATE( NoteMessageWrapper )
+ //@endcond
+};
+
+}
+}
+
+#endif
diff --git a/notes/tests/CMakeLists.txt b/notes/tests/CMakeLists.txt
new file mode 100644
index 0000000..d507a04
--- /dev/null
+++ b/notes/tests/CMakeLists.txt
@@ -0,0 +1,6 @@
+set(notestest_SRCS notestest.cpp)
+
+set(CMAKE_PREFIX_PATH ../)
+
+kde4_add_unit_test(notestest TESTNAME notestest ${notestest_SRCS})
+target_link_libraries(notestest ${KDE4_KDECORE_LIBS} akonadi-notes kmime ${QT_QTTEST_LIBRARY}) \ No newline at end of file
diff --git a/notes/tests/notestest.cpp b/notes/tests/notestest.cpp
new file mode 100644
index 0000000..fcf5a0f
--- /dev/null
+++ b/notes/tests/notestest.cpp
@@ -0,0 +1,85 @@
+/*
+ Copyright (c) 2012 Christian Mollekopf <mollekopf@kolabsys.com>
+
+ 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 "noteutils.h"
+
+#include <QTest>
+#include <QHash>
+#include <QDebug>
+
+#include <KDateTime>
+#include <kmime/kmime_message.h>
+
+using namespace Akonadi::NoteUtils;
+class NotesTest : public QObject
+{
+ Q_OBJECT
+ private slots:
+
+ void testSerializeAndParse()
+ {
+ NoteMessageWrapper note;
+ note.setTitle("title");
+ note.setText("title");
+ note.setUid("uid");
+ note.setClassification(NoteMessageWrapper::Private);
+ note.setFrom("from@kde.org");
+ note.setCreationDate(KDateTime(QDate(2012,3,3), QTime(3,3,3), KDateTime::UTC));
+ note.setLastModifiedDate(KDateTime(QDate(2012,3,3), QTime(4,4,4), KDateTime::UTC));
+ Attachment a("testfile2", "mimetype/mime3");
+ a.setLabel("label");
+ note.attachments() << Attachment(QUrl("file://url/to/file"), "mimetype/mime") << Attachment("testfile", "mimetype/mime2") << a;
+ note.custom().insert("key1", "value1");
+ note.custom().insert("key2", "value2");
+ note.custom().insert("key3", "value3");
+
+ KMime::MessagePtr msg = note.message();
+// qWarning() << msg->encodedContent();
+
+ NoteMessageWrapper result(msg);
+
+ QCOMPARE(result.title(), note.title());
+ QCOMPARE(result.text(), note.text());
+ QCOMPARE(result.textFormat(), note.textFormat());
+ QCOMPARE(result.uid(), note.uid());
+ QCOMPARE(result.classification(), note.classification());
+ QCOMPARE(result.from(), note.from());
+ QCOMPARE(result.creationDate(), note.creationDate());
+ QCOMPARE(result.lastModifiedDate(), note.lastModifiedDate());
+ QCOMPARE(result.custom(), note.custom());
+ QCOMPARE(result.attachments(), note.attachments());
+
+// qWarning() << result.message()->encodedContent();
+ }
+
+ void createIfEmpty()
+ {
+ NoteMessageWrapper note;
+ NoteMessageWrapper result(note.message());
+// qDebug() << result.uid();
+ QVERIFY(!result.uid().isEmpty());
+ QVERIFY(result.creationDate().isValid());
+ QVERIFY(result.lastModifiedDate().isValid());
+ }
+
+};
+
+QTEST_MAIN( NotesTest )
+
+#include "notestest.moc"