~ken-vandine/unity-scope-gdrive/libunity7_merge

« back to all changes in this revision

Viewing changes to unity_gdrive_daemon.py

  • Committer: David Callé
  • Date: 2013-03-15 09:33:30 UTC
  • Revision ID: davidc@framli.eu-20130315093330-9qfuqi1wv90g4lv3
Revert changes for libunity7

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#! /usr/bin/python3
2
 
#
3
 
# Copyright 2012 Canonical Ltd.
4
 
#
5
 
# Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
6
 
#
7
 
# GPLv3
8
 
#
9
 
 
10
 
import sys
11
 
from gi.repository import GLib, GObject, Gio
12
 
from gi.repository import Accounts, Signon
13
 
from gi.repository import GData
14
 
from gi.repository import Unity, UnityExtras
15
 
 
16
 
from datetime import datetime, timedelta
17
 
import gettext
18
 
import time
19
 
 
20
 
APP_NAME = "unity-scope-gdrive"
21
 
LOCAL_PATH = "/usr/share/locale/"
22
 
 
23
 
gettext.bindtextdomain(APP_NAME, LOCAL_PATH)
24
 
gettext.textdomain(APP_NAME)
25
 
_ = gettext.gettext
26
 
 
27
 
#
28
 
# The primary bus name we grab *must* match what we specify in our .scope file
29
 
#
30
 
BUS_NAME = "com.canonical.Unity.Scope.File.Gdrive"
31
 
# Map Google Docs types to the values of the "type" filter
32
 
TYPE_MAP = {
33
 
    "document": "documents",
34
 
    "pdf": "documents",
35
 
    "folder": "folders",
36
 
    "drawing": "images",
37
 
    "presentation": "presentations"
38
 
}
39
 
THEME = "/usr/share/icons/unity-icon-theme/places/svg/"
40
 
 
41
 
class Daemon:
42
 
 
43
 
  def __init__ (self):
44
 
    self._scope = Unity.DeprecatedScope.new ("/com/canonical/unity/scope/file/gdrive", "gdrive")
45
 
    self._scope.search_in_global = True;
46
 
    self._preferences = Unity.PreferencesManager.get_default()
47
 
    self._preferences.connect ("notify::remote-content-search",
48
 
            self._on_filters_or_preferences_changed);
49
 
 
50
 
    self._gdocs_accounts = []
51
 
    try:
52
 
      self._account_manager = Accounts.Manager.new_for_service_type("documents")
53
 
    except TypeError as e:
54
 
      print ("Couldn't start account manager, not initialising: %s" % e)
55
 
      sys.exit(0)
56
 
    self._account_manager.connect("enabled-event", self._on_enabled_event);
57
 
    for account in self._account_manager.get_enabled_account_services():
58
 
      self.add_account_service(account)
59
 
 
60
 
    # Listen for changes and requests
61
 
    self._scope.connect ("search-changed", self._on_search_changed)
62
 
 
63
 
    # This allows us to re-do the search if any parameter on a filter has changed
64
 
    # Though it's possible to connect to a more-specific changed signal on each
65
 
    # Fitler, as we re-do the search anyway, this catch-all signal is perfect for
66
 
    # us.
67
 
    self._scope.connect ("notify::filtering", self._on_filters_or_preferences_changed);
68
 
    
69
 
    filters = Unity.FilterSet.new()
70
 
    f = Unity.RadioOptionFilter.new ("modified", _("Last modified"), Gio.ThemedIcon.new("input-keyboard-symbolic"), False)
71
 
    f.add_option ("last-7-days", _("Last 7 days"), None)
72
 
    f.add_option ("last-30-days", _("Last 30 days"), None)
73
 
    f.add_option ("last-year", _("Last year"), None);
74
 
    filters.add(f)
75
 
    f2 = Unity.CheckOptionFilter.new ("type", _("Type"), Gio.ThemedIcon.new("input-keyboard-symbolic"), False)
76
 
    f2.add_option ("documents", _("Documents"), None)
77
 
    f2.add_option ("folders", _("Folders"), None)
78
 
    f2.add_option ("images", _("Images"), None)
79
 
    f2.add_option ("audio", _("Audio"), None)
80
 
    f2.add_option ("videos", _("Videos"), None)
81
 
    f2.add_option ("presentations", _("Presentations"), None)
82
 
    f2.add_option ("other", _("Other"), None)
83
 
    filters.add (f2)
84
 
    self._scope.props.filters = filters
85
 
    cats = Unity.CategorySet.new()
86
 
    cats.add (Unity.Category.new ('global',
87
 
                                  _("Files & Folders"),
88
 
                                  Gio.ThemedIcon.new(THEME + "group-folders.svg"),
89
 
                                  Unity.CategoryRenderer.VERTICAL_TILE))
