File indexing completed on 2024-05-19 05:11:18

0001 /*
0002   SPDX-FileCopyrightText: 2004 Reinhold Kainhofer <reinhold@kainhofer.com>
0003   SPDX-FileCopyrightText: 2010-2012 Sérgio Martins <iamsergio@gmail.com>
0004 
0005   SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 #pragma once
0008 
0009 #include "akonadi-calendar_export.h"
0010 
0011 #include "itiphandler.h"
0012 #include <Akonadi/Collection>
0013 #include <Akonadi/Item>
0014 
0015 #include <KCalendarCore/Incidence>
0016 
0017 #include <QFlags>
0018 #include <QWidget>
0019 
0020 #include <memory>
0021 
0022 namespace Akonadi
0023 {
0024 class EntityTreeModel;
0025 class IncidenceChangerPrivate;
0026 
0027 /**
0028  * @short IncidenceChanger is the preferred way to easily create, modify and delete incidences.
0029  *
0030  * It hides the communication with akonadi from the library user.
0031  *
0032  * It provides the following features that ItemCreateJob, ItemModifyJob and
0033  * ItemDeleteJob do not:
0034  * - Sending groupware ( iTip ) messages to attendees and organizers.
0035  * - Aware of recurrences, allowing to only change one occurrence.
0036  * - Undo/Redo
0037  * - Group operations which are executed in an atomic manner.
0038  * - Collection ACLs
0039  * - Error dialogs with calendaring lingo
0040  *
0041  * In the context of this API, "change", means "creation", "deletion" or incidence "modification".
0042  *
0043  * @code
0044  * IncidenceChanger *changer = new IncidenceChanger( parent );
0045  * connect( changer,
0046  *          SIGNAL(createFinished(int,Akonadi::Item,Akonadi::IncidenceChanger::ResultCode,QString)),
0047  *          SLOT(slotCreateFinished(int,Akonadi::Item,Akonadi::IncidenceChanger::ResultCode,QString)) );
0048  *
0049  * connect( changer,
0050  *          SIGNAL(deleteFinished(int,QList<Akonadi::Item::Id>,Akonadi::IncidenceChanger::ResultCode,QString)),
0051  *          SLOT(slotDeleteFinished(int,QList<Akonadi::Item::Id>,Akonadi::IncidenceChanger::ResultCode,QString)) );
0052  *
0053  * connect( changer,SIGNAL(modifyFinished(int,Akonadi::Item,Akonadi::IncidenceChanger::ResultCode,QString)),
0054  *          SLOT(slotModifyFinished(int,Akonadi::Item,Akonadi::IncidenceChanger::ResultCode,QString)) );
0055  *
0056  * changer->setDestinationPolicy( IncidenceChanger::DestinationPolicyAsk );
0057  *
0058  * KCalendarCore::Incidence::Ptr incidence = (...);
0059  * int changeId = changer->createIncidence( incidence, Akonadi::Collection() );
0060  *
0061  *
0062  * if ( changeId == -1 )
0063  * {
0064  *  // Invalid parameters, incidence is null.
0065  * }
0066  *
0067  * @endcode
0068  *
0069  * @author Sérgio Martins <iamsergio@gmail.com>
0070  * @since 4.11
0071  */
0072 
0073 class History;
0074 
0075 class AKONADI_CALENDAR_EXPORT IncidenceChanger : public QObject
0076 {
0077     Q_OBJECT
0078 public:
0079     /**
0080      * This enum describes result codes which are returned by createFinished(),
0081      * modifyfinished() and deleteFinished() signals.
0082      */
0083     enum ResultCode {
0084         ResultCodeSuccess = 0,
0085         ResultCodeJobError, ///< ItemCreateJob, ItemModifyJob or ItemDeleteJob weren't successful
0086         ResultCodeAlreadyDeleted, ///< That incidence was already deleted, or currently being deleted.
0087         ResultCodeInvalidDefaultCollection, ///< Default collection is invalid and DestinationPolicyNeverAsk was used
0088         ResultCodeRolledback, ///< One change belonging to an atomic operation failed. All other changes were rolled back.
0089         ResultCodePermissions, ///< The parent collection doesn't have ACLs for this operation
0090         ResultCodeUserCanceled, ///< User canceled the operation
0091         ResultCodeInvalidUserCollection, ///< User somehow chose an invalid collection in the collection dialog ( should not happen )
0092         ResultCodeModificationDiscarded, ///< A new modification came in, the old one is discarded
0093         ResultCodeDuplicateId ///< Duplicate Akonadi::Item::Ids must be unique in group operations
0094     };
0095 
0096     /**
0097      * This enum describes destination policies.
0098      * Destination policies control how the createIncidence() method chooses the
0099      * collection where the item will be created.
0100      */
0101     enum DestinationPolicy {
0102         DestinationPolicyDefault, ///< The default collection is used, if it's invalid, the user is prompted. @see setDefaultCollection().
0103         DestinationPolicyAsk, ///< User is always asked which collection to use.
0104         DestinationPolicyNeverAsk ///< The default collection is used, if it's invalid, an error is returned, and the incidence isn't added.
0105     };
0106 
0107     /**
0108      * Enum for controlling "Do you want to e-mail attendees" type of dialogs.
0109      * This is only honoured if groupware communication is active.
0110      *
0111      * @see groupwareCommunication()
0112      * @since 4.12
0113      */
0114     enum InvitationPolicy {
0115         InvitationPolicySend = 0, ///< Invitation e-mails are sent without asking the user if he wants to.
0116         InvitationPolicyAsk, ///< The user is asked if an e-mail should be sent. This is the default.
0117         InvitationPolicyDontSend ///< E-mails aren't sent
0118     };
0119 
0120     /**
0121      * Flags describing whether invitation emails should signed and/or encrypted.
0122      */
0123     enum InvitationPrivacy {
0124         InvitationPrivacyPlain = 0, ///< Invitation emails are not signed or encrpyted
0125         InvitationPrivacySign = 1, ///< Invitation emails are signed
0126         InvitationPrivacyEncrypt = 2 //< Invitation emails are encrypted
0127     };
0128     Q_DECLARE_FLAGS(InvitationPrivacyFlags, InvitationPrivacy)
0129 
0130     /**
0131      * This enum describes change types.
0132      */
0133     enum ChangeType {
0134         ChangeTypeCreate, ///> Represents an incidence creation.
0135         ChangeTypeModify, ///> Represents an incidence modification.
0136         ChangeTypeDelete ///> Represents an incidence deletion.
0137     };
0138 
0139     /**
0140      * Creates a new IncidenceChanger instance.
0141      * creates a default ITIPHandlerComponentFactory object.
0142      * @param parent parent QObject
0143      */
0144     explicit IncidenceChanger(QObject *parent = nullptr);
0145 
0146     /**
0147      * Creates a new IncidenceChanger instance.
0148      * @param factory factory for creating dialogs and the mail transport job. To create a default
0149      * factory set factory == 0
0150      * @param parent parent QObject
0151      * @since 4.15
0152      */
0153     explicit IncidenceChanger(ITIPHandlerComponentFactory *factory, QObject *parent);
0154 
0155     /**
0156      * Destroys this IncidenceChanger instance.
0157      */
0158     ~IncidenceChanger() override;
0159 
0160     /**
0161      * Creates a new incidence.
0162      *
0163      * @param incidence Incidence to create, must be valid.
0164      * @param collection Collection where the incidence will be created. If invalid, one according
0165      *                   to the DestinationPolicy will be used. You can know which collection was
0166      *                   used by calling lastCollectionUsed();
0167      * @param parent widget parent to be used in dialogs.
0168      *
0169      * @return Returns an integer which identifies this change. This identifier is useful
0170      *         to correlate this operation with the IncidenceChanger::createFinished() signal.
0171      *
0172      *         Returns -1 if @p incidence is invalid. The createFinished() signal
0173      *         won't be emitted in this case.
0174      */
0175     int
0176     createIncidence(const KCalendarCore::Incidence::Ptr &incidence, const Akonadi::Collection &collection = Akonadi::Collection(), QWidget *parent = nullptr);
0177 
0178     /**
0179      * Creates a new incidence.
0180      *
0181      * @param item Item containing the incidence to create and metadata, such as tags.
0182      * @param collection Collection where the incidence will be created. If invalid, one according
0183      *                   to the DestinationPolicy will be used. You can know which collection was
0184      *                   used by calling lastCollectionUsed();
0185      * @param parent widget parent to be used in dialogs.
0186      *
0187      * @return Returns an integer which identifies this change. This identifier is useful
0188      *         to correlate this operation with the IncidenceChanger::createFinished() signal.
0189      *
0190      *         Returns -1 if @p item is invalid. The createFinished() signal
0191      *         won't be emitted in this case.
0192      */
0193     int createFromItem(const Akonadi::Item &item, const Akonadi::Collection &collection = Akonadi::Collection(), QWidget *parent = nullptr);
0194 
0195     /**
0196      * Deletes an incidence. If it's recurring, all occurrences are deleted.
0197      *
0198      * @param item Item to delete. Item must be valid.
0199      * @param parent Parent to be used in dialogs.
0200      *
0201      * @return Returns an integer which identifies this deletion. This identifier is useful
0202      *         to correlate this deletion with the IncidenceChanger::deleteFinished() signal.
0203      *
0204      *         Returns -1 if item is invalid. The deleteFinished() signal won't be emitted in this
0205      *         case.
0206      */
0207     int deleteIncidence(const Akonadi::Item &item, QWidget *parent = nullptr);
0208 
0209     /**
0210      * Deletes a list of Items.
0211      *
0212      * @param items List of items do delete. They must be valid.
0213      * @param parent Parent to be used in dialogs.
0214      * @return Returns an integer which identifies this deletion. This identifier is useful
0215      *         to correlate this operation with the IncidenceChanger::deleteFinished() signal.
0216      *
0217      *         Returns -1 if any item is invalid or if @p items is empty. The deleteFinished() signal
0218      *         won't be emitted in this case.
0219      */
0220     int deleteIncidences(const Akonadi::Item::List &items, QWidget *parent = nullptr);
0221 
0222     /**
0223      * Modifies an incidence.
0224      *
0225      * @param item A valid item, with the new payload.
0226      * @param originalPayload The payload before the modification. If invalid it won't be recorded
0227      *                        to the undo stack and groupware functionality won't be used for this
0228      *                        deletion.
0229      * @param parent Parent to be used in dialogs.
0230      *
0231      * @return Returns an integer which identifies this modification. This identifier is useful
0232      *         to correlate this operation with the IncidenceChanger::modifyFinished() signal.
0233      *
0234      *         Returns -1 if the item doesn't have a valid payload. The modifyFinished() signal
0235      *         won't be emitted in this case.
0236      */
0237     int modifyIncidence(const Akonadi::Item &item,
0238                         const KCalendarCore::Incidence::Ptr &originalPayload = KCalendarCore::Incidence::Ptr(),
0239                         QWidget *parent = nullptr);
0240 
0241     /**
0242      * Some incidence operations require more than one change. Like dissociating
0243      * occurrences, which needs an incidence add and an incidence change.
0244      *
0245      * If you want to prevent that the same dialogs are presented multiple times
0246      * use this function to start a batch operation.
0247      *
0248      * If one change belonging to a batch operation fails, all other changes
0249      * are rolled back.
0250      *
0251      * @param operationDescription Describes what the atomic operation does.
0252      *        This will be what incidenceChanger->history()->descriptionForNextUndo()
0253      *        if you have history enabled.
0254      *
0255      * @see endAtomicOperation()
0256      */
0257     void startAtomicOperation(const QString &operationDescription = QString());
0258 
0259     /**
0260      * Tells IncidenceChanger you finished doing changes that belong to a
0261      * batch operation.
0262      *
0263      * @see startAtomicOperation()
0264      */
0265     void endAtomicOperation();
0266 
0267     /**
0268      * Sets the base ETM tree model
0269      * Used by the editor dialog's collection combobox, for instance.
0270      */
0271     void setEntityTreeModel(Akonadi::EntityTreeModel *model);
0272 
0273     /**
0274      * Returns the base ETM tree model
0275      */
0276     Akonadi::EntityTreeModel *entityTreeModel() const;
0277 
0278     /**
0279      * Sets the default collection.
0280      * @param collection The collection to be used in createIncidence() if the
0281      *        proper destination policy is set.
0282      * @see createIncidence()
0283      * @see destinationPolicy()
0284      * @see defaultCollection()
0285      */
0286     void setDefaultCollection(const Akonadi::Collection &collection);
0287 
0288     /**
0289      * Returns the defaultCollection.
0290      * If none is set, an invalid Collection is returned.
0291      * @see setDefaultCollection()
0292      * @see DestinationPolicy
0293      */
0294     [[nodiscard]] Akonadi::Collection defaultCollection() const;
0295 
0296     /**
0297      * Sets the destination policy to use. The destination policy determines the
0298      * collection to use in createIncidence()
0299      *
0300      * @see createIncidence()
0301      * @see destinationPolicy()
0302      */
0303     void setDestinationPolicy(DestinationPolicy destinationPolicy);
0304 
0305     /**
0306      * Returns the current destination policy.
0307      * If none is set, DestinationPolicyDefault is returned.
0308      * @see setDestinationPolicy()
0309      * @see DestinationPolicy
0310      */
0311     [[nodiscard]] DestinationPolicy destinationPolicy() const;
0312 
0313     /**
0314      * Sets if IncidenceChanger should show error dialogs.
0315      */
0316     void setShowDialogsOnError(bool enable);
0317 
0318     /**
0319      * Returns true if error dialogs are shown by IncidenceChanger.
0320      * The default is true.
0321      *
0322      * @see setShowDialogsOnError()
0323      */
0324     [[nodiscard]] bool showDialogsOnError() const;
0325 
0326     /**
0327      * Sets if IncidenceChanger should honour collection's ACLs by disallowing changes if
0328      * necessary.
0329      */
0330     void setRespectsCollectionRights(bool respect);
0331 
0332     /**
0333      * Returns true if IncidenceChanger honors collection's ACLs by disallowing
0334      * changes if necessary.
0335      *
0336      * The default is true.
0337      * @see setRespectsCollectionRights()
0338      * @see ResultCode::ResultCodePermissions
0339      */
0340     [[nodiscard]] bool respectsCollectionRights() const;
0341 
0342     /**
0343      * Enable or disable history.
0344      * With history enabled all changes are recorded into the undo/redo stack.
0345      *
0346      * @see history()
0347      * @see historyEnabled()
0348      */
0349     void setHistoryEnabled(bool enable);
0350 
0351     /**
0352      * Returns true if changes are added into the undo stack.
0353      * Default is true.
0354      *
0355      * @see history()
0356      * @see historyEnabled()
0357      */
0358     [[nodiscard]] bool historyEnabled() const;
0359 
0360     /**
0361      * Returns a pointer to the history object.
0362      * It's always valid.
0363      * Ownership remains with IncidenceChanger.
0364      */
0365     History *history() const;
0366 
0367     /**
0368      * For performance reasons, IncidenceChanger internally caches the ids of the last deleted items,
0369      * to avoid creating useless delete jobs.
0370      *
0371      * This function exposes that functionality so it can be used in other scenarios.
0372      * One popular scenario is when you're using an ETM and the user is deleting items very fast,
0373      * ETM doesn't know about the deletions immediately, so it can happen that some items are
0374      * deleted more than once, resulting in an error.
0375      *
0376      * @return true if the item was deleted recently, false otherwise.
0377      */
0378     [[nodiscard]] bool deletedRecently(Akonadi::Item::Id) const;
0379 
0380     /**
0381      * Enables or disabled groupware communication.
0382      * With groupware communication enabled, invitations and update e-mails will be sent to each
0383      * attendee.
0384      */
0385     void setGroupwareCommunication(bool enabled);
0386 
0387     /**
0388      * Returns if we're using groupware communication.
0389      * Default is false.
0390      * @see setGroupwareCommuniation()
0391      */
0392     [[nodiscard]] bool groupwareCommunication() const;
0393 
0394     /**
0395      * Makes modifyIncidence() adjust recurrence parameters when modifying DTSTART.
0396      */
0397     void setAutoAdjustRecurrence(bool enable);
0398 
0399     /**
0400      * True if recurrence parameters are adjusted when modifying DTSTART.
0401      * Default is true.
0402      */
0403     [[nodiscard]] bool autoAdjustRecurrence() const;
0404 
0405     /**
0406      * Sets the invitation policy.
0407      *
0408      * @since 4.12
0409      */
0410     void setInvitationPolicy(InvitationPolicy policy);
0411 
0412     /**
0413      * Returns the invitation policy.
0414      * The default is InvitationPolicyAsk.
0415      *
0416      * @since 4.12
0417      */
0418     [[nodiscard]] InvitationPolicy invitationPolicy() const;
0419 
0420     /**
0421      * Returns the collection that the last createIncidence() used.
0422      * Will be invalid if no incidences were created yet.
0423      *
0424      * @see createIncidence().
0425      */
0426     [[nodiscard]] Akonadi::Collection lastCollectionUsed() const;
0427 
0428     /**
0429      * Sets the invitation privacy flags.
0430      */
0431     void setInvitationPrivacy(InvitationPrivacyFlags invitationPrivacy);
0432 
0433     /**
0434      * Returns the invitation privacy policy.
0435      * Default value is InvitationPrivacyPlain.
0436      */
0437     [[nodiscard]] InvitationPrivacyFlags invitationPrivacy() const;
0438 
0439 Q_SIGNALS:
0440     /**
0441      * Emitted when IncidenceChanger creates an Incidence in akonadi.
0442      *
0443      * @param changeId the unique identifier of this change, returned by createIncidence().
0444      * @param item the created item, might be invalid if the @p resultCode is not ResultCodeSuccess
0445      * @param resultCode success/error code
0446      * @param errorString if @p resultCode is not ResultCodeSuccess, contains an i18n'ed error
0447      *        message. If you enabled error dialogs, this string was already presented to the user.
0448      */
0449     void createFinished(int changeId, const Akonadi::Item &item, Akonadi::IncidenceChanger::ResultCode resultCode, const QString &errorString);
0450     /**
0451      * Emitted when IncidenceChanger modifies an Incidence in akonadi.
0452      *
0453      * @param changeId the unique identifier of this change, returned by modifyIncidence().
0454      * @param item the modified item, might be invalid if the @p resultCode is not ResultCodeSuccess
0455      * @param resultCode success/error code
0456      * @param errorString if @p resultCode is not ResultCodeSuccess, contains an i18n'ed error
0457      *        message. If you enabled error dialogs, this string was already presented to the user.
0458      */
0459     void modifyFinished(int changeId, const Akonadi::Item &item, Akonadi::IncidenceChanger::ResultCode resultCode, const QString &errorString);
0460     /**
0461      * Emitted when IncidenceChanger deletes an Incidence in akonadi.
0462      *
0463      * @param changeId the unique identifier of this change, returned by deletedIncidence().
0464      * @param itemIdList list of deleted item ids, might be emptu if the @p resultCode is not
0465      *                   ResultCodeSuccess
0466      * @param resultCode success/error code
0467      * @param errorString if @p resultCode is not ResultCodeSuccess, contains an i18n'ed error
0468      *        message. If you enabled error dialogs, this string was already presented to the user.
0469      */
0470     void deleteFinished(int changeId, const QList<Akonadi::Item::Id> &itemIdList, Akonadi::IncidenceChanger::ResultCode resultCode, const QString &errorString);
0471 
0472 private:
0473     //@cond PRIVATE
0474     friend class HistoryPrivate;
0475     friend class AtomicOperation;
0476     // used internally by the History class
0477     explicit IncidenceChanger(bool enableHistory, QObject *parent = nullptr);
0478 
0479     std::unique_ptr<IncidenceChangerPrivate> const d;
0480     //@endcond
0481 };
0482 }
0483 
0484 Q_DECLARE_OPERATORS_FOR_FLAGS(Akonadi::IncidenceChanger::InvitationPrivacyFlags)
0485 
0486 Q_DECLARE_METATYPE(Akonadi::IncidenceChanger::DestinationPolicy)
0487 Q_DECLARE_METATYPE(Akonadi::IncidenceChanger::ResultCode)
0488 Q_DECLARE_METATYPE(Akonadi::IncidenceChanger::ChangeType)