Commit 867b69ca authored by Bilal Elmoussaoui's avatar Bilal Elmoussaoui Committed by GitHub

Search service provider (#93)

* add gnome shell search provider

* fix gnome shell search provider

* remove the gicon from search provider
parent 12d47b4e
......@@ -68,6 +68,11 @@ class Account(GObject.GObject):
return Account(obj['id'], username, provider, secret_id)
@staticmethod
def get_by_id(id_):
obj = Database.get_default().get_by_id(id_)
return Account(obj['id'], obj['username'], obj['provider'], obj['secret_id'])
def update(self, username, provider):
"""
Update the account name and/or provider.
......
......@@ -80,6 +80,28 @@ class Database:
Logger.error("[SQL] Couldn't add a new account")
Logger.error(str(error))
def get_by_id(self, id_):
"""
Get an account by the ID
:param id_: int the account id
:return: list: The account data
"""
query = "SELECT * FROM {table} WHERE {key}=?".format(
key=self.primary_key, table=self.table_name)
try:
data = self.conn.cursor().execute(query, (id_,))
obj = data.fetchone()
return OrderedDict([
("id", obj[0]),
("username", obj[1]),
("provider", obj[2]),
("secret_id", obj[3])
])
except Exception as error:
Logger.error("[SQL] Couldn't get account with ID={}".format(id_))
Logger.error(str(error))
return None
def get_secret_id(self, id_):
"""
Get the secret code by id
......@@ -125,6 +147,19 @@ class Database:
Logger.error("[SQL] Couldn't update account name by id")
Logger.error(error)
def search(self, terms):
filters = " ".join(terms)
if filters:
filters = "%" + filters + "%"
query = "SELECT {key} FROM {table} WHERE username LIKE ?".format(table=self.table_name, key=self.primary_key)
try:
data = self.conn.cursor().execute(query, (filters, ))
return [str(account[0]) for account in data.fetchall()]
except Exception as error:
Logger.error("[SQL]: Couldn't search for an account")
Logger.error(str(error))
return []
@property
def count(self):
"""
......
......@@ -21,7 +21,7 @@ from os import environ
from gi import require_version
require_version('Gtk', '3.0')
from gi.repository import Gtk, GdkPixbuf, GLib
from gi.repository import Gtk, GdkPixbuf, GLib, Gio
def can_use_qrscanner():
......
......@@ -20,12 +20,20 @@ if get_option('enable-zbar')
use_zebar = true
endif
python_bin = python.find_python()
if not python_bin.found()
error('No valid python3 binary found')
else
message('Found python3 binary')
endif
PYTHON_DIR = join_paths(get_option('prefix'), python.sysconfig_path('purelib'))
DATA_DIR = join_paths(get_option('prefix'), get_option('datadir'), meson.project_name())
LOCALE_DIR = join_paths(get_option('prefix'), get_option('localedir'))
LIB_DIR = join_paths(get_option('prefix'), get_option('libdir'), meson.project_name())
SERVICES_DIR = join_paths(get_option('datadir'), 'dbus-1', 'services')
SEARCH_PROVIDER_DIR = join_paths(get_option('datadir'), 'gnome-shell', 'search-providers')
LIBEXEC_DIR = join_paths(get_option('prefix'), get_option('libexecdir'))
subproject('libgd',
default_options: [
......@@ -44,12 +52,16 @@ conf = configuration_data()
conf.set('DATA_DIR', DATA_DIR)
conf.set('LOCALE_DIR', LOCALE_DIR)
conf.set('PYTHON_DIR', PYTHON_DIR)
conf.set('PYTHON_EXEC_DIR', join_paths(get_option('prefix'), python.sysconfig_path('stdlib')))
conf.set('VERSION', meson.project_version())
conf.set('ENABLE_QRSCANNER', use_zbar)
conf.set('LIB_DIR', LIB_DIR)
conf.set('libexecdir', LIBEXEC_DIR)
conf.set('PYTHON', python_bin.path())
subdir('data')
subdir('po')
subdir('search-provider')
configure_file(
input: 'authenticator.py.in',
......
[Shell Search Provider]
DesktopId=com.github.bilelmoussaoui.Authenticator.desktop
BusName=com.github.bilelmoussaoui.Authenticator.SearchProvider
ObjectPath=/com/github/bilelmoussaoui/Authenticator/SearchProvider
Version=2
#!@PYTHON@
# Copyright (c) 2018 Bilal Elmoussaoui <bil.elmoussaoui@gmail.com>
# Copyright (c) 2014-2016 Cedric Bellegarde <cedric.bellegarde@adishatz.org>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import sys
sys.path.insert(1, '@PYTHON_EXEC_DIR@')
sys.path.insert(1, '@PYTHON_DIR@')
from os import path
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('GIRepository', '2.0')
from gi.repository import Gio, GIRepository, GLib
GIRepository.Repository.prepend_search_path(
path.join('@LIB_DIR@', 'girepository-1.0'))
GIRepository.Repository.prepend_library_path('@LIB_DIR@')
from Authenticator.models.database import Database
from Authenticator.models.account import Account
class Server:
def __init__(self, con, path):
method_outargs = {}
method_inargs = {}
for interface in Gio.DBusNodeInfo.new_for_xml(self.__doc__).interfaces:
for method in interface.methods:
method_outargs[method.name] = '(' + ''.join(
[arg.signature for arg in method.out_args]) + ')'
method_inargs[method.name] = tuple(
arg.signature for arg in method.in_args)
con.register_object(object_path=path,
interface_info=interface,
method_call_closure=self.on_method_call)
self.method_inargs = method_inargs
self.method_outargs = method_outargs
def on_method_call(self,
connection,
sender,
object_path,
interface_name,
method_name,
parameters,
invocation):
args = list(parameters.unpack())
for i, sig in enumerate(self.method_inargs[method_name]):
if sig is 'h':
msg = invocation.get_message()
fd_list = msg.get_unix_fd_list()
args[i] = fd_list.get(args[i])
try:
result = getattr(self, method_name)(*args)
# out_args is atleast (signature1).
# We therefore always wrap the result as a tuple.
# Refer to https://bugzilla.gnome.org/show_bug.cgi?id=765603
result = (result,)
out_args = self.method_outargs[method_name]
if out_args != '()':
variant = GLib.Variant(out_args, result)
invocation.return_value(variant)
else:
invocation.return_value(None)
except Exception as e:
pass
class AuthenticatorSearchProvider(Server, Gio.Application):
'''
<!DOCTYPE node PUBLIC
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
<node>
<interface name="org.gnome.Shell.SearchProvider2">
<method name="GetInitialResultSet">
<arg type="as" name="terms" direction="in" />
<arg type="as" name="results" direction="out" />
</method>
<method name="GetSubsearchResultSet">
<arg type="as" name="previous_results" direction="in" />
<arg type="as" name="terms" direction="in" />
<arg type="as" name="results" direction="out" />
</method>
<method name="GetResultMetas">
<arg type="as" name="identifiers" direction="in" />
<arg type="aa{sv}" name="metas" direction="out" />
</method>
<method name="ActivateResult">
<arg type="s" name="identifier" direction="in" />
<arg type="as" name="terms" direction="in" />
<arg type="u" name="timestamp" direction="in" />
</method>
<method name="LaunchSearch">
<arg type="as" name="terms" direction="in" />
<arg type="u" name="timestamp" direction="in" />
</method>
</interface>
</node>
'''
__AUTHENTICATOR_BUS = 'com.github.bilelmoussaoui.Authenticator.SearchProvider'
__SEARCH_BUS = 'org.gnome.Shell.SearchProvider2'
__PATH_BUS = '/com/github/bilelmoussaoui/Authenticator/SearchProvider'
def __init__(self):
Gio.Application.__init__(
self,
application_id=self.__AUTHENTICATOR_BUS,
flags=Gio.ApplicationFlags.IS_SERVICE)
self.__bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
Gio.bus_own_name_on_connection(self.__bus,
self.__SEARCH_BUS,
Gio.BusNameOwnerFlags.NONE,
None,
None)
Server.__init__(self, self.__bus, self.__PATH_BUS)
def ActivateResult(self, *_):
self.LaunchSearch()
def GetInitialResultSet(self, terms):
return self.__search(terms)
def GetResultMetas(self, ids):
results = []
try:
for search_id in ids:
account = Account.get_by_id(int(search_id))
name = account.username
description = "{} - {}".format(
account.provider.strip(), account.username)
d = {'id': GLib.Variant('s', search_id),
'description': GLib.Variant('s', description),
'name': GLib.Variant('s', name)
}
results.append(d)
except Exception as e:
print("AuthenticatorSearchProvider::GetResultMetas():", e)
return []
return results
def GetSubsearchResultSet(self, _, new_terms):
return self.__search(new_terms)
def LaunchSearch(self, *_):
GLib.spawn_async_with_pipes(
None, ["authenticator"], None,
GLib.SpawnFlags.SEARCH_PATH |
GLib.SpawnFlags.DO_NOT_REAP_CHILD, None)
def __search(self, terms):
ids = []
try:
ids = Database.get_default().search(terms)
except Exception as e:
print("AuthenticatorSearchProvider::__search():", e)
return ids
if __name__ == '__main__':
service = AuthenticatorSearchProvider()
service.hold()
service.run()
[D-BUS Service]
Name=com.github.bilelmoussaoui.Authenticator.SearchProvider
Exec=@libexecdir@/authenticator-search-provider
configure_file(
input: 'authenticator-search-provider.py.in',
output: 'authenticator-search-provider',
configuration: conf,
install_dir: LIBEXEC_DIR
)
configure_file(
input: meson.project_name() + '.SearchProvider.service.in',
output: meson.project_name() + '.SearchProvider.service',
configuration: conf,
install_dir: SERVICES_DIR
)
install_data(
'authenticator-search-provider.ini',
install_dir: SEARCH_PROVIDER_DIR
)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment