File indexing completed on 2024-11-10 04:30:21

0001 /*
0002  *   SPDX-FileCopyrightText: 2015-2016 Ivan Cukic <ivan.cukic@kde.org>
0003  *
0004  *   SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005  */
0006 
0007 #include "ResourcesDatabaseSchema.h"
0008 
0009 #include <QCoreApplication>
0010 #include <QStandardPaths>
0011 #include <QVariant>
0012 
0013 namespace Common
0014 {
0015 namespace ResourcesDatabaseSchema
0016 {
0017 
0018 const QString name = QStringLiteral("Resources");
0019 
0020 QString version()
0021 {
0022     return QStringLiteral("2015.02.09");
0023 }
0024 
0025 QStringList schema()
0026 {
0027     // If only we could use initializer lists here ...
0028 
0029     return QStringList()
0030 
0031         << // Schema information table, used for versioning
0032         QStringLiteral(
0033                "CREATE TABLE IF NOT EXISTS SchemaInfo ("
0034                "key text PRIMARY KEY, value text"
0035                ")")
0036 
0037         << QStringLiteral("INSERT OR IGNORE INTO schemaInfo VALUES ('version', '%1')").arg(version())
0038         << QStringLiteral("UPDATE schemaInfo SET value = '%1' WHERE key = 'version'").arg(version())
0039 
0040         << // The ResourceEvent table saves the Opened/Closed event pairs for
0041         // a resource. The Accessed event is mapped to those.
0042         // Focusing events are not stored in order not to get a
0043         // huge database file and to lessen writes to the disk.
0044         QStringLiteral(
0045                "CREATE TABLE IF NOT EXISTS ResourceEvent ("
0046                "usedActivity TEXT, "
0047                "initiatingAgent TEXT, "
0048                "targettedResource TEXT, "
0049                "start INTEGER, "
0050                "end INTEGER "
0051                ")")
0052 
0053         << // The ResourceScoreCache table stores the calculated scores
0054         // for resources based on the recorded events.
0055         QStringLiteral(
0056                "CREATE TABLE IF NOT EXISTS ResourceScoreCache ("
0057                "usedActivity TEXT, "
0058                "initiatingAgent TEXT, "
0059                "targettedResource TEXT, "
0060                "scoreType INTEGER, "
0061                "cachedScore FLOAT, "
0062                "firstUpdate INTEGER, "
0063                "lastUpdate INTEGER, "
0064                "PRIMARY KEY(usedActivity, initiatingAgent, targettedResource)"
0065                ")")
0066 
0067         << // @since 2014.05.05
0068         // The ResourceLink table stores the information, formerly kept by
0069         // Nepomuk, of which resources are linked to which activities.
0070         // The additional features compared to the old days are
0071         // the ability to limit the link to specific applications, and
0072         // to create global links.
0073         QStringLiteral(
0074                "CREATE TABLE IF NOT EXISTS ResourceLink ("
0075                "usedActivity TEXT, "
0076                "initiatingAgent TEXT, "
0077                "targettedResource TEXT, "
0078                "PRIMARY KEY(usedActivity, initiatingAgent, targettedResource)"
0079                ")")
0080 
0081         << // @since 2015.01.18
0082         // The ResourceInfo table stores the collected information about a
0083         // resource that is not agent nor activity related like the
0084         // title and the mime type.
0085         // If these are automatically retrieved (works for files), the
0086         // flag is set to true. This is done for the agents to be able to
0087         // override these.
0088         QStringLiteral(
0089                "CREATE TABLE IF NOT EXISTS ResourceInfo ("
0090                "targettedResource TEXT, "
0091                "title TEXT, "
0092                "mimetype TEXT, "
0093                "autoTitle INTEGER, "
0094                "autoMimetype INTEGER, "
0095                "PRIMARY KEY(targettedResource)"
0096                ")")
0097 
0098         ;
0099 }
0100 
0101 // TODO: This will require some refactoring after we introduce more databases
0102 QString defaultPath()
0103 {
0104     return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/kactivitymanagerd/resources/database");
0105 }
0106 
0107 const char *overrideFlagProperty = "org.kde.KActivities.ResourcesDatabase.overrideDatabase";
0108 const char *overrideFileProperty = "org.kde.KActivities.ResourcesDatabase.overrideDatabaseFile";
0109 
0110 QString path()
0111 {
0112     auto app = QCoreApplication::instance();
0113 
0114     return (app->property(overrideFlagProperty).toBool()) ? app->property(overrideFileProperty).toString() : defaultPath();
0115 }
0116 
0117 void overridePath(const QString &path)
0118 {
0119     auto app = QCoreApplication::instance();
0120 
0121     app->setProperty(overrideFlagProperty, true);
0122     app->setProperty(overrideFileProperty, path);
0123 }
0124 
0125 void initSchema(Database &database)
0126 {
0127     QString dbSchemaVersion;
0128 
0129     auto query = database.execQuery(QStringLiteral("SELECT value FROM SchemaInfo WHERE key = 'version'"),
0130                                     /* ignore error */ true);
0131 
0132     if (query.next()) {
0133         dbSchemaVersion = query.value(0).toString();
0134     }
0135 
0136     // Early bail-out if the schema is up-to-date
0137     if (dbSchemaVersion == version()) {
0138         return;
0139     }
0140 
0141     // Transition to KF5:
0142     // We left the world of Nepomuk, and all the ontologies went
0143     // across the sea to the Undying Lands.
0144     // This needs to be done before executing the schema() queries
0145     // so that we do not create new (empty) tables and block these
0146     // queries from being executed.
0147     if (dbSchemaVersion < QStringLiteral("2014.04.14")) {
0148         database.execQuery(QStringLiteral("ALTER TABLE nuao_DesktopEvent RENAME TO ResourceEvent"),
0149                            /* ignore error */ true);
0150         database.execQuery(QStringLiteral("ALTER TABLE kext_ResourceScoreCache RENAME TO ResourceScoreCache"),
0151                            /* ignore error */ true);
0152     }
0153 
0154     database.execQueries(ResourcesDatabaseSchema::schema());
0155 
0156     // We can not allow empty fields for activity and agent, they need to
0157     // be at least magic values. These do not change the structure
0158     // of the database, but the old data.
0159     if (dbSchemaVersion < QStringLiteral("2015.02.09")) {
0160         const QString updateActivity = QStringLiteral(
0161             "SET usedActivity=':global' "
0162             "WHERE usedActivity IS NULL OR usedActivity = ''");
0163 
0164         const QString updateAgent = QStringLiteral(
0165             "SET initiatingAgent=':global' "
0166             "WHERE initiatingAgent IS NULL OR initiatingAgent = ''");
0167 
0168         // When the activity field was empty, it meant the file was
0169         // linked to all activities (aka :global)
0170         database.execQuery("UPDATE ResourceLink " + updateActivity);
0171 
0172         // When the agent field was empty, it meant the file was not
0173         // linked to a specified agent (aka :global)
0174         database.execQuery("UPDATE ResourceLink " + updateAgent);
0175 
0176         // These were not supposed to be empty, but in the case they were,
0177         // deal with them as well
0178         database.execQuery("UPDATE ResourceEvent " + updateActivity);
0179         database.execQuery("UPDATE ResourceEvent " + updateAgent);
0180         database.execQuery("UPDATE ResourceScoreCache " + updateActivity);
0181         database.execQuery("UPDATE ResourceScoreCache " + updateAgent);
0182     }
0183 }
0184 
0185 } // namespace Common
0186 } // namespace ResourcesDatabaseSchema