File indexing completed on 2024-11-17 05:00:55

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