2
Copyright (c) 2009, 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
3
Copyright (C) 2009 KDAB (author: Frank Osterfeld <osterfeld@kde.org>)
4
Copyright (c) 2010 Andras Mantia <andras@kdab.com>
6
This program is free software; you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation; either version 2 of the License, or
9
(at your option) any later version.
11
This program is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
GNU General Public License for more details.
16
You should have received a copy of the GNU General Public License along
17
with this program; if not, write to the Free Software Foundation, Inc.,
18
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20
As a special exception, permission is given to link this program
21
with any edition of Qt, and distribute the resulting executable,
22
without including the source code for Qt in the source distribution.
26
/*#include "kcalprefs.h"
27
#include "mailclient.h"
28
#include "mailscheduler.h"
29
#include "publishdialog.h"*/
31
#include <Akonadi/Collection>
32
#include <Akonadi/CollectionDialog>
33
#include <Akonadi/EntityDisplayAttribute>
34
#include <Akonadi/EntityTreeModel>
35
#include <Akonadi/Item>
37
#include <KHolidays/Holidays>
39
#include <KCalCore/CalFilter>
40
#include <KCalCore/Event>
41
#include <KCalCore/FreeBusy>
42
#include <KCalCore/Incidence>
43
#include <KCalCore/Journal>
44
#include <KCalCore/MemoryCalendar>
45
#include <KCalCore/Todo>
46
#include <KCalCore/ICalFormat>
48
#include <KCalUtils/DndFactory>
49
#include <KCalUtils/ICalDrag>
50
#include <KCalUtils/VCalDrag>
52
#include <Mailtransport/TransportManager>
54
#include <KIconLoader>
58
#include <QAbstractItemModel>
61
#include <QModelIndex>
65
#include <boost/bind.hpp>
66
#include <KMessageBox>
67
#include <KPIMIdentities/IdentityManager>
68
#include <KFileDialog>
69
#include <KIO/NetAccess>
71
using namespace CalendarSupport;
72
using namespace KHolidays;
74
KCalCore::Incidence::Ptr CalendarSupport::incidence( const Akonadi::Item &item )
77
item.hasPayload<KCalCore::Incidence::Ptr>() ?
78
item.payload<KCalCore::Incidence::Ptr>() :
79
KCalCore::Incidence::Ptr();
82
KCalCore::Event::Ptr CalendarSupport::event( const Akonadi::Item &item )
85
item.hasPayload<KCalCore::Event::Ptr>() ?
86
item.payload<KCalCore::Event::Ptr>() :
87
KCalCore::Event::Ptr();
90
KCalCore::Event::List CalendarSupport::eventsFromItems( const Akonadi::Item::List &items )
92
KCalCore::Event::List events;
93
Q_FOREACH ( const Akonadi::Item &item, items ) {
94
if ( const KCalCore::Event::Ptr e = CalendarSupport::event( item ) ) {
95
events.push_back( e );
101
KCalCore::Incidence::List CalendarSupport::incidencesFromItems( const Akonadi::Item::List &items )
103
KCalCore::Incidence::List incidences;
104
Q_FOREACH ( const Akonadi::Item &item, items ) {
105
if ( const KCalCore::Incidence::Ptr e = CalendarSupport::incidence( item ) ) {
106
incidences.push_back( e );
112
KCalCore::Todo::Ptr CalendarSupport::todo( const Akonadi::Item &item )
115
item.hasPayload<KCalCore::Todo::Ptr>() ?
116
item.payload<KCalCore::Todo::Ptr>() :
117
KCalCore::Todo::Ptr();
120
KCalCore::Journal::Ptr CalendarSupport::journal( const Akonadi::Item &item )
123
item.hasPayload<KCalCore::Journal::Ptr>() ?
124
item.payload<KCalCore::Journal::Ptr>() :
125
KCalCore::Journal::Ptr();
128
bool CalendarSupport::hasIncidence( const Akonadi::Item &item )
130
return item.hasPayload<KCalCore::Incidence::Ptr>();
133
bool CalendarSupport::hasEvent( const Akonadi::Item &item )
135
return item.hasPayload<KCalCore::Event::Ptr>();
138
bool CalendarSupport::hasTodo( const Akonadi::Item &item )
140
return item.hasPayload<KCalCore::Todo::Ptr>();
143
bool CalendarSupport::hasJournal( const Akonadi::Item &item )
145
return item.hasPayload<KCalCore::Journal::Ptr>();
148
QMimeData *CalendarSupport::createMimeData( const Akonadi::Item::List &items,
149
const KDateTime::Spec &timeSpec )
151
if ( items.isEmpty() ) {
155
KCalCore::MemoryCalendar::Ptr cal( new KCalCore::MemoryCalendar( timeSpec ) );
158
int incidencesFound = 0;
159
Q_FOREACH ( const Akonadi::Item &item, items ) {
160
const KCalCore::Incidence::Ptr incidence( CalendarSupport::incidence( item ) );
165
urls.push_back( item.url() );
166
KCalCore::Incidence::Ptr i( incidence->clone() );
167
cal->addIncidence( i );
170
if ( incidencesFound == 0 ) {
174
std::auto_ptr<QMimeData> mimeData( new QMimeData );
176
mimeData->setUrls( urls );
178
KCalUtils::ICalDrag::populateMimeData( mimeData.get(), cal );
179
KCalUtils::VCalDrag::populateMimeData( mimeData.get(), cal );
181
return mimeData.release();
184
QMimeData *CalendarSupport::createMimeData( const Akonadi::Item &item,
185
const KDateTime::Spec &timeSpec )
187
return createMimeData( Akonadi::Item::List() << item, timeSpec );
190
#ifndef QT_NO_DRAGANDDROP
191
QDrag *CalendarSupport::createDrag( const Akonadi::Item &item,
192
const KDateTime::Spec &timeSpec, QWidget *parent )
194
return createDrag( Akonadi::Item::List() << item, timeSpec, parent );
198
static QByteArray findMostCommonType( const Akonadi::Item::List &items )
201
if ( items.isEmpty() ) {
205
Q_FOREACH( const Akonadi::Item &item, items ) {
206
if ( !CalendarSupport::hasIncidence( item ) ) {
209
const QByteArray type = CalendarSupport::incidence( item )->typeStr();
210
if ( !prev.isEmpty() && type != prev ) {
218
#ifndef QT_NO_DRAGANDDROP
219
QDrag *CalendarSupport::createDrag( const Akonadi::Item::List &items,
220
const KDateTime::Spec &timeSpec, QWidget *parent )
222
std::auto_ptr<QDrag> drag( new QDrag( parent ) );
223
drag->setMimeData( CalendarSupport::createMimeData( items, timeSpec ) );
225
const QByteArray common = findMostCommonType( items );
226
if ( common == "Event" ) {
227
drag->setPixmap( BarIcon( QLatin1String( "view-calendar-day" ) ) );
228
} else if ( common == "Todo" ) {
229
drag->setPixmap( BarIcon( QLatin1String( "view-calendar-tasks" ) ) );
232
return drag.release();
236
static bool itemMatches( const Akonadi::Item &item, const KCalCore::CalFilter *filter )
239
KCalCore::Incidence::Ptr inc = CalendarSupport::incidence( item );
243
return filter->filterIncidence( inc );
246
Akonadi::Item::List CalendarSupport::applyCalFilter( const Akonadi::Item::List &items_,
247
const KCalCore::CalFilter *filter )
250
Akonadi::Item::List items( items_ );
251
items.erase( std::remove_if( items.begin(), items.end(),
252
!bind( itemMatches, _1, filter ) ), items.end() );
256
bool CalendarSupport::isValidIncidenceItemUrl( const KUrl &url,
257
const QStringList &supportedMimeTypes )
259
if ( !url.isValid() ) {
263
if ( url.scheme() != QLatin1String( "akonadi" ) ) {
267
return supportedMimeTypes.contains( url.queryItem( QLatin1String( "type" ) ) );
270
bool CalendarSupport::isValidIncidenceItemUrl( const KUrl &url )
272
return isValidIncidenceItemUrl( url,
273
QStringList() << KCalCore::Event::eventMimeType()
274
<< KCalCore::Todo::todoMimeType()
275
<< KCalCore::Journal::journalMimeType()
276
<< KCalCore::FreeBusy::freeBusyMimeType() );
279
static bool containsValidIncidenceItemUrl( const QList<QUrl>& urls )
282
std::find_if( urls.begin(), urls.end(),
283
bind( CalendarSupport::isValidIncidenceItemUrl, _1 ) ) != urls.constEnd();
286
bool CalendarSupport::isValidTodoItemUrl( const KUrl &url )
288
if ( !url.isValid() || url.scheme() != QLatin1String( "akonadi" ) ) {
292
return url.queryItem( QLatin1String( "type" ) ) == KCalCore::Todo::todoMimeType();
295
bool CalendarSupport::canDecode( const QMimeData *md )
299
containsValidIncidenceItemUrl( md->urls() ) ||
300
KCalUtils::ICalDrag::canDecode( md ) ||
301
KCalUtils::VCalDrag::canDecode( md );
304
QList<KUrl> CalendarSupport::incidenceItemUrls( const QMimeData *mimeData )
307
Q_FOREACH( const KUrl &i, mimeData->urls() ) {
308
if ( isValidIncidenceItemUrl( i ) ) {
315
QList<KUrl> CalendarSupport::todoItemUrls( const QMimeData *mimeData )
319
Q_FOREACH( const KUrl &i, mimeData->urls() ) {
320
if ( isValidIncidenceItemUrl( i, QStringList() << KCalCore::Todo::todoMimeType() ) ) {
327
bool CalendarSupport::mimeDataHasTodo( const QMimeData *mimeData )
329
return !todoItemUrls( mimeData ).isEmpty() || !todos( mimeData, KDateTime::Spec() ).isEmpty();
332
KCalCore::Todo::List CalendarSupport::todos( const QMimeData *mimeData,
333
const KDateTime::Spec &spec )
335
KCalCore::Todo::List todos;
337
#ifndef QT_NO_DRAGANDDROP
338
KCalCore::Calendar::Ptr cal( KCalUtils::DndFactory::createDropCalendar( mimeData, spec ) );
340
Q_FOREACH( const KCalCore::Todo::Ptr &i, cal->todos() ) {
341
todos.push_back( KCalCore::Todo::Ptr( i->clone() ) );
349
Akonadi::Collection CalendarSupport::selectCollection( QWidget *parent,
351
const QStringList &mimeTypes,
352
const Akonadi::Collection &defCollection )
354
QPointer<Akonadi::CollectionDialog> dlg( new Akonadi::CollectionDialog( parent ) );
356
kDebug() << "selecting collections with mimeType in " << mimeTypes;
358
dlg->setMimeTypeFilter( mimeTypes );
359
dlg->setAccessRightsFilter( Akonadi::Collection::CanCreateItem );
360
if ( defCollection.isValid() ) {
361
dlg->setDefaultCollection( defCollection );
363
Akonadi::Collection collection;
365
// FIXME: don't use exec.
366
dialogCode = dlg->exec();
367
if ( dialogCode == QDialog::Accepted ) {
368
collection = dlg->selectedCollection();
370
if ( !collection.isValid() ) {
371
kWarning() <<"An invalid collection was selected!";
379
Akonadi::Item CalendarSupport::itemFromIndex( const QModelIndex &idx )
381
Akonadi::Item item = idx.data( Akonadi::EntityTreeModel::ItemRole ).value<Akonadi::Item>();
382
item.setParentCollection(
383
idx.data( Akonadi::EntityTreeModel::ParentCollectionRole ).value<Akonadi::Collection>() );
387
Akonadi::Collection::List CalendarSupport::collectionsFromModel( const QAbstractItemModel *model,
388
const QModelIndex &parentIndex,
391
const int endRow = end >= 0 ? end : model->rowCount( parentIndex ) - 1;
392
Akonadi::Collection::List collections;
394
QModelIndex i = model->index( row, 0, parentIndex );
395
while ( row <= endRow ) {
396
const Akonadi::Collection collection = collectionFromIndex( i );
397
if ( collection.isValid() ) {
398
collections << collection;
399
QModelIndex childIndex = i.child( 0, 0 );
400
if ( childIndex.isValid() ) {
401
collections << collectionsFromModel( model, i );
405
i = i.sibling( row, 0 );
410
Akonadi::Item::List CalendarSupport::itemsFromModel( const QAbstractItemModel * model,
411
const QModelIndex &parentIndex,
414
const int endRow = end >= 0 ? end : model->rowCount( parentIndex ) - 1;
415
Akonadi::Item::List items;
417
QModelIndex i = model->index( row, 0, parentIndex );
418
while ( row <= endRow ) {
419
const Akonadi::Item item = itemFromIndex( i );
420
if ( CalendarSupport::hasIncidence( item ) ) {
423
QModelIndex childIndex = i.child( 0, 0 );
424
if ( childIndex.isValid() ) {
425
items << itemsFromModel( model, i );
430
i = i.sibling( row, 0 );
435
Akonadi::Collection CalendarSupport::collectionFromIndex( const QModelIndex &index )
437
return index.data( Akonadi::EntityTreeModel::CollectionRole ).value<Akonadi::Collection>();
440
Akonadi::Collection::Id CalendarSupport::collectionIdFromIndex( const QModelIndex &index )
442
return index.data( Akonadi::EntityTreeModel::CollectionIdRole ).value<Akonadi::Collection::Id>();
445
Akonadi::Collection::List CalendarSupport::collectionsFromIndexes( const QModelIndexList &indexes )
447
Akonadi::Collection::List l;
448
Q_FOREACH( const QModelIndex &idx, indexes ) {
449
l.push_back( collectionFromIndex( idx ) );
454
QString CalendarSupport::displayName( const Akonadi::Collection &c )
456
const Akonadi::EntityDisplayAttribute *attr = c.attribute<Akonadi::EntityDisplayAttribute>();
457
return ( attr && !attr->displayName().isEmpty() ) ? attr->displayName() : c.name();
460
QString CalendarSupport::subMimeTypeForIncidence( const KCalCore::Incidence::Ptr &incidence )
462
return incidence->mimeType();
465
QList<QDate> CalendarSupport::workDays( const QDate &startDate,
466
const QDate &endDate )
470
/* const int mask( ~( KCalPrefs::instance()->mWorkWeekMask ) );
471
const int numDays = startDate.daysTo( endDate ) + 1;
473
for ( int i = 0; i < numDays; ++i ) {
474
const QDate date = startDate.addDays( i );
475
if ( !( mask & ( 1 << ( date.dayOfWeek() - 1 ) ) ) ) {
476
result.append( date );
480
if ( KCalPrefs::instance()->mExcludeHolidays ) {
481
// NOTE: KOGlobals, where this method comes from, used to hold a pointer to
482
// a KHolidays object. I'm not sure about how expensive it is, just
483
// creating one here.
484
const HolidayRegion holidays( KCalPrefs::instance()->mHolidays );
485
const Holiday::List list = holidays.holidays( startDate, endDate );
486
for ( int i = 0; i < list.count(); ++i ) {
487
const Holiday &h = list.at( i );
488
const QString dateString = h.date().toString();
489
if ( h.dayType() == Holiday::NonWorkday ) {
490
result.removeAll( h.date() );
498
QStringList CalendarSupport::holiday( const QDate &date )
502
/* const HolidayRegion holidays( KCalPrefs::instance()->mHolidays );
503
const Holiday::List list = holidays.holidays( date );
505
for ( int i = 0; i < list.count(); ++i ) {
506
hdays.append( list.at( i ).text() );
511
void CalendarSupport::sendAsICalendar(const Akonadi::Item& item, KPIMIdentities::IdentityManager* identityManager, QWidget* parentWidget)
513
/* Incidence::Ptr incidence = CalendarSupport::incidence( item );
516
KMessageBox::information(
518
i18n( "No item selected." ),
519
i18n( "Forwarding" ),
520
"ForwardNoEventSelected" );
524
QPointer<PublishDialog> publishdlg = new PublishDialog;
525
if ( publishdlg->exec() == QDialog::Accepted ) {
526
const QString recipients = publishdlg->addresses();
527
if ( incidence->organizer()->isEmpty() ) {
528
incidence->setOrganizer( Person::Ptr( new Person( CalendarSupport::KCalPrefs::instance()->fullName(),
529
CalendarSupport::KCalPrefs::instance()->email() ) ) );
533
const QString from = CalendarSupport::KCalPrefs::instance()->email();
534
const bool bccMe = CalendarSupport::KCalPrefs::instance()->mBcc;
535
const QString messageText = format.createScheduleMessage( incidence, iTIPRequest );
536
CalendarSupport::MailClient mailer;
539
identityManager->identityForAddress( from ),
540
from, bccMe, recipients, messageText, MailTransport::TransportManager::self()->defaultTransportName() ) ) {
541
KMessageBox::information(
543
i18n( "The item information was successfully sent." ),
544
i18n( "Forwarding" ),
545
"IncidenceForwardSuccess" );
549
i18n( "Unable to forward the item '%1'", incidence->summary() ),
550
i18n( "Forwarding Error" ) );
556
void CalendarSupport::publishItemInformation(const Akonadi::Item& item, Calendar* calendar, QWidget* parentWidget)
558
/* Incidence::Ptr incidence = CalendarSupport::incidence( item );
561
KMessageBox::information(
563
i18n( "No item selected." ),
564
"PublishNoEventSelected" );
568
QPointer<PublishDialog> publishdlg = new PublishDialog();
569
if ( incidence->attendeeCount() > 0 ) {
570
Attendee::List attendees = incidence->attendees();
571
Attendee::List::ConstIterator it;
572
for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) {
573
publishdlg->addAttendee( *it );
576
if ( publishdlg->exec() == QDialog::Accepted ) {
577
Incidence::Ptr inc( incidence->clone() );
578
inc->registerObserver( 0 );
579
inc->clearAttendees();
582
CalendarSupport::MailScheduler scheduler( calendar );
583
if ( scheduler.publish( incidence, publishdlg->addresses() ) ) {
584
KMessageBox::information(
586
i18n( "The item information was successfully sent." ),
587
i18n( "Publishing" ),
588
"IncidencePublishSuccess" );
592
i18n( "Unable to publish the item '%1'", incidence->summary() ) );
598
void CalendarSupport::scheduleiTIPMethods( KCalCore::iTIPMethod method, const Akonadi::Item& item, CalendarSupport::Calendar* calendar, QWidget* parentWidget )
600
/* Incidence::Ptr incidence = CalendarSupport::incidence( item );
605
i18n( "No item selected." ),
606
"ScheduleNoEventSelected" );
610
if ( incidence->attendeeCount() == 0 && method != iTIPPublish ) {
611
KMessageBox::information(
613
i18n( "The item has no attendees." ),
614
"ScheduleNoIncidences" );
618
Incidence *inc = incidence->clone();
619
inc->registerObserver( 0 );
620
inc->clearAttendees();
623
CalendarSupport::MailScheduler scheduler( calendar );
624
if ( scheduler.performTransaction( incidence, method ) ) {
625
KMessageBox::information(
627
i18n( "The groupware message for item '%1' "
628
"was successfully sent.\nMethod: %2",
629
incidence->summary(),
630
ScheduleMessage::methodName( method ) ),
631
i18n( "Sending Free/Busy" ),
632
"FreeBusyPublishSuccess" );
636
i18nc( "Groupware message sending failed. "
637
"%2 is request/reply/add/cancel/counter/etc.",
638
"Unable to send the item '%1'.\nMethod: %2",
639
incidence->summary(),
640
ScheduleMessage::methodName( method ) ) );
644
void CalendarSupport::saveAttachments(const Akonadi::Item& item, QWidget* parentWidget)
646
/* Incidence::Ptr incidence = CalendarSupport::incidence( item );
651
i18n( "No item selected." ),
656
Attachment::List attachments = incidence->attachments();
658
if ( attachments.empty() )
661
QString targetFile, targetDir;
662
if ( attachments.count() > 1 ) {
664
targetDir = KFileDialog::getExistingDirectory( KUrl( "kfiledialog:///saveAttachment" ),
666
i18n( "Save Attachments To" ) );
667
if ( targetDir.isEmpty() ) {
671
// we may not get a slash-terminated url out of KFileDialog
672
if ( !targetDir.endsWith('/') )
673
targetDir.append('/');
676
// only one item, get the desired filename
677
QString fileName = attachments.first()->label();
678
if ( fileName.isEmpty() ) {
679
fileName = i18nc( "filename for an unnamed attachment", "attachment.1" );
681
targetFile = KFileDialog::getSaveFileName( KUrl( "kfiledialog:///saveAttachment/" + fileName ),
684
i18n( "Save Attachment" ) );
685
if ( targetFile.isEmpty() ) {
689
targetDir = QFileInfo( targetFile ).absolutePath() + "/";
692
Q_FOREACH( Attachment::Ptr attachment, attachments ) {
693
targetFile = targetDir + attachment->label();
695
if ( attachment->isUri() ) {
696
sourceUrl = attachment->uri();
698
sourceUrl = incidence->writeAttachmentToTempFile( attachment );
700
// save the attachment url
701
if ( !KIO::NetAccess::file_copy( sourceUrl, KUrl( targetFile ) ) &&
702
KIO::NetAccess::lastError() ) {
703
KMessageBox::error( parentWidget, KIO::NetAccess::lastErrorString() );