File indexing completed on 2024-11-24 04:42:33

0001 /*
0002  *  singlefileresource.h  -  calendar resource held in a single file
0003  *  Program:  kalarm
0004  *  SPDX-FileCopyrightText: 2020-2023 David Jarvie <djarvie@kde.org>
0005  *
0006  *  SPDX-License-Identifier: LGPL-2.0-or-later
0007  */
0008 
0009 #pragma once
0010 
0011 #include "fileresource.h"
0012 #include "fileresourceconfigmanager.h"
0013 
0014 #include <KCalendarCore/MemoryCalendar>
0015 #include <KCalendarCore/FileStorage>
0016 
0017 #include <QUrl>
0018 
0019 namespace KIO {
0020 class FileCopyJob;
0021 }
0022 class KJob;
0023 class QTimer;
0024 
0025 using namespace KAlarmCal;
0026 
0027 /**
0028  * Calendar resource stored in a single file, either local or remote.
0029  */
0030 class SingleFileResource : public FileResource
0031 {
0032     Q_OBJECT
0033 public:
0034     /** Construct a new SingleFileResource.
0035      *  Initialises the resource and initiates loading its events.
0036      */
0037     static Resource create(FileResourceSettings::Ptr settings);
0038 
0039 protected:
0040     /** Constructor.
0041      *  Initialises the resource and initiates loading its events.
0042      */
0043     explicit SingleFileResource(FileResourceSettings::Ptr settings);
0044 
0045 public:
0046     ~SingleFileResource() override;
0047 
0048     /** Return the type of storage used by the resource. */
0049     Storage storageType() const override   { return Storage::File; }
0050 
0051     /** Return whether the resource is configured as read-only or is
0052      *  read-only on disc.
0053      */
0054     bool readOnly() const override;
0055 
0056     /** Return whether the resource is both enabled and fully writable for a
0057      *  given alarm type, i.e. not read-only, and compatible with the current
0058      *  KAlarm calendar format.
0059      *
0060      *  @param type  alarm type to check for, or EMPTY to check for any type.
0061      *  @return 1 = fully enabled and writable,
0062      *          0 = enabled and writable except that backend calendar is in an
0063      *              old KAlarm format,
0064      *         -1 = read-only, disabled or incompatible format.
0065      */
0066     int writableStatus(CalEvent::Type type = CalEvent::EMPTY) const override;
0067 
0068     /** Reload the resource. Any cached data is first discarded.
0069      *  @param discardMods  Discard any modifications since the last save.
0070      *  @return true if loading succeeded or has been initiated.
0071      *          false if it failed.
0072      */
0073     bool reload(bool discardMods = false) override;
0074 
0075     /** Return whether the resource is waiting for a save() to complete. */
0076     bool isSaving() const override;
0077 
0078     /** Close the resource. This saves any unsaved data.
0079      *  Saving is not performed if the resource is disabled.
0080      */
0081     void close() override;
0082 
0083     /** Called when the resource's FileResourceSettings object is about to be destroyed. */
0084     void removeSettings() override;
0085 
0086 protected:
0087     /** Update the resource to the current KAlarm storage format. */
0088     bool updateStorageFmt() override;
0089 
0090     /** This method is called by load() to allow derived classes to implement
0091      *  loading the resource from its backend, and fetch all events into
0092      *  @p newEvents.
0093      *  If the resource is cached, it should be loaded from the cache file (which
0094      *  if @p readThroughCache is true, should first be downloaded from the
0095      *  resource file).
0096      *  If the resource initiates but does not complete loading, loaded() must be
0097      *  called when loading completes or fails.
0098      *  @see loaded()
0099      *
0100      *  @param newEvents         To be updated to contain the events fetched.
0101      *  @param readThroughCache  If the resource is cached, refresh the cache first.
0102      *  @return 1 = loading succeeded
0103      *          0 = loading has been initiated, but has not yet completed
0104      *         -1 = loading failed.
0105      */
0106     int doLoad(QHash<QString, KAEvent>& newEvents, bool readThroughCache, QString& errorMessage) override;
0107 
0108     /** This method is called by save() to allow derived classes to implement
0109      *  saving the resource to its backend.
0110      *  If the resource is cached, it should be saved to the cache file (which
0111      *  if @p writeThroughCache is true, should then be uploaded from the
0112      *  resource file).
0113      *  If the resource initiates but does not complete saving, saved() must be
0114      *  called when saving completes or fails.
0115      *  @see saved()
0116      *
0117      *  @param writeThroughCache  If the resource is cached, update the file
0118      *                            after writing to the cache.
0119      *  @param force              Save even if no changes have been made since last
0120      *                            loaded or saved.
0121      *  @return 1 = saving succeeded
0122      *          0 = saving has been initiated, but has not yet completed
0123      *         -1 = saving failed.
0124      */
0125     int doSave(bool writeThroughCache, bool force, QString& errorMessage) override;
0126 
0127     /** Schedule the resource for saving.
0128      *  This delays calling save(), so as to enable multiple event changes to
0129      *  be saved together.
0130      *
0131      *  @return true if saving succeeded or has been initiated/scheduled.
0132      *          false if it failed.
0133      */
0134     bool scheduleSave(bool writeThroughCache = true) override;
0135 
0136     /**
0137      * Handles everything needed when the hash of a file has changed between the
0138      * last write and the first read. This stores the new hash in a config file
0139      * and notifies implementing resources to handle a hash change if the
0140      * previous known hash was not empty.
0141      * Returns true on success, false otherwise.
0142      */
0143     bool readLocalFile(const QString& fileName, QString& errorMessage);
0144 
0145     /** Read the data from the given local file. */
0146     bool readFromFile(const QString& fileName, QString& errorMessage);
0147 
0148     /**
0149      * Reimplement to write your data to the given file.
0150      * The file is always local, storing back to the network url is done
0151      * automatically when needed.
0152      */
0153     bool writeToFile(const QString& fileName, QString& errorMessage);
0154 
0155     /** This method is called by addEvent() to allow derived classes to add
0156      *  an event to the resource.
0157      */
0158     bool doAddEvent(const KAEvent&) override;
0159 
0160     /** This method is called by updateEvent() to allow derived classes to update
0161      *  an event in the resource. The event's UID must be unchanged.
0162      */
0163     bool doUpdateEvent(const KAEvent&) override;
0164 
0165     /** This method is called by deleteEvent() to allow derived classes to delete
0166      *  an event from the resource.
0167      */
0168     bool doDeleteEvent(const KAEvent&) override;
0169 
0170     /** Called when settings have changed, to allow derived classes to process
0171      *  the changes.
0172      *  @note  Resources::notifySettingsChanged() is called after this, to
0173      *         notify clients.
0174      */
0175     void handleSettingsChange(Changes&) override;
0176 
0177     /**
0178      * Generates the full path for the cache file in the case that a remote file
0179      * is used.
0180      */
0181     QString cacheFilePath() const;
0182 
0183     /**
0184      * Calculates an MD5 hash for given file. If the file does not exists
0185      * or the path is empty, this will return an empty QByteArray.
0186      */
0187     QByteArray calculateHash(const QString& fileName) const;
0188 
0189     /**
0190      * Stores the given hash into the config file.
0191      */
0192     void saveHash(const QByteArray& hash) const;
0193 
0194 private Q_SLOTS:
0195     void slotSave()   { save(nullptr, mSavePendingCache); }
0196 //    void handleProgress(KJob*, unsigned long);
0197     void localFileChanged(const QString& fileName);
0198     void slotDownloadJobResult(KJob*);
0199     void slotUploadJobResult(KJob*);
0200     void updateFormat()    { updateStorageFmt(); }
0201     bool addLoadedEvent(const KCalendarCore::Event::Ptr&);
0202 
0203 private:
0204     void setLoadFailure(bool exists, Status);
0205 
0206     QUrl               mSaveUrl;   // current local file for save() to use (may be temporary)
0207     KIO::FileCopyJob*  mDownloadJob {nullptr};
0208     KIO::FileCopyJob*  mUploadJob {nullptr};
0209     QByteArray         mCurrentHash;
0210     KCalendarCore::MemoryCalendar::Ptr mCalendar;
0211     KCalendarCore::FileStorage::Ptr    mFileStorage;
0212     QHash<QString, KAEvent> mLoadedEvents;    // events loaded from calendar last time file was read
0213     QTimer*            mSaveTimer {nullptr};  // timer to enable multiple saves to be grouped
0214     bool               mSavePendingCache;     // writeThroughCache parameter for delayed save()
0215     bool               mFileReadOnly {false}; // the calendar file is a read-only local file
0216 };
0217 
0218 // vim: et sw=4: