1 /* 2 * singlefileresource.h - calendar resource held in a single file 3 * Program: kalarm 4 * SPDX-FileCopyrightText: 2020-2021 David Jarvie <djarvie@kde.org> 5 * 6 * SPDX-License-Identifier: LGPL-2.0-or-later 7 */ 8 9 #pragma once 10 11 #include "fileresource.h" 12 #include "fileresourceconfigmanager.h" 13 14 #include <KCalendarCore/MemoryCalendar> 15 #include <KCalendarCore/FileStorage> 16 17 #include <QUrl> 18 19 namespace KIO { 20 class FileCopyJob; 21 } 22 class KJob; 23 class QTimer; 24 25 using namespace KAlarmCal; 26 27 /** 28 * Calendar resource stored in a single file, either local or remote. 29 */ 30 class SingleFileResource : public FileResource 31 { 32 Q_OBJECT 33 public: 34 /** Construct a new SingleFileResource. 35 * Initialises the resource and initiates loading its events. 36 */ 37 static Resource create(FileResourceSettings* settings); 38 39 protected: 40 /** Constructor. 41 * Initialises the resource and initiates loading its events. 42 */ 43 explicit SingleFileResource(FileResourceSettings* settings); 44 45 public: 46 ~SingleFileResource() override; 47 48 /** Return the type of storage used by the resource. */ storageType()49 StorageType storageType() const override { return File; } 50 51 /** Return whether the resource is configured as read-only or is 52 * read-only on disc. 53 */ 54 bool readOnly() const override; 55 56 /** Return whether the resource is both enabled and fully writable for a 57 * given alarm type, i.e. not read-only, and compatible with the current 58 * KAlarm calendar format. 59 * 60 * @param type alarm type to check for, or EMPTY to check for any type. 61 * @return 1 = fully enabled and writable, 62 * 0 = enabled and writable except that backend calendar is in an 63 * old KAlarm format, 64 * -1 = read-only, disabled or incompatible format. 65 */ 66 int writableStatus(CalEvent::Type type = CalEvent::EMPTY) const override; 67 68 /** Reload the resource. Any cached data is first discarded. 69 * @param discardMods Discard any modifications since the last save. 70 * @return true if loading succeeded or has been initiated. 71 * false if it failed. 72 */ 73 bool reload(bool discardMods = false) override; 74 75 /** Return whether the resource is waiting for a save() to complete. */ 76 bool isSaving() const override; 77 78 /** Close the resource. This saves any unsaved data. 79 * Saving is not performed if the resource is disabled. 80 */ 81 void close() override; 82 83 /** Called when the resource's FileResourceSettings object is about to be destroyed. */ 84 void removeSettings() override; 85 86 protected: 87 /** Update the resource to the current KAlarm storage format. */ 88 bool updateStorageFmt() override; 89 90 /** This method is called by load() to allow derived classes to implement 91 * loading the resource from its backend, and fetch all events into 92 * @p newEvents. 93 * If the resource is cached, it should be loaded from the cache file (which 94 * if @p readThroughCache is true, should first be downloaded from the 95 * resource file). 96 * If the resource initiates but does not complete loading, loaded() must be 97 * called when loading completes or fails. 98 * @see loaded() 99 * 100 * @param newEvents To be updated to contain the events fetched. 101 * @param readThroughCache If the resource is cached, refresh the cache first. 102 * @return 1 = loading succeeded 103 * 0 = loading has been initiated, but has not yet completed 104 * -1 = loading failed. 105 */ 106 int doLoad(QHash<QString, KAEvent>& newEvents, bool readThroughCache, QString& errorMessage) override; 107 108 /** This method is called by save() to allow derived classes to implement 109 * saving the resource to its backend. 110 * If the resource is cached, it should be saved to the cache file (which 111 * if @p writeThroughCache is true, should then be uploaded from the 112 * resource file). 113 * If the resource initiates but does not complete saving, saved() must be 114 * called when saving completes or fails. 115 * @see saved() 116 * 117 * @param writeThroughCache If the resource is cached, update the file 118 * after writing to the cache. 119 * @param force Save even if no changes have been made since last 120 * loaded or saved. 121 * @return 1 = saving succeeded 122 * 0 = saving has been initiated, but has not yet completed 123 * -1 = saving failed. 124 */ 125 int doSave(bool writeThroughCache, bool force, QString& errorMessage) override; 126 127 /** Schedule the resource for saving. 128 * This delays calling save(), so as to enable multiple event changes to 129 * be saved together. 130 * 131 * @return true if saving succeeded or has been initiated/scheduled. 132 * false if it failed. 133 */ 134 bool scheduleSave(bool writeThroughCache = true) override; 135 136 /** 137 * Handles everything needed when the hash of a file has changed between the 138 * last write and the first read. This stores the new hash in a config file 139 * and notifies implementing resources to handle a hash change if the 140 * previous known hash was not empty. 141 * Returns true on success, false otherwise. 142 */ 143 bool readLocalFile(const QString& fileName, QString& errorMessage); 144 145 /** Read the data from the given local file. */ 146 bool readFromFile(const QString& fileName, QString& errorMessage); 147 148 /** 149 * Reimplement to write your data to the given file. 150 * The file is always local, storing back to the network url is done 151 * automatically when needed. 152 */ 153 bool writeToFile(const QString& fileName, QString& errorMessage); 154 155 /** This method is called by addEvent() to allow derived classes to add 156 * an event to the resource. 157 */ 158 bool doAddEvent(const KAEvent&) override; 159 160 /** This method is called by updateEvent() to allow derived classes to update 161 * an event in the resource. The event's UID must be unchanged. 162 */ 163 bool doUpdateEvent(const KAEvent&) override; 164 165 /** This method is called by deleteEvent() to allow derived classes to delete 166 * an event from the resource. 167 */ 168 bool doDeleteEvent(const KAEvent&) override; 169 170 /** Called when settings have changed, to allow derived classes to process 171 * the changes. 172 * @note Resources::notifySettingsChanged() is called after this, to 173 * notify clients. 174 */ 175 void handleSettingsChange(Changes&) override; 176 177 /** 178 * Generates the full path for the cache file in the case that a remote file 179 * is used. 180 */ 181 QString cacheFilePath() const; 182 183 /** 184 * Calculates an MD5 hash for given file. If the file does not exists 185 * or the path is empty, this will return an empty QByteArray. 186 */ 187 QByteArray calculateHash(const QString& fileName) const; 188 189 /** 190 * Stores the given hash into the config file. 191 */ 192 void saveHash(const QByteArray& hash) const; 193 194 private Q_SLOTS: slotSave()195 void slotSave() { save(nullptr, mSavePendingCache); } 196 // void handleProgress(KJob*, unsigned long); 197 void localFileChanged(const QString& fileName); 198 void slotDownloadJobResult(KJob*); 199 void slotUploadJobResult(KJob*); updateFormat()200 void updateFormat() { updateStorageFmt(); } 201 bool addLoadedEvent(const KCalendarCore::Event::Ptr&); 202 203 private: 204 void setLoadFailure(bool exists, Status); 205 206 QUrl mSaveUrl; // current local file for save() to use (may be temporary) 207 KIO::FileCopyJob* mDownloadJob {nullptr}; 208 KIO::FileCopyJob* mUploadJob {nullptr}; 209 QByteArray mCurrentHash; 210 KCalendarCore::MemoryCalendar::Ptr mCalendar; 211 KCalendarCore::FileStorage::Ptr mFileStorage; 212 QHash<QString, KAEvent> mLoadedEvents; // events loaded from calendar last time file was read 213 QTimer* mSaveTimer {nullptr}; // timer to enable multiple saves to be grouped 214 bool mSavePendingCache; // writeThroughCache parameter for delayed save() 215 bool mFileReadOnly {false}; // the calendar file is a read-only local file 216 }; 217 218 219 // vim: et sw=4: 220