Commit feaa3ddf authored by Daniel Vrátil's avatar Daniel Vrátil 🤖

Add support for CONFERENCE property

CONFERENCE property is specified in RFC7986, section 5.11 and is designed
to specify information about accessing a conferencing system.
parent 1b5dc8b4
......@@ -19,6 +19,7 @@ macro_unit_tests(
testattachment
testattendee
testcalfilter
testconference
testcustomproperties
testdateserialization
testduration
......@@ -57,6 +58,7 @@ macro_unit_tests(
set_target_properties(testmemorycalendar PROPERTIES COMPILE_FLAGS -DICALTESTDATADIR="\\"${CMAKE_CURRENT_SOURCE_DIR}/data/\\"")
set_target_properties(testreadrecurrenceid PROPERTIES COMPILE_FLAGS -DICALTESTDATADIR="\\"${CMAKE_CURRENT_SOURCE_DIR}/data/\\"")
set_target_properties(testconference PROPERTIES COMPILE_FLAGS -DICALTESTDATADIR="\\"${CMAKE_CURRENT_SOURCE_DIR}/data/\\"")
# this test cannot work with msvc because libical should not be altered
# and therefore we can't add KCALENDARCORE_EXPORT there
# it should work fine with mingw because of the auto-import feature
......
BEGIN:VCALENDAR
PRODID:-//K Desktop Environment//NONSGML libkcal 4.3//EN
VERSION:2.0
X-KDE-ICAL-IMPLEMENTATION-VERSION:1.0
BEGIN:VTIMEZONE
TZID:Europe/Berlin
BEGIN:DAYLIGHT
TZNAME:CEST
TZOFFSETFROM:+0000
TZOFFSETTO:+0200
DTSTART:19800406T010000
RDATE:19800406T010000
END:DAYLIGHT
BEGIN:STANDARD
TZNAME:CET
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
DTSTART:19971026T030000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
END:STANDARD
BEGIN:STANDARD
TZNAME:CET
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
DTSTART:19800928T030000
RRULE:FREQ=YEARLY;UNTIL=19961027T030000;BYDAY=-1SU;BYMONTH=9
RDATE:19950924T030000
END:STANDARD
BEGIN:DAYLIGHT
TZNAME:CEST
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
DTSTART:19810329T020000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VTIMEZONE
TZID:Europe/Prague
BEGIN:DAYLIGHT
TZNAME:CEST
TZOFFSETFROM:+0000
TZOFFSETTO:+0200
DTSTART:19790401T010000
RDATE:19790401T010000
END:DAYLIGHT
BEGIN:STANDARD
TZNAME:CET
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
DTSTART:19971026T030000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
END:STANDARD
BEGIN:STANDARD
TZNAME:CET
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
DTSTART:19790930T030000
RRULE:FREQ=YEARLY;UNTIL=19961027T030000;BYDAY=-1SU;BYMONTH=9
RDATE:19950924T030000
END:STANDARD
BEGIN:DAYLIGHT
TZNAME:CEST
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
DTSTART:19810329T020000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
END:DAYLIGHT
BEGIN:DAYLIGHT
TZNAME:CEST
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
DTSTART:19800406T020000
RDATE:19800406T020000
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
ORGANIZER;CN=Daniel Vrátil:MAILTO:dvratil@kde.example
DTSTAMP:20201116T134859Z
ATTENDEE;RSVP=FALSE;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;
CUTYPE=INDIVIDUAL;X-UID=189296704:mailto:konqi@kde.example
ATTENDEE;RSVP=FALSE;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;
CUTYPE=INDIVIDUAL;X-UID=192611760:mailto:kate@kde.example
CREATED:20201116T134859Z
UID:bb320b0a-6ee3-4a91-8304-4b8d96bb141f
LAST-MODIFIED:20201116T134859Z
SUMMARY:Conference call with colleagues
RRULE:FREQ=WEEKLY;BYDAY=MO
DTSTART;TZID=Europe/Prague:20201116T150000
DTEND;TZID=Europe/Prague:20201116T160000
TRANSP:OPAQUE
CONFERENCE;VALUE=URI;FEATURE=AUDIO,VIDEO;
LABEL=Join NextCloud Talk, password is 12345:https://corp.kde.example/call/efi83r28
END:VEVENT
END:VCALENDAR
/*
This file is part of the kcalcore library.
SPDX-FileCopyrightText: 2020 Daniel Vrátil <dvratil@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "testconference.h"
#include "conference.h"
#include "memorycalendar.h"
#include "filestorage.h"
#include <QDataStream>
#include <QTest>
QTEST_MAIN(ConferenceTest)
using namespace KCalendarCore;
void ConferenceTest::testValidity()
{
{
Conference test;
QVERIFY(test.isNull());
}
{
Conference test(QUrl(QStringLiteral("tel:000326870")),
QStringLiteral("Phone call for conference"));
QVERIFY(!test.isNull());
}
}
void ConferenceTest::testCompare()
{
Conference conf1{QUrl{QStringLiteral("tel:123456789")},
QStringLiteral("Conference call"),
{QStringLiteral("PHONE")},
QStringLiteral("en")};
Conference conf2{QUrl{QStringLiteral("xmpp:conference@conference.conference")},
QStringLiteral("Conference chat"),
{QStringLiteral("CHAT")},
QStringLiteral("en")};
QVERIFY(conf1 != conf2);
conf2.setUri(QUrl{QStringLiteral("tel:123456789")});
conf2.setLabel(QStringLiteral("Conference call"));
conf2.setFeatures({QStringLiteral("PHONE")});
conf2.setLanguage(QStringLiteral("en"));
QVERIFY(conf1 == conf2);
}
void ConferenceTest::testAssign()
{
Conference conf1{QUrl{QStringLiteral("sip:1234-5678@sip.example")}, QStringLiteral("SIP Call")};
Conference conf2 = conf1;
QCOMPARE(conf1, conf2);
conf2.setLanguage(QStringLiteral("en"));
QVERIFY(!(conf1 == conf2));
Conference conf3(conf1);
QCOMPARE(conf3, conf1);
}
void ConferenceTest::testDataStream()
{
Conference conf1;
conf1.setUri(QUrl{QStringLiteral("tel:000326870")});
conf1.setLabel(QStringLiteral("Phone conference"));
conf1.addFeature(QStringLiteral("PHONE"));
conf1.setLanguage(QStringLiteral("en"));
QByteArray byteArray;
QDataStream out_stream(&byteArray, QIODevice::WriteOnly);
out_stream << conf1;
QDataStream in_stream(&byteArray, QIODevice::ReadOnly);
Conference conf2;
in_stream >> conf2;
QCOMPARE(conf2.uri(), conf1.uri());
QCOMPARE(conf2.label(), conf1.label());
QCOMPARE(conf2.features(), conf1.features());
QCOMPARE(conf2.language(), conf1.language());
}
void ConferenceTest::testLoading()
{
MemoryCalendar::Ptr cal(new MemoryCalendar(QTimeZone::utc()));
FileStorage store(cal, QLatin1String(ICALTESTDATADIR) + QLatin1String("test_conference.ics"));
QVERIFY(store.load());
const auto events = cal->events();
QCOMPARE(events.size(), 1);
const auto event = events.at(0);
const auto conferences = event->conferences();
QCOMPARE(conferences.size(), 1);
const auto conference = conferences.at(0);
QCOMPARE(conference.uri(), QUrl{QStringLiteral("https://corp.kde.example/call/efi83r28")});
QCOMPARE(conference.features(), (QStringList{QStringLiteral("AUDIO"), QStringLiteral("VIDEO")}));
QCOMPARE(conference.label(), QStringLiteral("Join NextCloud Talk, password is 12345"));
}
/*
This file is part of the kcalcore library.
SPDX-FileCopyrightText: 2020 Daniel Vrátil <dvratil@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef TESTCONFERENCE_H
#define TESTCONFERENCE_H
#include <QObject>
class ConferenceTest: public QObject
{
Q_OBJECT
private Q_SLOTS:
void testValidity();
void testCompare();
void testAssign();
void testDataStream();
void testLoading();
};
#endif
......@@ -10,6 +10,7 @@ set(kcalcore_LIB_SRCS
calformat.cpp
calstorage.cpp
compat.cpp
conference.cpp
customproperties.cpp
duration.cpp
event.cpp
......@@ -92,6 +93,7 @@ set(kcalendarcore_headers
CalFormat
CalStorage
Calendar
Conference
CustomProperties
Duration
Event
......
/*
This file is part of the kcalcore library.
SPDX-FileCopyrightText: 2020 Daniel Vrátil <dvratil@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "conference.h"
#include <QDataStream>
#include <QDebug>
using namespace KCalendarCore;
/**
Private class that helps to provide binary compatibility between releases.
@internal
*/
//@cond PRIVATE
class Q_DECL_HIDDEN KCalendarCore::Conference::Private : public QSharedData
{
public:
QString label;
QString language;
QStringList features;
QUrl uri;
CustomProperties customProperties;
};
//@endcond
Conference::Conference()
: d(new Conference::Private)
{
}
Conference::Conference(const QUrl &uri, const QString &label, const QStringList &features, const QString &language)
: d(new Conference::Private)
{
setUri(uri);
setLabel(label);
setFeatures(features);
setLanguage(language);
}
Conference::Conference(const Conference &) = default;
Conference::~Conference() = default;
bool Conference::isNull() const
{
// isNull rather than isEmpty, as user code is actually creating empty but non-null conferences...
return !d->uri.isValid() && d->label.isNull();
}
bool KCalendarCore::Conference::operator==(const Conference &other) const
{
return std::tie(d->label, d->language, d->features, d->uri)
== std::tie(other.d->label, other.d->language, other.d->features, other.d->uri);
}
bool KCalendarCore::Conference::operator!=(const Conference &other) const
{
return !operator==(other);
}
Conference &KCalendarCore::Conference::operator=(const Conference &) = default;
QUrl Conference::uri() const
{
return d->uri;
}
void Conference::setUri(const QUrl &uri)
{
d->uri = uri;
}
QString Conference::label() const
{
return d->label;
}
void Conference::setLabel(const QString &label)
{
d->label = label;
}
QStringList Conference::features() const
{
return d->features;
}
void Conference::addFeature(const QString &feature)
{
d->features.push_back(feature);
}
void Conference::removeFeature(const QString &feature)
{
d->features.removeAll(feature);
}
void Conference::setFeatures(const QStringList &features)
{
d->features = features;
}
QString Conference::language() const
{
return d->language;
}
void Conference::setLanguage(const QString &language)
{
d->language = language;
}
void Conference::setCustomProperty(const QByteArray &xname, const QString &xvalue)
{
d->customProperties.setNonKDECustomProperty(xname, xvalue);
}
CustomProperties &Conference::customProperties()
{
return d->customProperties;
}
const CustomProperties &Conference::customProperties() const
{
return d->customProperties;
}
QDataStream &KCalendarCore::operator<<(QDataStream &stream, const KCalendarCore::Conference &conference)
{
return stream << conference.d->uri
<< conference.d->label
<< conference.d->features
<< conference.d->language
<< conference.d->customProperties;
}
QDataStream &KCalendarCore::operator>>(QDataStream &stream, KCalendarCore::Conference &conference)
{
Conference conf;
stream >> conf.d->uri
>> conf.d->label
>> conf.d->features
>> conf.d->language
>> conf.d->customProperties;
conference = conf;
return stream;
}
/*
This file is part of the kcalcore library.
SPDX-FileCopyrightText: 2020 Daniel Vrátil <dvratil@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KCALCORE_CONFERENCE_H
#define KCALCORE_CONFERENCE_H
#include <QMetaType>
#include <QSharedDataPointer>
#include <QUrl>
#include "kcalendarcore_export.h"
#include "customproperties.h"
namespace KCalendarCore
{
/**
@brief
Represents information related to a conference information of an Calendar
Incidence, typically a meeting or task (to-do).
Conference contains information needed to join a remote conference system
(e.g. phone call, audio/video meeting etc.)
@since 5.77
*/
class KCALENDARCORE_EXPORT Conference
{
Q_GADGET
Q_PROPERTY(bool isNull READ isNull)
Q_PROPERTY(QStringList features READ features WRITE setFeatures)
Q_PROPERTY(QString label READ label WRITE setLabel)
Q_PROPERTY(QUrl uri READ uri WRITE setUri)
Q_PROPERTY(QString language READ language WRITE setLanguage)
public:
using List = QVector<Conference>;
/** Create a null Conference. */
explicit Conference();
/**
Constructs a conference consisting of a @p uri, description of
the URI (@p label), list of features of the conference (@p features)
and @p langauge.
@param uri Uri to join the conference.
@param label Label of the URI.
@param features Features of this particular conference method.
@param language Language of the information present in other fields.
*/
Conference(const QUrl &uri, const QString &label,
const QStringList &features = {},
const QString &language = {});
/**
Constructs a conference by copying another conference.
@param conference is the conference to be copied.
*/
Conference(const Conference &conference);
/**
Destroys the conference.
*/
~Conference();
/**
Compares this with @p conference for equality.
@param conference the conference to compare.
*/
bool operator==(const Conference &conference) const;
/**
Compares this with @p conference for inequality.
@param conference the conference to compare.
*/
bool operator!=(const Conference &other) const;
/**
* Returns @c true if this is a default-constructed Conference instance.
*/
Q_REQUIRED_RESULT bool isNull() const;
/**
* Returns URI to join the conference, with access code included.
*/
Q_REQUIRED_RESULT QUrl uri() const;
/**
* Sets the URI to @uri.
*/
void setUri(const QUrl &uri);
/**
* Returns label with additional details regarding further use of the URI.
*/
Q_REQUIRED_RESULT QString label() const;
/**
* Sets the URI label to @p label.
*/
void setLabel(const QString &label);
/**
* Returns the list of features of the conferencing system at given URI.
*
* This can be e.g. CHAT, AUDIO, VIDEO, PHONE, etc.
*/
Q_REQUIRED_RESULT QStringList features() const;
/**
* Adds @p feature to the list of features.
*
* @param feature Feature to add.
*/
void addFeature(const QString &feature);
/**
* Removes @p feature from the list of features.
*
* @param feature Feature to remove.
*/
void removeFeature(const QString &feature);
/**
* Sets the list of features to @p features.
*/
void setFeatures(const QStringList &features);
/**
* Returns the language of the text present in other properties of this object.
*/
Q_REQUIRED_RESULT QString language() const;
/**
* Sets the language to @p language.
*/
void setLanguage(const QString &language);
/**
Sets this conference equal to @p conference.
@param conference is the conference to copy.
*/
Conference &operator=(const Conference &conference);
/**
Adds a custom property. If the property already exists it will be overwritten.
@param xname is the name of the property.
@param xvalue is its value.
*/
void setCustomProperty(const QByteArray &xname, const QString &xvalue);
/**
Returns a reference to the CustomProperties object
*/
Q_REQUIRED_RESULT CustomProperties &customProperties();
/**
Returns a const reference to the CustomProperties object
*/
const CustomProperties &customProperties() const;
private:
//@cond PRIVATE
class Private;
QSharedDataPointer<Private> d;
//@endcond
friend KCALENDARCORE_EXPORT QDataStream &operator<<(QDataStream &, const KCalendarCore::Conference &);
friend KCALENDARCORE_EXPORT QDataStream &operator>>(QDataStream &, KCalendarCore::Conference &);
};
/**
Serializes a Conference object into a data stream.
@param stream is a QDataStream.
@param conference is a reference to a Conference object to be serialized.
*/
KCALENDARCORE_EXPORT QDataStream &operator<<(QDataStream &stream, const KCalendarCore::Conference &conference);
/**
Initializes a Conference object from a data stream.
@param stream is a QDataStream.
@param conference is a reference to a Conference object to be initialized.
*/
KCALENDARCORE_EXPORT QDataStream &operator>>(QDataStream &stream, KCalendarCore::Conference &conference);
}
//@cond PRIVATE
Q_DECLARE_TYPEINFO(KCalendarCore::Conference, Q_MOVABLE_TYPE);
Q_DECLARE_METATYPE(KCalendarCore::Conference)
//@endcond
#endif
......@@ -620,6 +620,14 @@ void ICalFormatImpl::writeIncidence(icalcomponent *parent,
icalcomponent_add_component(parent, writeAlarm(*it));
}
#if defined(USE_ICAL_3)
// conferences
const auto conferences = incidence->conferences();
for (const auto &conf : conferences) {
icalcomponent_add_property(parent, writeConference(conf));
}
#endif
// duration
if (incidence->hasDuration()) {
icaldurationtype duration;
......@@ -1156,6 +1164,18 @@ icalcomponent *ICalFormatImpl::writeAlarm(const Alarm::Ptr &alarm