90
 
    cats.add (Unity.Category.new ('recent',
91
 
                                  _("Recent"),
92
 
                                  Gio.ThemedIcon.new(THEME + "group-recent.svg"),
93
 
                                  Unity.CategoryRenderer.VERTICAL_TILE))
94
 
    cats.add (Unity.Category.new ('downloads',
95
 
                                  _("Downloads"),
96
 
                                  Gio.ThemedIcon.new(THEME + "group-downloads.svg"),
97
 
                                  Unity.CategoryRenderer.VERTICAL_TILE))
98
 
    cats.add (Unity.Category.new ('folders',
99
 
                                  _("Folders"),
100
 
                                  Gio.ThemedIcon.new(THEME + "group-folders.svg"),
101
 
                                  Unity.CategoryRenderer.VERTICAL_TILE))
102
 
    self._scope.props.categories = cats
103
 
    self._scope.export()
104
 
 
105
 
  def _on_enabled_event (self, account_manager, account_id):
106
 
    account = self._account_manager.get_account(account_id)
107
 
    for service in account.list_services():
108
 
      account_service = Accounts.AccountService.new(account, service)
109
 
      if account_service.get_enabled():
110
 
        self.add_account_service(account_service)
111
 
 
112
 
  def add_account_service(self, account_service):
113
 
    for gdocs_account in self._gdocs_accounts:
114
 
      if gdocs_account.get_account_service() == account_service:
115
 
        return
116
 
    gdocs_account = GDocsAccount(self._scope, account_service);
117
 
    self._gdocs_accounts.append(gdocs_account)
118
 
 
119
 
  def _on_search_changed (self, scope, search, search_type, cancellable):
120
 
    search_string = search.props.search_string
121
 
    results = search.props.results_model
122
 
    results.clear()
123
 
 
124
 
    if self._preferences.props.remote_content_search != Unity.PreferencesManagerRemoteContent.ALL:
125
 
      search.emit("finished")
126
 
      return
127
 
 
128
 
    if search_type == Unity.SearchType.GLOBAL:
129
 
      is_global = True
130
 
    else:
131
 
      is_global = False
132
 
 
133
 
    print("Search changed to: '%s'" % search_string)
134
 
 
135
 
    for gdocs_account in self._gdocs_accounts:
136
 
      gdocs_account.update_results_model (search_string, results, search, is_global)
137
 
    search.emit("finished")
138
 
  
139
 
  def _on_filters_or_preferences_changed (self, *_):
140
 
    self._scope.queue_search_changed(Unity.SearchType.DEFAULT)
141
 
  
142
 
 
143
 
class SignOnAuthorizer(GObject.Object, GData.Authorizer):
144
 
  __g_type_name__ = "SignOnAuthorizer"
145
 
  def __init__(self, account_service):
146
 
    GObject.Object.__init__(self)
147
 
    self._account_service = account_service
148
 
    self._main_loop = None
149
 
    self._token = None
150
 
 
151
 
  def do_process_request(self, domain, message):
152
 
    message.props.request_headers.replace('Authorization', 'OAuth %s' % (self._token, ))
153
 
 
154
 
  def do_is_authorized_for_domain(self, domain):
155
 
    return True if self._token else False
156
 
 
157
 
  def do_refresh_authorization(self, cancellable):
158
 
    if self._main_loop:
159
 
      print("Authorization already in progress")
160
 
      return False
161
 
 
162
 
    old_token = self._token
163
 
    # Get the global account settings
164
 
    auth_data = self._account_service.get_auth_data()
165
 
    identity = auth_data.get_credentials_id()
166
 
    session_data = auth_data.get_parameters()
167
 
    self._auth_session = Signon.AuthSession.new(identity, auth_data.get_method())
168
 
    self._main_loop = GLib.MainLoop()
169
 
    self._auth_session.process(session_data,
170
 
            auth_data.get_mechanism(),
171
 
            self.login_cb, None)
172
 
    if self._main_loop:
173
 
      self._main_loop.run()
174
 
    if self._token == old_token:
175
 
      print("Got the same token")
176
 
      return False
177
 
    else:
178
 
      print("Got token: %s" % (self._token, ))
179
 
      return True
180
 
 
181
 
  def login_cb(self, session, reply, error, user_data):
182
 
    print("login finished")
183
 
    self._main_loop.quit()
184
 
    self._main_loop = None
185
 
    if error:
186
 
      print("Got authentication error:", error.message)
187
 
      return
188
 
    if "AuthToken" in reply:
189
 
      self._token = reply["AuthToken"]
190
 
    elif "AccessToken" in reply:
191
 
      self._token = reply["AccessToken"]
192
 
    else:
193
 
      print("Didn't find token in session:", reply)
194
 
 
195
 
 
196
 
# Encapsulates searching a single user's GDocs
197
 
class GDocsAccount:
198
 
  def __init__ (self, scope, account_service):
199
 
    self._scope = scope
200
 
    self._account_service = account_service
