1 /* 2 SPDX-FileCopyrightText: 2004 Reinhold Kainhofer <reinhold@kainhofer.com> 3 SPDX-FileCopyrightText: 2010-2012 Sérgio Martins <iamsergio@gmail.com> 4 5 SPDX-License-Identifier: LGPL-2.0-or-later 6 */ 7 #pragma once 8 9 #include "akonadi-calendar_export.h" 10 11 #include "itiphandler.h" 12 #include <Akonadi/Collection> 13 #include <Akonadi/Item> 14 15 #include <KCalendarCore/Incidence> 16 17 #include <QWidget> 18 19 #include <memory> 20 21 namespace Akonadi 22 { 23 class EntityTreeModel; 24 class IncidenceChangerPrivate; 25 26 /** 27 * @short IncidenceChanger is the preferred way to easily create, modify and delete incidences. 28 * 29 * It hides the communication with akonadi from the library user. 30 * 31 * It provides the following features that ItemCreateJob, ItemModifyJob and 32 * ItemDeleteJob do not: 33 * - Sending groupware ( iTip ) messages to attendees and organizers. 34 * - Aware of recurrences, allowing to only change one occurrence. 35 * - Undo/Redo 36 * - Group operations which are executed in an atomic manner. 37 * - Collection ACLs 38 * - Error dialogs with calendaring lingo 39 * 40 * In the context of this API, "change", means "creation", "deletion" or incidence "modification". 41 * 42 * @code 43 * IncidenceChanger *changer = new IncidenceChanger( parent ); 44 * connect( changer, 45 * SIGNAL(createFinished(int,Akonadi::Item,Akonadi::IncidenceChanger::ResultCode,QString)), 46 * SLOT(slotCreateFinished(int,Akonadi::Item,Akonadi::IncidenceChanger::ResultCode,QString)) ); 47 * 48 * connect( changer, 49 * SIGNAL(deleteFinished(int,QVector<Akonadi::Item::Id>,Akonadi::IncidenceChanger::ResultCode,QString)), 50 * SLOT(slotDeleteFinished(int,QVector<Akonadi::Item::Id>,Akonadi::IncidenceChanger::ResultCode,QString)) ); 51 * 52 * connect( changer,SIGNAL(modifyFinished(int,Akonadi::Item,Akonadi::IncidenceChanger::ResultCode,QString)), 53 * SLOT(slotModifyFinished(int,Akonadi::Item,Akonadi::IncidenceChanger::ResultCode,QString)) ); 54 * 55 * changer->setDestinationPolicy( IncidenceChanger::DestinationPolicyAsk ); 56 * 57 * KCalendarCore::Incidence::Ptr incidence = (...); 58 * int changeId = changer->createIncidence( incidence, Akonadi::Collection() ); 59 * 60 * 61 * if ( changeId == -1 ) 62 * { 63 * // Invalid parameters, incidence is null. 64 * } 65 * 66 * @endcode 67 * 68 * @author Sérgio Martins <iamsergio@gmail.com> 69 * @since 4.11 70 */ 71 72 class History; 73 74 class AKONADI_CALENDAR_EXPORT IncidenceChanger : public QObject 75 { 76 Q_OBJECT 77 public: 78 /** 79 * This enum describes result codes which are returned by createFinished(), 80 * modifyfinished() and deleteFinished() signals. 81 */ 82 enum ResultCode { 83 ResultCodeSuccess = 0, 84 ResultCodeJobError, ///< ItemCreateJob, ItemModifyJob or ItemDeleteJob weren't successful 85 ResultCodeAlreadyDeleted, ///< That incidence was already deleted, or currently being deleted. 86 ResultCodeInvalidDefaultCollection, ///< Default collection is invalid and DestinationPolicyNeverAsk was used 87 ResultCodeRolledback, ///< One change belonging to an atomic operation failed. All other changes were rolled back. 88 ResultCodePermissions, ///< The parent collection doesn't have ACLs for this operation 89 ResultCodeUserCanceled, ///< User canceled the operation 90 ResultCodeInvalidUserCollection, ///< User somehow chose an invalid collection in the collection dialog ( should not happen ) 91 ResultCodeModificationDiscarded, ///< A new modification came in, the old one is discarded 92 ResultCodeDuplicateId ///< Duplicate Akonadi::Item::Ids must be unique in group operations 93 }; 94 95 /** 96 * This enum describes destination policies. 97 * Destination policies control how the createIncidence() method chooses the 98 * collection where the item will be created. 99 */ 100 enum DestinationPolicy { 101 DestinationPolicyDefault, ///< The default collection is used, if it's invalid, the user is prompted. @see setDefaultCollection(). 102 DestinationPolicyAsk, ///< User is always asked which collection to use. 103 DestinationPolicyNeverAsk ///< The default collection is used, if it's invalid, an error is returned, and the incidence isn't added. 104 }; 105 106 /** 107 * Enum for controlling "Do you want to e-mail attendees" type of dialogs. 108 * This is only honoured if groupware communication is active. 109 * 110 * @see groupwareCommunication() 111 * @since 4.12 112 */ 113 enum InvitationPolicy { 114 InvitationPolicySend = 0, ///< Invitation e-mails are sent without asking the user if he wants to. 115 InvitationPolicyAsk, ///< The user is asked if an e-mail should be sent. This is the default. 116 InvitationPolicyDontSend ///< E-mails aren't sent 117 }; 118 119 /** 120 * This enum describes change types. 121 */ 122 enum ChangeType { 123 ChangeTypeCreate, ///> Represents an incidence creation. 124 ChangeTypeModify, ///> Represents an incidence modification. 125 ChangeTypeDelete ///> Represents an incidence deletion. 126 }; 127 128 /** 129 * Creates a new IncidenceChanger instance. 130 * creates a default ITIPHandlerComponentFactory object. 131 * @param parent parent QObject 132 */ 133 explicit IncidenceChanger(QObject *parent = nullptr); 134 135 /** 136 * Creates a new IncidenceChanger instance. 137 * @param factory factory for creating dialogs and the mail transport job. To create a default 138 * factory set factory == 0 139 * @param parent parent QObject 140 * @since 4.15 141 */ 142 explicit IncidenceChanger(ITIPHandlerComponentFactory *factory, QObject *parent); 143 144 /** 145 * Destroys this IncidenceChanger instance. 146 */ 147 ~IncidenceChanger() override; 148 149 /** 150 * Creates a new incidence. 151 * 152 * @param incidence Incidence to create, must be valid. 153 * @param collection Collection where the incidence will be created. If invalid, one according 154 * to the DestinationPolicy will be used. You can know which collection was 155 * used by calling lastCollectionUsed(); 156 * @param parent widget parent to be used in dialogs. 157 * 158 * @return Returns an integer which identifies this change. This identifier is useful 159 * to correlate this operation with the IncidenceChanger::createFinished() signal. 160 * 161 * Returns -1 if @p incidence is invalid. The createFinished() signal 162 * won't be emitted in this case. 163 */ 164 int createIncidence(const KCalendarCore::Incidence::Ptr &incidence, const Akonadi::Collection &collection = Akonadi::Collection(), QWidget *parent = nullptr); 165 166 /** 167 * Creates a new incidence. 168 * 169 * @param item Item containing the incidence to create and metadata, such as tags. 170 * @param collection Collection where the incidence will be created. If invalid, one according 171 * to the DestinationPolicy will be used. You can know which collection was 172 * used by calling lastCollectionUsed(); 173 * @param parent widget parent to be used in dialogs. 174 * 175 * @return Returns an integer which identifies this change. This identifier is useful 176 * to correlate this operation with the IncidenceChanger::createFinished() signal. 177 * 178 * Returns -1 if @p item is invalid. The createFinished() signal 179 * won't be emitted in this case. 180 */ 181 int createFromItem(const Akonadi::Item &item, const Akonadi::Collection &collection = Akonadi::Collection(), QWidget *parent = nullptr); 182 183 /** 184 * Deletes an incidence. If it's recurring, all occurrences are deleted. 185 * 186 * @param item Item to delete. Item must be valid. 187 * @param parent Parent to be used in dialogs. 188 * 189 * @return Returns an integer which identifies this deletion. This identifier is useful 190 * to correlate this deletion with the IncidenceChanger::deleteFinished() signal. 191 * 192 * Returns -1 if item is invalid. The deleteFinished() signal won't be emitted in this 193 * case. 194 */ 195 int deleteIncidence(const Akonadi::Item &item, QWidget *parent = nullptr); 196 197 /** 198 * Deletes a list of Items. 199 * 200 * @param items List of items do delete. They must be valid. 201 * @param parent Parent to be used in dialogs. 202 * @return Returns an integer which identifies this deletion. This identifier is useful 203 * to correlate this operation with the IncidenceChanger::deleteFinished() signal. 204 * 205 * Returns -1 if any item is invalid or if @p items is empty. The deleteFinished() signal 206 * won't be emitted in this case. 207 */ 208 int deleteIncidences(const Akonadi::Item::List &items, QWidget *parent = nullptr); 209 210 /** 211 * Modifies an incidence. 212 * 213 * @param item A valid item, with the new payload. 214 * @param originalPayload The payload before the modification. If invalid it won't be recorded 215 * to the undo stack and groupware functionality won't be used for this 216 * deletion. 217 * @param parent Parent to be used in dialogs. 218 * 219 * @return Returns an integer which identifies this modification. This identifier is useful 220 * to correlate this operation with the IncidenceChanger::modifyFinished() signal. 221 * 222 * Returns -1 if the item doesn't have a valid payload. The modifyFinished() signal 223 * won't be emitted in this case. 224 */ 225 int modifyIncidence(const Akonadi::Item &item, 226 const KCalendarCore::Incidence::Ptr &originalPayload = KCalendarCore::Incidence::Ptr(), 227 QWidget *parent = nullptr); 228 229 /** 230 * Some incidence operations require more than one change. Like dissociating 231 * occurrences, which needs an incidence add and an incidence change. 232 * 233 * If you want to prevent that the same dialogs are presented multiple times 234 * use this function to start a batch operation. 235 * 236 * If one change belonging to a batch operation fails, all other changes 237 * are rolled back. 238 * 239 * @param operationDescription Describes what the atomic operation does. 240 * This will be what incidenceChanger->history()->descriptionForNextUndo() 241 * if you have history enabled. 242 * 243 * @see endAtomicOperation() 244 */ 245 void startAtomicOperation(const QString &operationDescription = QString()); 246 247 /** 248 * Tells IncidenceChanger you finished doing changes that belong to a 249 * batch operation. 250 * 251 * @see startAtomicOperation() 252 */ 253 void endAtomicOperation(); 254 255 /** 256 * Sets the base ETM tree model 257 * Used by the editor dialog's collection combobox, for instance. 258 */ 259 void setEntityTreeModel(Akonadi::EntityTreeModel *model); 260 261 /** 262 * Returns the base ETM tree model 263 */ 264 Akonadi::EntityTreeModel *entityTreeModel() const; 265 266 /** 267 * Sets the default collection. 268 * @param collection The collection to be used in createIncidence() if the 269 * proper destination policy is set. 270 * @see createIncidence() 271 * @see destinationPolicy() 272 * @see defaultCollection() 273 */ 274 void setDefaultCollection(const Akonadi::Collection &collection); 275 276 /** 277 * Returns the defaultCollection. 278 * If none is set, an invalid Collection is returned. 279 * @see setDefaultCollection() 280 * @see DestinationPolicy 281 */ 282 Q_REQUIRED_RESULT Akonadi::Collection defaultCollection() const; 283 284 /** 285 * Sets the destination policy to use. The destination policy determines the 286 * collection to use in createIncidence() 287 * 288 * @see createIncidence() 289 * @see destinationPolicy() 290 */ 291 void setDestinationPolicy(DestinationPolicy destinationPolicy); 292 293 /** 294 * Returns the current destination policy. 295 * If none is set, DestinationPolicyDefault is returned. 296 * @see setDestinationPolicy() 297 * @see DestinationPolicy 298 */ 299 Q_REQUIRED_RESULT DestinationPolicy destinationPolicy() const; 300 301 /** 302 * Sets if IncidenceChanger should show error dialogs. 303 */ 304 void setShowDialogsOnError(bool enable); 305 306 /** 307 * Returns true if error dialogs are shown by IncidenceChanger. 308 * The default is true. 309 * 310 * @see setShowDialogsOnError() 311 */ 312 Q_REQUIRED_RESULT bool showDialogsOnError() const; 313 314 /** 315 * Sets if IncidenceChanger should honour collection's ACLs by disallowing changes if 316 * necessary. 317 */ 318 void setRespectsCollectionRights(bool respect); 319 320 /** 321 * Returns true if IncidenceChanger honors collection's ACLs by disallowing 322 * changes if necessary. 323 * 324 * The default is true. 325 * @see setRespectsCollectionRights() 326 * @see ResultCode::ResultCodePermissions 327 */ 328 Q_REQUIRED_RESULT bool respectsCollectionRights() const; 329 330 /** 331 * Enable or disable history. 332 * With history enabled all changes are recorded into the undo/redo stack. 333 * 334 * @see history() 335 * @see historyEnabled() 336 */ 337 void setHistoryEnabled(bool enable); 338 339 /** 340 * Returns true if changes are added into the undo stack. 341 * Default is true. 342 * 343 * @see history() 344 * @see historyEnabled() 345 */ 346 Q_REQUIRED_RESULT bool historyEnabled() const; 347 348 /** 349 * Returns a pointer to the history object. 350 * It's always valid. 351 * Ownership remains with IncidenceChanger. 352 */ 353 History *history() const; 354 355 /** 356 * For performance reasons, IncidenceChanger internally caches the ids of the last deleted items, 357 * to avoid creating useless delete jobs. 358 * 359 * This function exposes that functionality so it can be used in other scenarios. 360 * One popular scenario is when you're using an ETM and the user is deleting items very fast, 361 * ETM doesn't know about the deletions immediately, so it can happen that some items are 362 * deleted more than once, resulting in an error. 363 * 364 * @return true if the item was deleted recently, false otherwise. 365 */ 366 Q_REQUIRED_RESULT bool deletedRecently(Akonadi::Item::Id) const; 367 368 /** 369 * Enables or disabled groupware communication. 370 * With groupware communication enabled, invitations and update e-mails will be sent to each 371 * attendee. 372 */ 373 void setGroupwareCommunication(bool enabled); 374 375 /** 376 * Returns if we're using groupware communication. 377 * Default is false. 378 * @see setGroupwareCommuniation() 379 */ 380 Q_REQUIRED_RESULT bool groupwareCommunication() const; 381 382 /** 383 * Makes modifyIncidence() adjust recurrence parameters when modifying DTSTART. 384 */ 385 void setAutoAdjustRecurrence(bool enable); 386 387 /** 388 * True if recurrence parameters are adjusted when modifying DTSTART. 389 * Default is true. 390 */ 391 Q_REQUIRED_RESULT bool autoAdjustRecurrence() const; 392 393 /** 394 * Sets the invitation policy. 395 * 396 * @since 4.12 397 */ 398 void setInvitationPolicy(InvitationPolicy policy); 399 400 /** 401 * Returns the invitation policy. 402 * The default is InvitationPolicyAsk. 403 * 404 * @since 4.12 405 */ 406 Q_REQUIRED_RESULT InvitationPolicy invitationPolicy() const; 407 408 /** 409 * Returns the collection that the last createIncidence() used. 410 * Will be invalid if no incidences were created yet. 411 * 412 * @see createIncidence(). 413 */ 414 Q_REQUIRED_RESULT Akonadi::Collection lastCollectionUsed() const; 415 416 Q_SIGNALS: 417 /** 418 * Emitted when IncidenceChanger creates an Incidence in akonadi. 419 * 420 * @param changeId the unique identifier of this change, returned by createIncidence(). 421 * @param item the created item, might be invalid if the @p resultCode is not ResultCodeSuccess 422 * @param resultCode success/error code 423 * @param errorString if @p resultCode is not ResultCodeSuccess, contains an i18n'ed error 424 * message. If you enabled error dialogs, this string was already presented to the user. 425 */ 426 void createFinished(int changeId, const Akonadi::Item &item, Akonadi::IncidenceChanger::ResultCode resultCode, const QString &errorString); 427 /** 428 * Emitted when IncidenceChanger modifies an Incidence in akonadi. 429 * 430 * @param changeId the unique identifier of this change, returned by modifyIncidence(). 431 * @param item the modified item, might be invalid if the @p resultCode is not ResultCodeSuccess 432 * @param resultCode success/error code 433 * @param errorString if @p resultCode is not ResultCodeSuccess, contains an i18n'ed error 434 * message. If you enabled error dialogs, this string was already presented to the user. 435 */ 436 void modifyFinished(int changeId, const Akonadi::Item &item, Akonadi::IncidenceChanger::ResultCode resultCode, const QString &errorString); 437 /** 438 * Emitted when IncidenceChanger deletes an Incidence in akonadi. 439 * 440 * @param changeId the unique identifier of this change, returned by deletedIncidence(). 441 * @param itemIdList list of deleted item ids, might be emptu if the @p resultCode is not 442 * ResultCodeSuccess 443 * @param resultCode success/error code 444 * @param errorString if @p resultCode is not ResultCodeSuccess, contains an i18n'ed error 445 * message. If you enabled error dialogs, this string was already presented to the user. 446 */ 447 void 448 deleteFinished(int changeId, const QVector<Akonadi::Item::Id> &itemIdList, Akonadi::IncidenceChanger::ResultCode resultCode, const QString &errorString); 449 450 private: 451 //@cond PRIVATE 452 friend class HistoryPrivate; 453 friend class AtomicOperation; 454 // used internally by the History class 455 explicit IncidenceChanger(bool enableHistory, QObject *parent = nullptr); 456 457 std::unique_ptr<IncidenceChangerPrivate> const d; 458 //@endcond 459 }; 460 } 461 462 Q_DECLARE_METATYPE(Akonadi::IncidenceChanger::DestinationPolicy) 463 Q_DECLARE_METATYPE(Akonadi::IncidenceChanger::ResultCode) 464 Q_DECLARE_METATYPE(Akonadi::IncidenceChanger::ChangeType) 465 466