diff --git a/desktop/gnome/apps/gitg/actions.py b/desktop/gnome/apps/gitg/actions.py
new file mode 100644
index 0000000000..cc6214d303
--- /dev/null
+++ b/desktop/gnome/apps/gitg/actions.py
@@ -0,0 +1,18 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Licensed under the GNU General Public License, version 2.
+# See the file http://www.gnu.org/copyleft/gpl.txt.
+
+from pisi.actionsapi import mesontools, pisitools
+
+def setup():
+ mesontools.configure()
+
+def build():
+ mesontools.build()
+
+def install():
+ mesontools.install()
+
+ pisitools.dodoc("AUTHORS", "ChangeLog", "README*", "NEWS")
diff --git a/desktop/gnome/apps/gitg/files/vala.patch b/desktop/gnome/apps/gitg/files/vala.patch
new file mode 100644
index 0000000000..15cefa4944
--- /dev/null
+++ b/desktop/gnome/apps/gitg/files/vala.patch
@@ -0,0 +1,29 @@
+--- libgitg/gitg-diff-view-file-selectable.vala 2019-07-25 13:13:01.000000000 +0200
++++ libgitg/gitg-diff-view-file-selectable.vala 2020-09-06 14:36:26.374965220 +0200
+@@ -54,7 +54,7 @@
+
+ buffer.get_start_iter(out iter);
+
+- while (buffer.forward_iter_to_source_mark(ref iter, d_selection_category))
++ while (buffer.forward_iter_to_source_mark(iter, d_selection_category))
+ {
+ ret += iter.get_line();
+ }
+@@ -321,7 +321,7 @@
+
+ var buffer = source_view.buffer as Gtk.SourceBuffer;
+
+- if (!buffer.forward_iter_to_source_mark(ref iter, "header"))
++ if (!buffer.forward_iter_to_source_mark(iter, "header"))
+ {
+ iter.forward_to_end();
+ }
+@@ -493,7 +493,7 @@
+ }
+ else
+ {
+- something_selected = (buffer as Gtk.SourceBuffer).forward_iter_to_source_mark(ref iter, d_selection_category);
++ something_selected = (buffer as Gtk.SourceBuffer).forward_iter_to_source_mark(iter, d_selection_category);
+ }
+
+ if (something_selected != has_selection)
diff --git a/desktop/gnome/apps/gitg/pspec.xml b/desktop/gnome/apps/gitg/pspec.xml
new file mode 100644
index 0000000000..34bf177d75
--- /dev/null
+++ b/desktop/gnome/apps/gitg/pspec.xml
@@ -0,0 +1,81 @@
+
+
+
+
+ gitg
+ https://wiki.gnome.org/Apps/Gitg
+
+ Ayhan Yalçınsoy
+ ayhanyalcinsoy@pisilinux.org
+
+ GPLv2
+ gitg
+ app:gui
+ GTK+ graphical interface for the git revision control system
+ GTK+ graphical interface for the git revision control system
+ mirrors://gnome/gitg/3.32/gitg-3.32.1.tar.xz
+
+ gettext-devel
+ python-pygobject3-devel
+ python3-pygobject3-devel
+ glib2-devel
+ gobject-introspection-devel
+ gsettings-desktop-schemas-devel
+ gtk3-devel
+ gtksourceview3-devel
+ gtkspell3-devel
+ json-glib-devel
+ meson
+ libdazzle-devel
+ libgee-devel
+ libgit2-devel
+ libgit2-glib-devel
+ libpeas-devel
+ libsecret-devel
+ libsoup-devel
+ python3-devel
+ vala-devel
+ webkit2gtk-devel
+
+
+ vala.patch
+
+
+
+
+ gitg
+
+ vala
+
+
+ /usr/bin
+ /usr/lib
+ /usr/share
+ /usr/share/local
+
+
+
+
+ gitg-devel
+ Development files for gitg
+
+ gitg
+ vala
+
+
+ /usr/include
+ /usr/lib/pkgconfig
+ /usr/share/gir-1.0
+
+
+
+
+
+ 2021-06-08
+ 3.32.1
+ First release
+ Ayhan Yalçınsoy
+ ayhanyalcinsoy@pisilinux.org
+
+
+
diff --git a/desktop/gnome/apps/gitg/translations.xml b/desktop/gnome/apps/gitg/translations.xml
new file mode 100644
index 0000000000..06a00a4568
--- /dev/null
+++ b/desktop/gnome/apps/gitg/translations.xml
@@ -0,0 +1,13 @@
+
+
+
+ gitg
+ Git sürüm kontrol sistemi için bir GTK tabanlı bir arayüz
+ Git sürüm kontrol sistemi için bir GTK tabanlı bir arayüz
+
+
+
+ gitg-devel
+ devhelp için geliştirme dosyaları
+
+
diff --git a/desktop/gnome/apps/gnome-books/actions.py b/desktop/gnome/apps/gnome-books/actions.py
new file mode 100644
index 0000000000..cb466a0763
--- /dev/null
+++ b/desktop/gnome/apps/gnome-books/actions.py
@@ -0,0 +1,18 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Licensed under the GNU General Public License, version 2.
+# See the file http://www.gnu.org/copyleft/gpl.txt.
+
+from pisi.actionsapi import mesontools, pisitools, shelltools
+
+def setup():
+ mesontools.configure()
+
+def build():
+ mesontools.build()
+
+def install():
+ mesontools.install()
+
+ pisitools.dodoc("AUTHORS", "ChangeLog", "README*", "NEWS")
diff --git a/desktop/gnome/apps/gnome-books/files/51.patch b/desktop/gnome/apps/gnome-books/files/51.patch
new file mode 100644
index 0000000000..b256cbb5e0
--- /dev/null
+++ b/desktop/gnome/apps/gnome-books/files/51.patch
@@ -0,0 +1,1238 @@
+From 87623fc7161855a8ae67764c61a91384baf1f25d Mon Sep 17 00:00:00 2001
+From: Carlos Garnacho
+Date: Sun, 17 Jan 2021 16:21:26 +0100
+Subject: [PATCH 01/12] search: Don't inspect Tracker settings
+
+In a sandboxed environment it's no possible (or shouldn't be!) to
+inspect tracker settings. This is just used to include results from
+custom folders in addition to Documents/Downloads/Desktop folders,
+however these are not accessible within the sandbox anymore.
+
+Drop this code, and stick to the given XDG folders, if support for
+custom folders is desirable, should go through the
+org.freedesktop.Tracker3.Miner.Files.Index interface.
+---
+ src/search.js | 34 ----------------------------------
+ 1 file changed, 34 deletions(-)
+
+diff --git a/src/search.js b/src/search.js
+index b8e9b593..1a25f3ed 100644
+--- a/src/search.js
++++ b/src/search.js
+@@ -286,9 +286,6 @@ var SearchSourceStock = {
+ LOCAL: 'local'
+ };
+
+-const TRACKER_SCHEMA = 'org.freedesktop.Tracker.Miner.Files';
+-const TRACKER_KEY_RECURSIVE_DIRECTORIES = 'index-recursive-directories';
+-
+ const Source = new Lang.Class({
+ Name: 'Source',
+
+@@ -303,36 +300,6 @@ const Source = new Lang.Class({
+ this.builtin = params.builtin;
+ },
+
+- _getTrackerLocations: function() {
+- let settings = new Gio.Settings({ schema_id: TRACKER_SCHEMA });
+- let locations = settings.get_strv(TRACKER_KEY_RECURSIVE_DIRECTORIES);
+- let files = [];
+-
+- locations.forEach(Lang.bind(this,
+- function(location) {
+- // ignore special XDG placeholders, since we handle those internally
+- if (location[0] == '&' || location[0] == '$')
+- return;
+-
+- let trackerFile = Gio.file_new_for_commandline_arg(location);
+-
+- // also ignore XDG locations if they are present with their full path
+- for (let idx = 0; idx < GLib.UserDirectory.N_DIRECTORIES; idx++) {
+- let path = GLib.get_user_special_dir(idx);
+- if (!path)
+- continue;
+-
+- let file = Gio.file_new_for_path(path);
+- if (trackerFile.equal(file))
+- return;
+- }
+-
+- files.push(trackerFile);
+- }));
+-
+- return files;
+- },
+-
+ _getBuiltinLocations: function() {
+ let files = [];
+ let xdgDirs = [GLib.UserDirectory.DIRECTORY_DESKTOP,
+@@ -351,7 +318,6 @@ const Source = new Lang.Class({
+
+ _buildFilterLocal: function() {
+ let locations = this._getBuiltinLocations();
+- locations = locations.concat(this._getTrackerLocations());
+
+ let filters = [];
+ locations.forEach(Lang.bind(this,
+--
+GitLab
+
+
+From 26597b99b69529b8bb733664ce8904ac5ebcd630 Mon Sep 17 00:00:00 2001
+From: Carlos Garnacho
+Date: Sun, 17 Jan 2021 16:26:05 +0100
+Subject: [PATCH 02/12] application: Drop unused import
+
+This library does not exist anymore in Tracker3, and is besides unused
+here.
+---
+ src/application.js | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/src/application.js b/src/application.js
+index 07af5471..67b87247 100644
+--- a/src/application.js
++++ b/src/application.js
+@@ -30,7 +30,6 @@ const Gtk = imports.gi.Gtk;
+ const GLib = imports.gi.GLib;
+ const GObject = imports.gi.GObject;
+ const Tracker = imports.gi.Tracker;
+-const TrackerControl = imports.gi.TrackerControl;
+
+ const ChangeMonitor = imports.changeMonitor;
+ const Format = imports.format;
+--
+GitLab
+
+
+From 853664968e82d00dadc356cf6c4ec3ead924a988 Mon Sep 17 00:00:00 2001
+From: Carlos Garnacho
+Date: Sun, 17 Jan 2021 16:41:57 +0100
+Subject: [PATCH 03/12] notification: Delete indexing notification
+
+This relies on libtracker-control, which is no longer a thing. It
+might make sense to track indexing state, but that goes together
+with requests to index specific locations.
+---
+ src/notifications.js | 122 -------------------------------------------
+ 1 file changed, 122 deletions(-)
+
+diff --git a/src/notifications.js b/src/notifications.js
+index cc46bdcd..32d7c1a6 100644
+--- a/src/notifications.js
++++ b/src/notifications.js
+@@ -22,7 +22,6 @@
+ const Gd = imports.gi.Gd;
+ const Gettext = imports.gettext;
+ const Gtk = imports.gi.Gtk;
+-const TrackerControl = imports.gi.TrackerControl;
+ const _ = imports.gettext.gettext;
+
+ const Application = imports.application;
+@@ -166,124 +165,6 @@ var PrintNotification = new Lang.Class({
+ }
+ });
+
+-const REMOTE_MINER_TIMEOUT = 10; // seconds
+-const TRACKER_MINER_FILES_NAME = 'org.freedesktop.Tracker1.Miner.Files';
+-
+-const IndexingNotification = new Lang.Class({
+- Name: 'IndexingNotification',
+-
+- _init: function() {
+- this._closed = false;
+-
+- try {
+- this._manager = TrackerControl.MinerManager.new_full(false);
+- this._manager.connect('miner-progress', Lang.bind(this, this._checkNotification));
+- } catch(e) {
+- logError(e, 'Unable to create a TrackerMinerManager, indexing progress ' +
+- 'notification won\'t work');
+- return;
+- }
+-
+- Application.application.connect('miners-changed', Lang.bind(this, this._checkNotification));
+- Application.modeController.connect('window-mode-changed', Lang.bind(this, this._checkNotification));
+- },
+-
+- _checkNotification: function() {
+- if (Application.modeController.getWindowMode() == WindowMode.WindowMode.PREVIEW_EV) {
+- this._destroy(false);
+- return;
+- }
+-
+- let isIndexingLocal = false;
+-
+- if (this._manager) {
+- let running = this._manager.get_running();
+- if (running.indexOf(TRACKER_MINER_FILES_NAME) != -1) {
+- let [res, status, progress, time] = this._manager.get_status(TRACKER_MINER_FILES_NAME);
+-
+- if (progress < 1)
+- isIndexingLocal = true;
+- }
+- }
+-
+- if (isIndexingLocal) {
+- this._display(_("Your documents are being indexed"),
+- _("Some documents might not be available during this process"));
+- } else {
+- this._destroy(false);
+- }
+- },
+-
+- _buildWidget: function() {
+- this.widget = new Gtk.Grid({ orientation: Gtk.Orientation.HORIZONTAL,
+- column_spacing: 12 });
+-
+- let spinner = new Gtk.Spinner({ width_request: 16,
+- height_request: 16 });
+- spinner.start();
+- this.widget.add(spinner);
+-
+- let labels = new Gtk.Grid({ orientation: Gtk.Orientation.VERTICAL,
+- row_spacing: 3 });
+- this.widget.add(labels);
+-
+- this._primaryLabel = new Gtk.Label({ halign: Gtk.Align.START });
+- labels.add(this._primaryLabel);
+-
+- this._secondaryLabel = new Gtk.Label({ halign: Gtk.Align.START });
+- this._secondaryLabel.get_style_context().add_class('dim-label');
+- labels.add(this._secondaryLabel);
+-
+- let close = new Gtk.Button({ image: new Gtk.Image({ icon_name: 'window-close-symbolic',
+- pixel_size: 16,
+- margin_top: 2,
+- margin_bottom: 2 }),
+- valign: Gtk.Align.CENTER,
+- focus_on_click: false,
+- relief: Gtk.ReliefStyle.NONE });
+- close.connect('clicked', Lang.bind(this,
+- function() {
+- this._destroy(true);
+- }));
+- this.widget.add(close);
+-
+- Application.notificationManager.addNotification(this);
+- },
+-
+- _update: function(primaryText, secondaryText) {
+- this._primaryLabel.label = primaryText;
+- this._secondaryLabel.label = secondaryText;
+-
+- if (secondaryText) {
+- this._primaryLabel.vexpand = false;
+- this._secondaryLabel.show();
+- } else {
+- this._primaryLabel.vexpand = true;
+- this._secondaryLabel.hide();
+- }
+- },
+-
+- _display: function(primaryText, secondaryText) {
+- if (this._closed) {
+- return;
+- }
+-
+- if (!this.widget)
+- this._buildWidget();
+-
+- this._update(primaryText, secondaryText);
+- },
+-
+- _destroy: function(closed) {
+- if (this.widget) {
+- this.widget.destroy();
+- this.widget = null;
+- }
+-
+- this._closed = closed;
+- }
+-});
+-
+ var NotificationManager = new Lang.Class({
+ Name: 'NotificationManager',
+ Extends: Gtk.Revealer,
+@@ -300,9 +181,6 @@ var NotificationManager = new Lang.Class({
+ row_spacing: 6 });
+
+ frame.add(this._grid);
+-
+- // add indexing monitor notification
+- this._indexingNotification = new IndexingNotification();
+ },
+
+ addNotification: function(notification) {
+--
+GitLab
+
+
+From 058389819f9a5ea2f633e4097db3cc444b7270db Mon Sep 17 00:00:00 2001
+From: Carlos Garnacho
+Date: Sun, 17 Jan 2021 16:54:46 +0100
+Subject: [PATCH 04/12] application: Use
+ org.freedesktop.Tracker3.Miner.Files.Control for index control
+
+This new request allows prioritising graphs on index folders, so request
+the tracker:Documents graph on the Documents folder here.
+---
+ src/application.js | 31 +++++++++++++++----------------
+ 1 file changed, 15 insertions(+), 16 deletions(-)
+
+diff --git a/src/application.js b/src/application.js
+index 67b87247..a6ac6f0f 100644
+--- a/src/application.js
++++ b/src/application.js
+@@ -69,20 +69,21 @@ var trackerCollectionsController = null;
+ var trackerDocumentsController = null;
+ var trackerSearchController = null;
+
+-const TrackerExtractPriorityIface = ' \
+- \
+- \
+- \
+- \
++const TrackerMinerFilesIndexIface = ' \
++ \
++ \
++ \
++ \
++ \
+ \
+ \
+ ';
+
+-var TrackerExtractPriorityProxy = Gio.DBusProxy.makeProxyWrapper(TrackerExtractPriorityIface);
+-function TrackerExtractPriority() {
+- return new TrackerExtractPriorityProxy(Gio.DBus.session,
+- 'org.freedesktop.Tracker1.Miner.Extract',
+- '/org/freedesktop/Tracker1/Extract/Priority');
++var TrackerMinerFilesControlProxy = Gio.DBusProxy.makeProxyWrapper(TrackerMinerFilesIndexIface);
++function TrackerMinerFilesControl() {
++ return new TrackerMinerFilesControlProxy(Gio.DBus.session,
++ 'org.freedesktop.Tracker3.Miner.Files.Control',
++ '/org/freedesktop/Tracker3/Miner/Files/Index');
+ }
+
+ const MINER_REFRESH_TIMEOUT = 60; /* seconds */
+@@ -97,7 +98,7 @@ var Application = new Lang.Class({
+ _init: function() {
+ this.minersRunning = [];
+ this._activationTimestamp = Gdk.CURRENT_TIME;
+- this._extractPriority = null;
++ this._minerControl = null;
+
+ let appid;
+ GLib.set_application_name(_("Books"));
+@@ -219,8 +220,9 @@ var Application = new Lang.Class({
+ this._mainWindow.connect('destroy', Lang.bind(this, this._onWindowDestroy));
+
+ try {
+- this._extractPriority = TrackerExtractPriority();
+- this._extractPriority.SetRdfTypesRemote(['nfo:Document']);
++ this._minerControl = TrackerMinerFilesControl();
++ this._minerControl.IndexLocationRemote(GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DOCUMENTS),
++ ['tracker:Documents'], []);
+ } catch (e) {
+ logError(e, 'Unable to connect to the tracker extractor');
+ }
+@@ -264,9 +266,6 @@ var Application = new Lang.Class({
+ modeController.setWindowMode(WindowMode.WindowMode.NONE);
+ selectionController.setSelection(null);
+ notificationManager = null;
+-
+- if (this._extractPriority)
+- this._extractPriority.ClearRdfTypesRemote();
+ },
+
+ _onWindowDestroy: function(window) {
+--
+GitLab
+
+
+From d51f0d4eecd83e5f4c73668bf2577afaeaa16b96 Mon Sep 17 00:00:00 2001
+From: Carlos Garnacho
+Date: Sun, 17 Jan 2021 17:02:04 +0100
+Subject: [PATCH 05/12] changeMonitor: Rewrite to use TrackerNotifier
+
+The GraphUpdated DBus signal became superseded by TrackerNotifier
+in 2.x, and completely removed on 3.x. Use TrackerNotifier here
+for event tracking.
+---
+ src/changeMonitor.js | 160 +++++--------------------------------------
+ 1 file changed, 19 insertions(+), 141 deletions(-)
+
+diff --git a/src/changeMonitor.js b/src/changeMonitor.js
+index a9e3a5ba..0172cd65 100644
+--- a/src/changeMonitor.js
++++ b/src/changeMonitor.js
+@@ -23,176 +23,54 @@ const Gio = imports.gi.Gio;
+ const Lang = imports.lang;
+ const Mainloop = imports.mainloop;
+ const Signals = imports.signals;
++const Tracker = imports.gi.Tracker;
+
+ const Application = imports.application;
+
+-const TrackerResourcesServiceIface = ' \
+- \
+- \
+- \
+- \
+- \
+- \
+- \
+-';
+-
+-var TrackerResourcesServiceProxy = Gio.DBusProxy.makeProxyWrapper(TrackerResourcesServiceIface);
+-function TrackerResourcesService() {
+- return new TrackerResourcesServiceProxy(Gio.DBus.session,
+- 'org.freedesktop.Tracker1',
+- '/org/freedesktop/Tracker1/Resources');
+-}
+-
+ var ChangeEventType = {
+ CHANGED: 0,
+ CREATED: 1,
+ DELETED: 2
+ };
+
+-const _RDF_TYPE = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type";
+-
+ const ChangeEvent = new Lang.Class({
+ Name: 'ChangeEvent',
+
+- _init: function(urnId, predicateId, isDelete) {
+- this.urnId = urnId;
+- this.predicateId = predicateId;
+-
+- if (isDelete)
+- this.type = ChangeEventType.DELETED;
+- else
+- this.type = ChangeEventType.CREATED;
+- },
+-
+- setResolvedValues: function(urn, predicate) {
++ _init: function(type, urn) {
+ this.urn = urn;
+- this.predicate = predicate;
+
+- if (predicate != _RDF_TYPE)
++ if (type == Tracker.NotifierEventType.CREATE)
++ this.type = ChangeEventType.CREATED;
++ else if (type == Tracker.NotifierEventType.DELETE)
++ this.type = ChangeEventType.DELETED;
++ else if (type == Tracker.NotifierEventType.UPDATE)
+ this.type = ChangeEventType.CHANGED;
+- },
+-
+- merge: function(event) {
+- // deletions or creations override the current type
+- if (event.type == ChangeEventType.DELETED ||
+- event.type == ChangeEventType.CREATED) {
+- this.type = event.type;
+- }
+ }
+ });
+
+-const CHANGE_MONITOR_TIMEOUT = 500; // msecs
+-const CHANGE_MONITOR_MAX_ITEMS = 500; // items
+-
+ var TrackerChangeMonitor = new Lang.Class({
+ Name: 'TrackerChangeMonitor',
+
+ _init: function() {
+- this._pendingChanges = {};
+- this._unresolvedIds = {};
+-
+- this._pendingEvents = [];
+- this._pendingEventsId = 0;
+-
+- this._resourceService = new TrackerResourcesService();
+- this._resourceService.connectSignal('GraphUpdated', Lang.bind(this, this._onGraphUpdated));
+- },
+-
+- _onGraphUpdated: function(proxy, senderName, [className, deleteEvents, insertEvents]) {
+- deleteEvents.forEach(Lang.bind(this,
+- function(event) {
+- this._addPendingEvent(event, true);
+- }));
+-
+- insertEvents.forEach(Lang.bind(this,
+- function(event) {
+- this._addPendingEvent(event, false);
+- }));
+- },
+-
+- _addPendingEvent: function(event, isDelete) {
+- if (this._pendingEventsId != 0)
+- Mainloop.source_remove(this._pendingEventsId);
+-
+- this._unresolvedIds[event[1]] = event[1];
+- this._unresolvedIds[event[2]] = event[2];
+- this._pendingEvents.push(new ChangeEvent(event[1], event[2], isDelete));
+-
+- if (this._pendingEvents.length >= CHANGE_MONITOR_MAX_ITEMS)
+- this._processEvents();
+- else
+- this._pendingEventsId =
+- Mainloop.timeout_add(CHANGE_MONITOR_TIMEOUT, Lang.bind(this, this._processEvents));
+- },
+-
+- _processEvents: function() {
+- let events = this._pendingEvents;
+- let idTable = this._unresolvedIds;
+-
+- this._pendingEventsId = 0;
+- this._pendingEvents = [];
+- this._unresolvedIds = {};
+-
+- let sparql = 'SELECT';
+- Object.keys(idTable).forEach(Lang.bind(this,
+- function(unresolvedId) {
+- sparql += (' tracker:uri(%d)').format(unresolvedId);
+- }));
+- sparql += ' {}';
+-
+- // resolve all the unresolved IDs we got so far
+- Application.connectionQueue.add(sparql, null, Lang.bind(this,
+- function(object, res) {
+- let cursor = object.query_finish(res);
+-
+- cursor.next_async(null, Lang.bind(this,
+- function(object, res) {
+- let valid = false;
+- try {
+- valid = cursor.next_finish(res);
+- } catch(e) {
+- logError(e, 'Unable to resolve item URNs for graph changes');
+- }
+-
+- if (valid) {
+- let idx = 0;
+- Object.keys(idTable).forEach(Lang.bind(this,
+- function(unresolvedId) {
+- idTable[unresolvedId] = cursor.get_string(idx)[0];
+- idx++;
+- }));
+-
+- this._sendEvents(events, idTable);
+- }
+-
+- cursor.close();
+- }));
+- }));
+-
+- return false;
++ this._notifier = Application.connection.create_notifier();
++ this._notifier.signal_subscribe(Gio.DBus.session,
++ 'org.freedesktop.Tracker3.Miner.Files',
++ null,
++ 'http://tracker.api.gnome.org/ontology/v3/tracker#Documents');
++ this._notifier.connect('events', Lang.bind(this, this._onNotifierEvents));
+ },
+
+- _addEvent: function(event) {
+- let urn = event.urn;
+- let oldEvent = this._pendingChanges[urn];
+-
+- if (oldEvent != null) {
+- oldEvent.merge(event);
+- this._pendingChanges[urn] = oldEvent;
+- } else {
+- this._pendingChanges[urn] = event;
+- }
+- },
++ _onNotifierEvents: function(notifier, service, graph, events) {
++ let pendingChanges = {};
+
+- _sendEvents: function(events, idTable) {
+ events.forEach(Lang.bind(this,
+ function(event) {
+- event.setResolvedValues(idTable[event.urnId], idTable[event.predicateId]);
+- this._addEvent(event);
++ let urn = event.get_urn();
++ let changeEvent = new ChangeEvent(event.get_event_type(), urn);
++ pendingChanges[urn] = changeEvent;
+ }));
+
+- this.emit('changes-pending', this._pendingChanges);
+- this._pendingChanges = {};
++ this.emit('changes-pending', pendingChanges);
+ }
+ });
+ Signals.addSignalMethods(TrackerChangeMonitor.prototype);
+--
+GitLab
+
+
+From 3e066e63742f88eab0f131faa6bc7e70fc78da3b Mon Sep 17 00:00:00 2001
+From: Carlos Garnacho
+Date: Sun, 17 Jan 2021 17:03:14 +0100
+Subject: [PATCH 06/12] Port queries to Tracker3
+
+Use new Tracker.SparqlConnection constructors, and rework the queries
+so that it:
+
+1) Observes the data layout in use in Tracker Miners 3.x
+2) Uses a private database for the data considered writeable:
+ - collections
+ - titles
+
+All queries happen on the local connection, optionally including data
+from tracker-miner-fs via SERVICE{} clauses, this is just not the case
+for collection queries, since collections are stored in the private
+books database.
+---
+ meson.build | 4 +-
+ src/application.js | 8 +++-
+ src/documents.js | 2 +-
+ src/main.js | 3 +-
+ src/query.js | 99 ++++++++++++++++++++++++++++++----------
+ src/search.js | 8 ++--
+ src/trackerController.js | 4 +-
+ src/trackerUtils.js | 2 +-
+ 8 files changed, 92 insertions(+), 38 deletions(-)
+
+diff --git a/meson.build b/meson.build
+index afd97aa4..f608bfc1 100644
+--- a/meson.build
++++ b/meson.build
+@@ -50,7 +50,6 @@ endforeach
+ add_project_arguments('-DHAVE_CONFIG_H', language: 'c')
+
+ evince_req_version = '>= 3.13.3'
+-tracker_req_version = '>= 0.17.3'
+
+ gjs_dep = dependency('gjs-1.0', version: '>= 1.48.0')
+ gjs_console = gjs_dep.get_pkgconfig_variable('gjs_console')
+@@ -63,8 +62,7 @@ books_deps = [
+ dependency('gnome-desktop-3.0'),
+ dependency('gobject-introspection-1.0', version: '>= 1.31.6'),
+ dependency('gtk+-3.0', version: '>= 3.22.15'),
+- dependency('tracker-control-2.0', version: tracker_req_version),
+- dependency('tracker-sparql-2.0', version: tracker_req_version),
++ dependency('tracker-sparql-3.0'),
+ dependency('webkit2gtk-4.0', version: '>= 2.6.0'),
+ cc.find_library('m')
+ ]
+diff --git a/src/application.js b/src/application.js
+index a6ac6f0f..c3ebf933 100644
+--- a/src/application.js
++++ b/src/application.js
+@@ -175,9 +175,13 @@ var Application = new Lang.Class({
+
+ // connect to tracker
+ try {
+- connection = Tracker.SparqlConnection.get(null);
++ let cacheDir = GLib.build_filenamev([GLib.get_user_cache_dir(), 'org.gnome.Books', 'db']);
++ let nepomuk = Tracker.sparql_get_ontology_nepomuk();
++ connection = Tracker.SparqlConnection.new(Tracker.SparqlConnectionFlags.NONE,
++ Gio.File.new_for_path(cacheDir),
++ nepomuk, null);
+ } catch (e) {
+- logError(e, 'Unable to connect to the tracker database');
++ logError(e, 'Unable to set up the tracker database');
+ return;
+ }
+
+diff --git a/src/documents.js b/src/documents.js
+index b6aea652..a2f15c6b 100644
+--- a/src/documents.js
++++ b/src/documents.js
+@@ -803,7 +803,7 @@ const DocCommon = new Lang.Class({
+ let retval = '';
+
+ if (this.collection)
+- retval = '{ ?urn nie:isPartOf <' + this.id + '> }';
++ retval = '{ ?urn nie:isLogicalPartOf <' + this.id + '> }';
+
+ return retval;
+ },
+diff --git a/src/main.js b/src/main.js
+index 57f62019..d6624940 100644
+--- a/src/main.js
++++ b/src/main.js
+@@ -33,8 +33,7 @@ pkg.require({ 'EvinceDocument': '3.0',
+ 'GLib': '2.0',
+ 'Gtk': '3.0',
+ 'GObject': '2.0',
+- 'Tracker': '2.0',
+- 'TrackerControl': '2.0',
++ 'Tracker': '3.0',
+ 'WebKit2': '4.0' });
+
+ const Application = imports.application;
+diff --git a/src/query.js b/src/query.js
+index ce7928cd..fa7c5ca1 100644
+--- a/src/query.js
++++ b/src/query.js
+@@ -94,9 +94,6 @@ var QueryBuilder = new Lang.Class({
+ part += this._buildOptional();
+
+ if ((flags & QueryFlags.UNFILTERED) == 0) {
+- if (global)
+- part += this._context.documentManager.getWhere();
+-
+ part += this._buildFilterString(currentType, flags, ftsQuery.length > 0);
+ }
+
+@@ -139,12 +136,25 @@ var QueryBuilder = new Lang.Class({
+
+ // put all the clauses in an UNION
+ whereSparql += whereParts.join(' UNION ');
++
+ whereSparql += ' }';
+
+ return whereSparql;
+ },
+
+ _buildQueryInternal: function(global, flags, offsetController, sortBy) {
++ let selectClauses =
++ ' (nie:isStoredAs(?urn) AS ?uri) ' +
++ ' (nfo:fileName(?urn) AS ?filename) ' +
++ ' (nie:mimeType(?urn) AS ?mimetype) ' +
++ ' (nie:title(?urn) AS ?title) ' +
++ ' (tracker:coalesce(nco:fullname(?creator), nco:fullname(?publisher), \'\') AS ?author) ' +
++ ' (nie:contentLastModified(?urn) AS ?mtime) ' +
++ ' (nao:identifier(?urn) AS ?identifier) ' +
++ ' (rdf:type(?urn) AS ?type) ' +
++ ' (nie:dataSource(?urn) AS ?datasource ) ' +
++ ' (( EXISTS { ?urn nco:contributor ?contributor FILTER ( ?contributor != ?creator ) } ) AS ?shared) ' +
++ ' (nie:contentCreated(?urn) AS ?created) ';
+ let whereSparql = this._buildWhere(global, flags);
+ let tailSparql = '';
+
+@@ -179,19 +189,44 @@ var QueryBuilder = new Lang.Class({
+ }
+
+ let sparql =
+- 'SELECT DISTINCT ?urn ' + // urn
+- 'nie:url(?urn) ' + // uri
+- 'nfo:fileName(?urn) AS ?filename ' + // filename
+- 'nie:mimeType(?urn)' + // mimetype
+- 'nie:title(?urn) AS ?title ' + // title
+- 'tracker:coalesce(nco:fullname(?creator), nco:fullname(?publisher), \'\') AS ?author ' + // author
+- 'tracker:coalesce(nfo:fileLastModified(?urn), nie:contentLastModified(?urn)) AS ?mtime ' + // mtime
+- 'nao:identifier(?urn) ' + // identifier
+- 'rdf:type(?urn) ' + // type
+- 'nie:dataSource(?urn) ' + // resource URN
+- '( EXISTS { ?urn nco:contributor ?contributor FILTER ( ?contributor != ?creator ) } ) ' + // shared
+- 'tracker:coalesce(nfo:fileCreated(?urn), nie:contentCreated(?urn)) ' + // date created
+- whereSparql + tailSparql;
++ 'SELECT ?urn ' +
++ ' ?uri ' +
++ ' ?filename ' +
++ ' ?mimetype ' +
++ ' COALESCE (?localTitle, ?title) ' +
++ ' ?author ' +
++ ' ?mtime ' +
++ ' ?identifier ' +
++ ' ?type ' +
++ ' ?datasource ' +
++ ' ?shared ' +
++ ' ?created ' +
++ 'WHERE { ';
++
++ // Collections queries are local
++ if (flags & QueryFlags.COLLECTIONS) {
++ sparql +=
++ 'SELECT DISTINCT ?urn ' +
++ selectClauses +
++ whereSparql +
++ tailSparql;
++ } else {
++ sparql +=
++ 'SERVICE {' +
++ ' GRAPH tracker:Documents { ' +
++ ' SELECT DISTINCT ?urn ' +
++ selectClauses +
++ whereSparql +
++ tailSparql +
++ ' }' +
++ '}' +
++ 'OPTIONAL { ?urn nie:title ?localTitle } . ';
++
++ if (global && (flags & QueryFlags.UNFILTERED) == 0)
++ sparql += this._context.documentManager.getWhere();
++ }
++
++ sparql += '}';
+
+ return sparql;
+ },
+@@ -208,8 +243,18 @@ var QueryBuilder = new Lang.Class({
+ },
+
+ buildCountQuery: function(flags) {
+- let sparql = 'SELECT DISTINCT COUNT(?urn) ' +
+- this._buildWhere(true, flags);
++ let sparql;
++ if (flags & QueryFlags.COLLECTIONS) {
++ sparql = 'SELECT DISTINCT COUNT(?urn) AS ?c ' +
++ this._buildWhere(true, flags);
++ } else {
++ sparql = 'SELECT ?c {' +
++ ' SERVICE { ' +
++ ' SELECT DISTINCT COUNT(?urn) AS ?c ' +
++ this._buildWhere(true, flags) +
++ ' }' +
++ '}';
++ }
+
+ return this._createQuery(sparql);
+ },
+@@ -219,8 +264,8 @@ var QueryBuilder = new Lang.Class({
+ let sparql =
+ ('SELECT ' +
+ '?urn ' +
+- 'tracker:coalesce(nfo:fileLastModified(?urn), nie:contentLastModified(?urn)) AS ?mtime ' +
+- 'WHERE { ?urn nie:isPartOf ?collUrn } ' +
++ 'nie:contentLastModified(?urn) AS ?mtime ' +
++ 'WHERE { ?urn nie:isLogicalPartOf ?collUrn } ' +
+ 'ORDER BY DESC (?mtime)' +
+ 'LIMIT 4').replace(/\?collUrn/, '<' + resource + '>');
+
+@@ -232,7 +277,7 @@ var QueryBuilder = new Lang.Class({
+ let sparql =
+ ('SELECT ' +
+ '?urn ' +
+- 'WHERE { ?urn a nfo:DataContainer . ?docUrn nie:isPartOf ?urn }'
++ 'WHERE { ?urn a nfo:DataContainer . ?docUrn nie:isLogicalPartOf ?urn }'
+ ).replace(/\?docUrn/, '<' + resource + '>');
+
+ return this._createQuery(sparql);
+@@ -240,15 +285,21 @@ var QueryBuilder = new Lang.Class({
+
+ // adds or removes the given item to the given collection
+ buildSetCollectionQuery: function(itemUrn, collectionUrn, setting) {
+- let sparql = ('%s { <%s> nie:isPartOf <%s> }'
+- ).format((setting ? 'INSERT' : 'DELETE'), itemUrn, collectionUrn);
++ let sparql;
++ if (setting) {
++ sparql = ('INSERT DATA { <%s> a nie:InformationElement; nie:isLogicalPartOf <%s> }'
++ ).format(itemUrn, collectionUrn);
++ } else {
++ sparql = ('DELETE DATA { <%s> nie:isLogicalPartOf <%s> }'
++ ).format(itemUrn, collectionUrn);
++ }
+ return this._createQuery(sparql);
+ },
+
+ // bumps the mtime to current time for the given resource
+ buildUpdateMtimeQuery: function(resource) {
+ let time = GdPrivate.iso8601_from_timestamp(GLib.get_real_time() / GLib.USEC_PER_SEC);
+- let sparql = ('INSERT OR REPLACE { <%s> nie:contentLastModified \"%s\" }'
++ let sparql = ('INSERT OR REPLACE { <%s> a nie:InformationElement ; nie:contentLastModified \"%s\" }'
+ ).format(resource, time);
+
+ return this._createQuery(sparql);
+diff --git a/src/search.js b/src/search.js
+index 1a25f3ed..0bcc6d2c 100644
+--- a/src/search.js
++++ b/src/search.js
+@@ -19,6 +19,8 @@
+ *
+ */
+
++imports.gi.versions.Tracker = '3.0';
++
+ const Application = imports.application;
+ const Documents = imports.documents;
+ const Manager = imports.manager;
+@@ -126,11 +128,11 @@ const SearchTypeManager = new Lang.Class({
+ this.addItem(new SearchType({ id: SearchTypeStock.EBOOKS,
+ name: _("e-Books"),
+ filter: '(nie:mimeType(?urn) IN (\"application/epub+zip\", \"application/x-mobipocket-ebook\", \"application/vnd.amazon.mobi8-ebook\", \"application/x-fictionbook+xml\", \"application/x-zip-compressed-fb2\", \"image/vnd.djvu+multipage\"))',
+- where: '?urn rdf:type nfo:FileDataObject .' }));
++ where: '?urn rdf:type nfo:EBook .' }));
+ this.addItem(new SearchType({ id: SearchTypeStock.COMICS,
+ name: _("Comics"),
+ filter: '(nie:mimeType(?urn) IN (\"application/x-cbr\", \"application/vnd.comicbook-rar\", \"application/x-cbz\", \"application/vnd.comicbook+zip\", \"application/x-cbt\", \"application/x-cb7\"))',
+- where: '?urn rdf:type nfo:FileDataObject .' }));
++ where: '?urn rdf:type nfo:EBook .' }));
+
+
+ this.setActiveItemById(SearchTypeStock.ALL);
+@@ -322,7 +324,7 @@ const Source = new Lang.Class({
+ let filters = [];
+ locations.forEach(Lang.bind(this,
+ function(location) {
+- filters.push('(fn:contains (nie:url(?urn), "%s"))'.format(location.get_uri()));
++ filters.push('(fn:contains (nie:isStoredAs(?urn), "%s"))'.format(location.get_uri()));
+ }));
+
+ filters.push('(fn:starts-with (nao:identifier(?urn), "gb:collection:local:"))');
+diff --git a/src/trackerController.js b/src/trackerController.js
+index e45f3a7f..17507e03 100644
+--- a/src/trackerController.js
++++ b/src/trackerController.js
+@@ -89,10 +89,10 @@ var TrackerConnectionQueue = new Lang.Class({
+ Application.connection.query_async(params.query, params.cancellable,
+ Lang.bind(this, this._queueCollector, params));
+ else if (params.queryType == QueryType.UPDATE)
+- Application.connection.update_async(params.query, GLib.PRIORITY_DEFAULT, params.cancellable,
++ Application.connection.update_async(params.query, params.cancellable,
+ Lang.bind(this, this._queueCollector, params));
+ else if (params.queryType == QueryType.UPDATE_BLANK)
+- Application.connection.update_blank_async(params.query, GLib.PRIORITY_DEFAULT, params.cancellable,
++ Application.connection.update_blank_async(params.query, params.cancellable,
+ Lang.bind(this, this._queueCollector, params));
+ },
+
+diff --git a/src/trackerUtils.js b/src/trackerUtils.js
+index a1653fed..c98a3345 100644
+--- a/src/trackerUtils.js
++++ b/src/trackerUtils.js
+@@ -24,7 +24,7 @@ const Lang = imports.lang;
+ const Application = imports.application;
+
+ function setEditedName(newTitle, docId, callback) {
+- let sparql = ('INSERT OR REPLACE { <%s> nie:title \"%s\" }'.format(docId, newTitle));
++ let sparql = ('INSERT OR REPLACE { <%s> a nie:InformationElement ; nie:title \"%s\" }'.format(docId, newTitle));
+
+ Application.connectionQueue.update(sparql, null,
+ function(object, res) {
+--
+GitLab
+
+
+From 1e915784f2009f280757eb0601446fa44ec38926 Mon Sep 17 00:00:00 2001
+From: Carlos Garnacho
+Date: Tue, 26 Jan 2021 14:18:53 +0100
+Subject: [PATCH 07/12] flatpak: Update reference to use Tracker3
+
+Use the Tracker3 policy to specify the accessed graph, drop
+the builtin tracker copy, and change tracker-miners build parameters
+so we can run our own internal copy.
+
+In code, first check whether there is access to the stock miner,
+then attempt this internal miner. This is a fallback for systems that
+do not have tracker3 installed.
+---
+ flatpak/org.gnome.Books.json | 37 ++++++++++--------------------------
+ src/application.js | 32 +++++++++++++++++++++++--------
+ src/query.js | 4 ++--
+ 3 files changed, 36 insertions(+), 37 deletions(-)
+
+diff --git a/flatpak/org.gnome.Books.json b/flatpak/org.gnome.Books.json
+index cac5df46..6c625776 100644
+--- a/flatpak/org.gnome.Books.json
++++ b/flatpak/org.gnome.Books.json
+@@ -17,10 +17,8 @@
+ "--filesystem=xdg-documents:ro", "--filesystem=xdg-download:ro",
+ /* Needs to talk to the network: */
+ "--share=network",
+- /* Tracker D-Bus access */
+- "--talk-name=org.freedesktop.Tracker1",
+- "--talk-name=org.freedesktop.Tracker1.Miner.Extract",
+- "--env=TRACKER_SPARQL_BACKEND=bus",
++ /* Tracker3 graph access */
++ "--add-policy=Tracker3.dbus:org.freedesktop.Tracker3.Miner.Files=tracker:Documents",
+ /* For the WebP loader */
+ "--env=GDK_PIXBUF_MODULE_FILE=/app/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache",
+ /* Needed for dconf to work */
+@@ -125,19 +123,6 @@
+ }
+ ]
+ },
+- {
+- "name": "tracker",
+- "buildsystem": "meson",
+- "cleanup": [ "/bin", "/etc", "/lib/systemd", "/libexec", "/share/dbus-1/services" ],
+- "config-opts": [ "--default-library=shared", "-Dbash-completion=no", "-Ddocs=false", "-Dsystemd_user_services=no" ],
+- "sources": [
+- {
+- "type": "git",
+- "url": "https://gitlab.gnome.org/GNOME/tracker.git",
+- "branch": "tracker-2.3"
+- }
+- ]
+- },
+ {
+ "name": "intltool",
+ "cleanup": [ "*" ],
+@@ -152,20 +137,18 @@
+ {
+ "name": "tracker-miners",
+ "buildsystem": "meson",
+- "cleanup": [ "/bin", "/etc", "/lib/systemd", "/libexec" ],
+- "config-opts": [ "--default-library=shared",
+- "-Dminer_apps=false",
+- "-Dminer_rss=false",
+- "-Dextract=false",
+- "-Dgeneric_media_extractor=none",
+- "-Dwriteback=false",
++ "config-opts": [ "-Ddefault_index_single_directories=&DOWNLOADS",
++ "-Ddefault_index_recursive_directories=&DOCUMENTS",
++ "-Ddomain_prefix=org.gnome.Books",
++ "-Dman=false",
+ "-Dminer_fs=true",
+- "-Ddbus_services=/app/share/dbus-1/services/" ],
++ "-Dminer_fs_cache_location=$XDG_CACHE_HOME/org.gnome.Books/miner/files",
++ "-Dminer_rss=false",
++ "-Dsystemd_user_services=false" ],
+ "sources": [
+ {
+ "type": "git",
+- "url": "https://gitlab.gnome.org/GNOME/tracker-miners.git",
+- "branch": "tracker-miners-2.3"
++ "url": "https://gitlab.gnome.org/GNOME/tracker-miners.git"
+ }
+ ]
+ },
+diff --git a/src/application.js b/src/application.js
+index c3ebf933..7d7493d4 100644
+--- a/src/application.js
++++ b/src/application.js
+@@ -68,6 +68,7 @@ var sourceManager = null;
+ var trackerCollectionsController = null;
+ var trackerDocumentsController = null;
+ var trackerSearchController = null;
++var trackerMinerService = null;
+
+ const TrackerMinerFilesIndexIface = ' \
+ \
+@@ -160,6 +161,11 @@ var Application = new Lang.Class({
+ }
+ },
+
++ _isSandboxed: function() {
++ file = Gio.File.new_for_path("/.flatpak-info");
++ return file.query_exists(null);
++ },
++
+ vfunc_startup: function() {
+ this.parent();
+ String.prototype.format = Format.format;
+@@ -185,6 +191,24 @@ var Application = new Lang.Class({
+ return;
+ }
+
++ if (this._isSandboxed()) {
++ // if the daemon is not available, run our own copy (for the sandboxed case)
++ trackerMinerService = this.get_application_id() + '.Tracker3.Miner.Files';
++ } else {
++ // use global tracker daemon
++ let busConn = Tracker.SparqlConnection.bus_new('org.freedesktop.Tracker3.Miner.Files',
++ null, Gio.DBus.session);
++ trackerMinerService = 'org.freedesktop.Tracker3.Miner.Files';
++
++ try {
++ this._minerControl = TrackerMinerFilesControl();
++ this._minerControl.IndexLocationRemote(GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DOCUMENTS),
++ ['tracker:Documents'], []);
++ } catch (e) {
++ logError(e, 'Unable to connect to the tracker extractor');
++ }
++ }
++
+ connectionQueue = new TrackerController.TrackerConnectionQueue();
+ changeMonitor = new ChangeMonitor.TrackerChangeMonitor();
+
+@@ -222,14 +246,6 @@ var Application = new Lang.Class({
+ notificationManager = new Notifications.NotificationManager();
+ this._mainWindow = new MainWindow.MainWindow(this);
+ this._mainWindow.connect('destroy', Lang.bind(this, this._onWindowDestroy));
+-
+- try {
+- this._minerControl = TrackerMinerFilesControl();
+- this._minerControl.IndexLocationRemote(GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DOCUMENTS),
+- ['tracker:Documents'], []);
+- } catch (e) {
+- logError(e, 'Unable to connect to the tracker extractor');
+- }
+ },
+
+ vfunc_handle_local_options: function(options) {
+diff --git a/src/query.js b/src/query.js
+index fa7c5ca1..4fc94b6b 100644
+--- a/src/query.js
++++ b/src/query.js
+@@ -212,7 +212,7 @@ var QueryBuilder = new Lang.Class({
+ tailSparql;
+ } else {
+ sparql +=
+- 'SERVICE {' +
++ 'SERVICE {' +
+ ' GRAPH tracker:Documents { ' +
+ ' SELECT DISTINCT ?urn ' +
+ selectClauses +
+@@ -249,7 +249,7 @@ var QueryBuilder = new Lang.Class({
+ this._buildWhere(true, flags);
+ } else {
+ sparql = 'SELECT ?c {' +
+- ' SERVICE { ' +
++ ' SERVICE { ' +
+ ' SELECT DISTINCT COUNT(?urn) AS ?c ' +
+ this._buildWhere(true, flags) +
+ ' }' +
+--
+GitLab
+
+
+From df74c08221003d63ef486f5aa7b33b9d2cd01697 Mon Sep 17 00:00:00 2001
+From: Bastien Nocera
+Date: Tue, 16 Feb 2021 11:37:16 +0100
+Subject: [PATCH 08/12] flatpak: Use WIP gjs patch to fix signal reception
+
+See https://gitlab.gnome.org/GNOME/gjs/-/merge_requests/569
+---
+ flatpak/org.gnome.Books.json | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/flatpak/org.gnome.Books.json b/flatpak/org.gnome.Books.json
+index 6c625776..a80bab77 100644
+--- a/flatpak/org.gnome.Books.json
++++ b/flatpak/org.gnome.Books.json
+@@ -180,6 +180,17 @@
+ }
+ ]
+ },
++ {
++ "name": "gjs",
++ "buildsystem": "meson",
++ "sources": [
++ {
++ "type": "git",
++ "url": "https://gitlab.gnome.org/GNOME/gjs.git",
++ "branch": "wip/carlosg/arrays-in-signals"
++ }
++ ]
++ },
+ {
+ "name": "gnome-books",
+ "buildsystem": "meson",
+--
+GitLab
+
+
+From ed61ff9d0689378a0fe39856fed454565c97cb2e Mon Sep 17 00:00:00 2001
+From: Carlos Garnacho
+Date: Tue, 16 Feb 2021 13:04:45 +0100
+Subject: [PATCH 09/12] query: Fix filename lookup in queries
+
+Coalesce the nfo:fileName of the resource (if we have access to
+it) with runtime function call (if we only have the URI at hand
+but no access to the nfo:FileDataObject)
+---
+ src/query.js | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/query.js b/src/query.js
+index 4fc94b6b..3600f1c4 100644
+--- a/src/query.js
++++ b/src/query.js
+@@ -145,7 +145,7 @@ var QueryBuilder = new Lang.Class({
+ _buildQueryInternal: function(global, flags, offsetController, sortBy) {
+ let selectClauses =
+ ' (nie:isStoredAs(?urn) AS ?uri) ' +
+- ' (nfo:fileName(?urn) AS ?filename) ' +
++ ' (COALESCE (nfo:fileName(nie:isStoredAs(?urn)), tracker:string-from-filename(nie:isStoredAs(?urn))) AS ?filename) ' +
+ ' (nie:mimeType(?urn) AS ?mimetype) ' +
+ ' (nie:title(?urn) AS ?title) ' +
+ ' (tracker:coalesce(nco:fullname(?creator), nco:fullname(?publisher), \'\') AS ?author) ' +
+--
+GitLab
+
+
+From 935a5a3d63807668af94c40d12bc778d702e48e9 Mon Sep 17 00:00:00 2001
+From: Carlos Garnacho
+Date: Tue, 16 Feb 2021 13:16:52 +0100
+Subject: [PATCH 10/12] flatpak: Add libgsf dependency
+
+This is needed by tracker-miners for epub documents.
+---
+ flatpak/org.gnome.Books.json | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+diff --git a/flatpak/org.gnome.Books.json b/flatpak/org.gnome.Books.json
+index a80bab77..aefbba7a 100644
+--- a/flatpak/org.gnome.Books.json
++++ b/flatpak/org.gnome.Books.json
+@@ -104,6 +104,18 @@
+ }
+ ]
+ },
++ {
++ "name" : "libgsf",
++ "cleanup" : [
++ "/bin"
++ ],
++ "sources": [
++ {
++ "type": "git",
++ "url": "https://gitlab.gnome.org/GNOME/libgsf.git"
++ }
++ ]
++ },
+ {
+ "name": "evince",
+ "buildsystem": "meson",
+--
+GitLab
+
+
+From 17ad73a79f292f0b9852c43985c349c5549b644a Mon Sep 17 00:00:00 2001
+From: Carlos Garnacho
+Date: Tue, 16 Feb 2021 13:21:24 +0100
+Subject: [PATCH 11/12] flatpak: Disable tracker-miners writeback service
+
+This is unused, and unnecessary.
+---
+ flatpak/org.gnome.Books.json | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/flatpak/org.gnome.Books.json b/flatpak/org.gnome.Books.json
+index aefbba7a..a4859489 100644
+--- a/flatpak/org.gnome.Books.json
++++ b/flatpak/org.gnome.Books.json
+@@ -156,6 +156,7 @@
+ "-Dminer_fs=true",
+ "-Dminer_fs_cache_location=$XDG_CACHE_HOME/org.gnome.Books/miner/files",
+ "-Dminer_rss=false",
++ "-Dwriteback=false",
+ "-Dsystemd_user_services=false" ],
+ "sources": [
+ {
+--
+GitLab
+
+
+From b5875b2d435be067b5affc7e01c6e85574846cf6 Mon Sep 17 00:00:00 2001
+From: Carlos Garnacho
+Date: Tue, 16 Feb 2021 18:42:48 +0100
+Subject: [PATCH 12/12] query: Make nfo:fileName extraction more reliable
+
+Nesting properties with multiple cardinality don't result in the
+desired effect here. Get the nfo:FileDataObject inside the WHERE
+block, and use it in the select clause for this.
+---
+ src/query.js | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/query.js b/src/query.js
+index 3600f1c4..f753d3ae 100644
+--- a/src/query.js
++++ b/src/query.js
+@@ -79,6 +79,7 @@ var QueryBuilder = new Lang.Class({
+
+ _buildOptional: function() {
+ let sparql =
++ 'OPTIONAL { ?urn nie:isStoredAs ?file . } ';
+ 'OPTIONAL { ?urn nco:creator ?creator . } ' +
+ 'OPTIONAL { ?urn nco:publisher ?publisher . } ';
+
+@@ -145,7 +146,7 @@ var QueryBuilder = new Lang.Class({
+ _buildQueryInternal: function(global, flags, offsetController, sortBy) {
+ let selectClauses =
+ ' (nie:isStoredAs(?urn) AS ?uri) ' +
+- ' (COALESCE (nfo:fileName(nie:isStoredAs(?urn)), tracker:string-from-filename(nie:isStoredAs(?urn))) AS ?filename) ' +
++ ' (COALESCE (nfo:fileName(?file), tracker:string-from-filename(nie:isStoredAs(?urn))) AS ?filename) ' +
+ ' (nie:mimeType(?urn) AS ?mimetype) ' +
+ ' (nie:title(?urn) AS ?title) ' +
+ ' (tracker:coalesce(nco:fullname(?creator), nco:fullname(?publisher), \'\') AS ?author) ' +
+--
+GitLab
+
diff --git a/desktop/gnome/apps/gnome-books/pspec.xml b/desktop/gnome/apps/gnome-books/pspec.xml
new file mode 100644
index 0000000000..be04d060ce
--- /dev/null
+++ b/desktop/gnome/apps/gnome-books/pspec.xml
@@ -0,0 +1,75 @@
+
+
+
+
+ gnome-books
+ https://wiki.gnome.org/Apps/Books
+
+ Ayhan Yalçınsoy
+ ayhanyalcinsoy@pisilinux.org
+
+ GPLv2
+ gnome-books
+ app:gui
+ E-Book Manager
+ E-Book Manager
+ mirrors://gnome/gnome-books/40/gnome-books-40.0.tar.xz
+
+ appstream-glib-devel
+ desktop-file-utils
+ docbook-xsl
+ librsvg-devel
+ gjs-devel
+ evince-devel
+ git
+ glib2-devel
+ gnome-desktop-devel
+ gobject-introspection-devel
+ gtk3-devel
+ libxslt-devel
+ libgepub-devel
+ libseccomp-devel
+ meson
+ tracker-devel
+ webkit2gtk-devel
+
+
+
+
+
+
+
+ gnome-books
+
+ gtk3
+
+
+ /usr/bin
+ /usr/lib
+ /usr/share
+ /usr/share/local
+
+
+
+
+ gnome-books-devel
+
+ gnome-books
+
+
+ /usr/include
+ /usr/lib/pkgconfig
+ /usr/share/gir-1.0
+
+
+
+
+
+ 2021-06-08
+ 40.0
+ First release
+ Ayhan Yalçınsoy
+ ayhanyalcinsoy@pisilinux.org
+
+
+
diff --git a/desktop/gnome/apps/gnome-books/translations.xml b/desktop/gnome/apps/gnome-books/translations.xml
new file mode 100644
index 0000000000..c8f4ad121a
--- /dev/null
+++ b/desktop/gnome/apps/gnome-books/translations.xml
@@ -0,0 +1,13 @@
+
+
+
+ gnome-books
+ E-kitap yöneticisi
+ E-kitap yöneticisi
+
+
+
+ gnome-books-devel
+ gnome-books için geliştirme dosyaları
+
+