201
 
    self._account_service.connect("enabled", self._on_account_enabled)
202
 
    self._enabled = self._account_service.get_enabled()
203
 
    self._authenticating = False
204
 
    authorizer = SignOnAuthorizer(self._account_service)
205
 
    authorizer.refresh_authorization(None)
206
 
    self._client = GData.DocumentsService(authorizer=authorizer)
207
 
 
208
 
  def get_account_service (self):
209
 
    return self._account_service
210
 
 
211
 
  def _on_account_enabled (self, account, enabled):
212
 
    print("account %s, enabled %s" % (account, enabled))
213
 
    self._enabled = enabled
214
 
 
215
 
  def update_results_model (self, search, model, s, is_global=False):
216
 
    if not self._enabled:
217
 
      return
218
 
 
219
 
    # Get the list of documents
220
 
    feed = self.get_doc_list(search, s, is_global);
221
 
    for entry in feed:
222
 
      rtype = entry.get_resource_id().split(":")[0]
223
 
 
224
 
      if is_global:
225
 
        category = 0
226
 
      else:
227
 
        if rtype == "folder":
228
 
          category = 3
229
 
        else:
230
 
          category = 1
231
 
 
232
 
      model.append(uri=entry.look_up_link(GData.LINK_ALTERNATE).get_uri(),
233
 
                   icon_hint=self.icon_for_type(rtype),
234
 
                   category=category,
235
 
                   mimetype="text/html",
236
 
                   title=entry.props.title,
237
 
                   comment=rtype,
238
 
                   dnd_uri=entry.props.content_uri,
239
 
                   result_type=Unity.ResultType.PERSONAL);
240
 
 
241
 
  # This is where we do the actual search for documents
242
 
  def get_doc_list (self, search, s, is_global):
243
 
    query = GData.DocumentsQuery(q=search)
244
 
 
245
 
    # We do not want filters to effect global results
246
 
    if not is_global:
247
 
      self.apply_filters(query, s)
248
 
 
249
 
    print("Searching for: " + query.props.q)
250
 
 
251
 
    if not self._client.is_authorized():
252
 
      if not self._client.props.authorizer.refresh_authorization(None):
253
 
        return []
254
 
    try:
255
 
      feed = self._client.query_documents(query, None, None, None).get_entries()
256
 
    except GObject.GError as e:
257
 
      print(e.message)
258
 
      return []
259
 
 
260
 
    if not is_global:
261
 
      feed = self.filter_results(feed, s)
262
 
    return feed
263
 
 
264
 
  def apply_filters (self, query, s):
265
 
    f = s.get_filter("modified")
266
 
    if f != None:
267
 
      o = f.get_active_option()
268
 
      if o != None:
269
 
        age = 0
270
 
        if o.props.id == "last-year":
271
 
          age = 365
272
 
        elif o.props.id == "last-30-days":
273
 
          age = 30
274
 
        elif o.props.id == "last-7-days":
275
 
          age = 7
276
 
        if age:
277
 
          last_time = datetime.now() - timedelta(age)
278
 
          query.set_updated_min(time.mktime(last_time.timetuple()))
279
 
 
280
 
  def filter_results (self, feed, s):
281
 
    f = s.get_filter("type")
282
 
    if not f: return feed
283
 
    if not f.props.filtering:
284
 
      return feed
285
 
    r = []
286
 
    for entry in feed:
287
 
        rtype = entry.get_resource_id().split(":")[0]
288
 
        filter_type = TYPE_MAP.get(rtype, "other")
289
 
        if f.get_option(filter_type).props.active:
290
 
            r.append(entry)
291
 
    return r
292
 
 
293
 
  # Send back a useful icon depending on the document type
294
 
  def icon_for_type (self, doc_type):
295
 
    ret = "text-x-preview"
296
 
 
297
 
    if doc_type == "pdf":
298
 
      ret = "gnome-mime-application-pdf"
299
 
    elif doc_type == "drawing":
300
 
      ret = "x-office-drawing"
301
 
    elif doc_type == "document":
302
 
      ret = "x-office-document"
303
 
    elif doc_type == "presentation":
304
 
      ret = "libreoffice-oasis-presentation"
305
 
    elif doc_type == "spreadsheet" or doc_type == "text/xml":
306
 
      ret = "x-office-spreadsheet"
307
 
    elif doc_type == "folder":
308
 
      ret = "folder"
309
 
    elif doc_type == "file":
310
 
      ret = "gnome-fs-regular"
311
 
    else:
312
 
      print("Unhandled icon type: ", doc_type)
313
 
 
314
 
    return ret;
315
 
 
316
 
if __name__ == '__main__':
317
 
    daemon = UnityExtras.dbus_own_name(BUS_NAME, Daemon, None)
318
 
    if daemon:
319
 
        GLib.unix_signal_add(0, 2, lambda x: daemon.quit(), None)
320
 
        daemon.run([])
321