File indexing completed on 2025-02-16 04:48:49

0001 /*
0002  *  fileresourceconfigmanager.cpp  -  config manager for resources accessed via file system
0003  *  Program:  kalarm
0004  *  SPDX-FileCopyrightText: 2020-2023 David Jarvie <djarvie@kde.org>
0005  *
0006  *  SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #include "fileresourceconfigmanager.h"
0010 
0011 #include "resources.h"
0012 #include "singlefileresource.h"
0013 #include "fileresourcecalendarupdater.h"
0014 #include "kalarm_debug.h"
0015 
0016 #include <KConfig>
0017 #include <KConfigGroup>
0018 
0019 #include <QRegularExpression>
0020 
0021 namespace
0022 {
0023 // Config file keys
0024 const QLatin1String GENERAL_GROUP("General");
0025 const char* KEY_LASTID    = "LastId";
0026 }
0027 
0028 FileResourceConfigManager* FileResourceConfigManager::mInstance {nullptr};
0029 
0030 /******************************************************************************
0031 * Creates and returns the unique instance.
0032 */
0033 FileResourceConfigManager* FileResourceConfigManager::instance()
0034 {
0035     if (!mInstance)
0036         mInstance = new FileResourceConfigManager;
0037     return mInstance;
0038 }
0039 
0040 /******************************************************************************
0041 * Constructor. Reads the current config and creates all resources.
0042 */
0043 FileResourceConfigManager::FileResourceConfigManager()
0044 {
0045     mConfig = new KConfig(QStringLiteral("kalarmresources"));
0046 }
0047 
0048 /******************************************************************************
0049 * Read the current config and create all resources.
0050 */
0051 void FileResourceConfigManager::createResources(QObject* parent)
0052 {
0053     FileResourceConfigManager* manager = instance();
0054     if (manager->mCreated)
0055         return;
0056     manager->mCreated = 1;
0057 
0058     static const QRegularExpression re(QStringLiteral("^Resource_\\d+$"));
0059     QStringList resourceGroups = manager->mConfig->groupList().filter(re);
0060     if (!resourceGroups.isEmpty())
0061     {
0062         std::sort(resourceGroups.begin(), resourceGroups.end(),
0063                   [](const QString& g1, const QString& g2)
0064                   {
0065                       return QStringView(g1).mid(9).toInt() < QStringView(g2).mid(9).toInt();
0066                   });
0067 
0068         KConfigGroup general(manager->mConfig, GENERAL_GROUP);
0069         manager->mLastId = general.readEntry(KEY_LASTID, 0) | ResourceType::IdFlag;
0070 
0071         for (const QString& resourceGroup : std::as_const(resourceGroups))
0072         {
0073             const int groupIndex = QStringView(resourceGroup).mid(9).toInt();
0074             FileResourceSettings::Ptr settings(new FileResourceSettings(manager->mConfig, resourceGroup));
0075             if (!settings->isValid())
0076             {
0077                 qCWarning(KALARM_LOG) << "FileResourceConfigManager: Invalid config for" << resourceGroup;
0078                 manager->mConfig->deleteGroup(resourceGroup);   // invalid config for this resource
0079             }
0080             else
0081             {
0082                 // Check for and remove duplicate URL or 'standard' setting
0083                 for (auto it = manager->mResources.constBegin();  it != manager->mResources.constEnd();  ++it)
0084                 {
0085                     const ResourceData& data = it.value();
0086                     if (settings->url() == data.resource.location())
0087                     {
0088                         qCWarning(KALARM_LOG) << "FileResourceConfigManager: Duplicate URL in config for" << resourceGroup;
0089                         manager->mConfig->deleteGroup(resourceGroup);   // invalid config for this resource
0090                         qCWarning(KALARM_LOG) << "FileResourceConfigManager: Deleted duplicate resource" << settings->displayName();
0091                         settings.clear();
0092                         break;
0093                     }
0094                     const CalEvent::Types std = settings->standardTypes() & data.settings->standardTypes();
0095                     if (std)
0096                     {
0097                         qCWarning(KALARM_LOG) << "FileResourceConfigManager: Duplicate 'standard' setting in config for" << resourceGroup;
0098                         settings->setStandard(settings->standardTypes() ^ std);
0099                     }
0100                 }
0101                 if (settings)
0102                 {
0103                     Resource resource(createResource(settings));
0104                     manager->mResources[settings->id()] = ResourceData(resource, settings);
0105                     manager->mConfigGroups[groupIndex] = settings->id();
0106 
0107                     Resources::notifyNewResourceInitialised(resource);
0108 
0109                     // Update the calendar to the current KAlarm format if necessary, and
0110                     // if the user agrees.
0111                     FileResourceCalendarUpdater::updateToCurrentFormat(resource, false, parent);
0112                 }
0113             }
0114         }
0115         manager->mConfig->sync();
0116 
0117         // Allow any calendar updater instances to complete and auto-delete.
0118         FileResourceCalendarUpdater::waitForCompletion();
0119     }
0120     manager->mCreated = 2;
0121 }
0122 
0123 /******************************************************************************
0124 * Destructor. Writes the current config.
0125 */
0126 FileResourceConfigManager::~FileResourceConfigManager()
0127 {
0128     writeConfig();
0129     delete mConfig;
0130     mInstance = nullptr;
0131 }
0132 
0133 /******************************************************************************
0134 * Writes the 'kalarmresources' config file.
0135 */
0136 void FileResourceConfigManager::writeConfig()
0137 {
0138     // No point in writing unless the config has already been read!
0139     if (mInstance)
0140         mInstance->mConfig->sync();
0141 }
0142 
0143 /******************************************************************************
0144 * Return the IDs of all calendar resources.
0145 */
0146 QList<ResourceId> FileResourceConfigManager::resourceIds()
0147 {
0148     return instance()->mResources.keys();
0149 }
0150 
0151 /******************************************************************************
0152 * Create a new calendar resource with the given settings.
0153 */
0154 Resource FileResourceConfigManager::addResource(FileResourceSettings::Ptr& settings)
0155 {
0156     // Find the first unused config group name index.
0157     FileResourceConfigManager* manager = instance();
0158     int lastIndex = 0;
0159     for (auto it = manager->mConfigGroups.constBegin();  it != manager->mConfigGroups.constEnd();  ++it)
0160     {
0161         const int index = it.key();
0162         if (index > lastIndex + 1)
0163             break;
0164         lastIndex = index;
0165     }
0166     const int groupIndex = lastIndex + 1;
0167 
0168     // Get a unique ID.
0169     const int id = ++manager->mLastId;
0170     settings->setId(id);
0171     // Save the new last-used ID, but strip out IdFlag to make it more legible.
0172     KConfigGroup general(manager->mConfig, GENERAL_GROUP);
0173     general.writeEntry(KEY_LASTID, id & ~ResourceType::IdFlag);
0174 
0175     const QString configGroup = groupName(groupIndex);
0176     settings->createConfig(manager->mConfig, configGroup);
0177     manager->mConfigGroups[groupIndex] = id;
0178     Resource resource(createResource(settings));
0179     manager->mResources[id] = ResourceData(resource, settings);
0180 
0181     Resources::notifyNewResourceInitialised(resource);
0182     return resource;
0183 }
0184 
0185 /******************************************************************************
0186 * Delete a calendar resource and its settings.
0187 */
0188 bool FileResourceConfigManager::removeResource(Resource& resource)
0189 {
0190     if (resource.isValid())
0191     {
0192         FileResourceConfigManager* manager = instance();
0193         const ResourceId id = resource.id();
0194         const int groupIndex = manager->findResourceGroup(id);
0195         if (groupIndex >= 0)
0196         {
0197             const QString configGroup = groupName(groupIndex);
0198             manager->mConfig->deleteGroup(configGroup);
0199             manager->mConfig->sync();
0200             manager->mConfigGroups.remove(groupIndex);
0201             Resources::notifySettingsDestroyed(id);   // removing from mResources will destroy settings instance
0202             manager->mResources.remove(id);
0203             return true;
0204         }
0205     }
0206     return false;
0207 }
0208 
0209 /******************************************************************************
0210 * Return the available file system resource types handled by the manager.
0211 */
0212 QList<ResourceType::Storage> FileResourceConfigManager::storageTypes()
0213 {
0214     return { ResourceType::Storage::File
0215 //           , ResourceType::Storage::Directory   // not currently intended to be implemented
0216            };
0217 }
0218 
0219 /******************************************************************************
0220 * Find the config group for a resource ID.
0221 */
0222 int FileResourceConfigManager::findResourceGroup(ResourceId id) const
0223 {
0224     for (auto it = mConfigGroups.constBegin();  it != mConfigGroups.constEnd();  ++it)
0225         if (it.value() == id)
0226             return it.key();
0227     return -1;
0228 }
0229 
0230 /******************************************************************************
0231 * Return the config group name for a given config group index.
0232 */
0233 QString FileResourceConfigManager::groupName(int groupIndex)
0234 {
0235     return QStringLiteral("Resource_%1").arg(groupIndex);
0236 }
0237 
0238 /******************************************************************************
0239 * Create a new resource with the given settings.
0240 */
0241 Resource FileResourceConfigManager::createResource(FileResourceSettings::Ptr& settings)
0242 {
0243     switch (settings->storageType())
0244     {
0245         case FileResourceSettings::File:
0246             return SingleFileResource::create(settings);
0247         case FileResourceSettings::Directory:   // not currently intended to be implemented
0248         default:
0249             return Resource::null();
0250     }
0251 }
0252 
0253 // vim: et sw=4: