Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/python
- # -*- coding: utf-8 -*-
- # Insync Nautilus Plugin
- #
- # Authors
- # Brett Hartshorn <brett@insynchq.com>
- # Luis Manuel R. Pugoy <lpugoy@insynchq.com>
- #
- # Copyright Insynchq Pte. Ltd., ("Insync") | http://www.insynchq.com
- # License: GNU GPL v2
- DEV_MODE = False
- import json
- import os
- import socket
- import sys
- import urllib
- from gi.repository import GObject
- GTK3 = False
- try:
- from gi.repository import Nautilus
- GTK3 = True # this might not be true
- except:
- print('WARN - using Nautilus2 wrapper')
- import nautilus as Nautilus # Nautilus2
- if GTK3:
- # http://python-gtk-3-tutorial.readthedocs.org/en/latest/drag_and_drop.html
- from gi.repository import Gtk
- from gi.repository import Gdk
- (TARGET_ENTRY_TEXT, TARGET_ENTRY_PIXBUF) = range(2)
- (COLUMN_TEXT, COLUMN_PIXBUF) = range(2)
- DRAG_ACTION = Gdk.DragAction.COPY
- print(Nautilus)
- if __name__ == '__main__': ## this is only for testing ##
- if '--install-local' in sys.argv:
- ## Nautilus2 is no longer supported ##
- #if os.path.isdir( os.path.expanduser('~/.nautilus') ):
- # path = os.path.expanduser('~/.nautilus/python-extensions')
- # if not os.path.isdir(path): os.makedirs( path )
- # os.system('cp -v %s ~/.nautilus/python-extensions/.' %__file__)
- ## Nautilus3 ##
- path = os.path.expanduser('~/.local/share/nautilus-python/extensions')
- if not os.path.isdir(path): os.makedirs( path )
- os.system('cp -v %s ~/.local/share/nautilus-python/extensions/.' %__file__)
- os.system('cp -Rv ../core/linux/libgio/ ~/.local/share/nautilus-python/extensions/.')
- else:
- os.system('cp -v %s /usr/share/nautilus-python/extensions/.' %__file__)
- os.system('cp -Rv ../core/linux/libgio/ /usr/share/nautilus-python/extensions/.')
- os.system('nautilus -q')
- sys.exit()
- print('Insync Nautilus Plugin')
- FS_ENCODING = sys.getfilesystemencoding() # C is 'ANSI_X3.4-1968'
- def ipc_insync(**kw):
- data = json.dumps(kw)
- socket_file = u'/tmp/insync%r.sock' % os.getuid()
- if not os.path.exists(socket_file):
- if DEV_MODE:
- print('WARN: insync socket not found')
- return None
- sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
- try:
- sock.connect(socket_file)
- except:
- return None
- sock.send(data)
- res = sock.recv(4096)
- sock.close()
- if res.strip():
- return json.loads(res.strip())
- else:
- return None
- ## Keep in sync with client/insyncd/clientmenuitems.py :: context_menu_items_for
- GDRIVE = ('Open in Google Drive', 'cm:open', 'web-browser-symbolic')
- SEPARATOR = (unicode(u'\u2015' * 10), None, None)
- SHARE = ('Share', 'cm:share', 'send-to-symbolic')
- LINK = ('Copy public Link', 'cm:public_link', 'document-save-symbolic')
- _EMBLEM_KEYS = {
- 'SYNCED': 'emblem-insync-synced',
- 'SYNCING': 'emblem-insync-syncing',
- 'ERROR': 'emblem-insync-error',
- 'DES_ERROR': 'emblem-insync-des-error',
- 'DES_SYNCING': 'emblem-insync-syncing',
- }
- class InsyncExtension(GObject.GObject, Nautilus.MenuProvider, Nautilus.InfoProvider):
- '''
- see: /usr/share/doc/python-nautilus/examples/
- 1. nautilus.ColumnProvider - It exposes a single function get_columns . This function has to return a sequence of nautilus.Column objects.
- 2. nautilus.InfoProvider - Exposes a single function update_file_info with a file as an argument.
- 3. nautilus.LocationWidgetProvider - Exposes a single function get_widget, return some gtk widget near the location bar.
- 4. nautilus.MenuProvider - Probably the most used interface. It exposes three functions :
- get_file_items, get_background_items and get_toolbar_items . The first two functions determine the entries that appear in the context menu. The difference is that get_background_items is usually called for a folder.
- The last function is used for toolbar items hence you must name the icon parameter of the menuItem.
- 5. nautilus.PropertyPageProvider - Exposes get_property_pages function where you reture one or more "pages" or tabs.
- '''
- DEFAULT_INSYNC_CONFIG_PATH = os.path.expanduser('~/.config/Insync')
- _hack_ = False # Nautilus 3.4 has fixed the issue
- def load_insync_settings(self):
- msg = ipc_insync(command='GET-INFO')
- if msg:
- if DEV_MODE:
- print('INSYNC ACTIVE')
- print(msg)
- self._insync_active = True
- self._insync_roots = msg
- else:
- self._insync_active = False
- symcache = os.path.join(self.DEFAULT_INSYNC_CONFIG_PATH, 'symlink-cache.json')
- if os.path.isfile(symcache):
- f = open(symcache, 'rb')
- self._symlinks = json.load(f)
- f.close()
- def __init__(self):
- print('[__init__ insync plugin]', self)
- self._insync_active = False
- self._insync_roots = None
- self._symlinks = {} # link : target
- if InsyncExtension._hack_:
- print('[WARN old nautilus bug]')
- self.get_file_items = lambda win, files: None
- self.get_widget = lambda uri, win: None
- InsyncExtension._hack_ = False
- else:
- print('::new insync extension::')
- #InsyncExtension._hack_ = self
- InsyncExtension._hack_ = False
- #self.get_property_pages = lambda files: None
- self.get_widget = lambda uri, win: None
- def get_background_items(self, window, folder):
- '''
- This method needs to be defined to avoid this warning:
- ** (nautilus:2913): CRITICAL **: nautilus_menu_provider_get_background_items: assertion
- `NAUTILUS_IS_MENU_PROVIDER (provider)' failed
- '''
- #print('background items', window, folder)
- return None
- def get_file_items(self, window, files):
- if DEV_MODE:
- print('get file items', files)
- if len(files) != 1:
- # TODO what can we do with mutiple files here
- return None
- self.load_insync_settings()
- if not self._insync_roots:
- return None
- file = files[0]
- path = urllib.unquote(file.get_uri()[len('file://'):])
- path = unicode(path, FS_ENCODING)
- if DEV_MODE:
- print path
- is_dir = os.path.isdir(path)
- roots = tuple([root + '/' for root in self._insync_roots])
- if self._insync_active and path.startswith(roots):
- tip = 'Insync folder actions' if is_dir else 'Insync file actions'
- item = Nautilus.MenuItem(
- name="Insync",
- label="Insync",
- tip=tip,
- icon='insync'
- )
- sub_menu = Nautilus.Menu()
- item.set_submenu(sub_menu)
- for text, cmd, icon in [GDRIVE, SEPARATOR, SHARE, LINK]:
- menu_item = Nautilus.MenuItem(name=text, label=text, tip=text, icon=icon)
- if cmd:
- menu_item.connect('activate', self.do_action, file, cmd)
- else:
- menu_item.sensitive = False
- sub_menu.append_item(menu_item)
- return [item]
- elif not path.startswith(tuple(self._insync_roots)):
- targets = self._symlinks.values()
- if DEV_MODE:
- print 'self._symlinks', self._symlinks
- symlink_target = unicode(
- urllib.unquote(file.get_uri()[len('file://'):]),
- FS_ENCODING
- )
- toplevel = self.make_symlinks_menu(symlink_target)
- return [toplevel]
- def make_symlinks_menu(self, symlink_target):
- uid = 0 # this is lame
- toplevel = Nautilus.MenuItem(
- name="Insync::%s" % uid,
- label="Add to Insync",
- tip="Add to Insync - creates a symlink to this folder, in the user folder you select",
- icon='insync'
- )
- uid += 1
- submenu = Nautilus.Menu()
- toplevel.set_submenu(submenu)
- reverse_symlinks = dict((value, key) for key, value in self._symlinks.iteritems())
- if symlink_target in reverse_symlinks:
- full_path, symlink_name = os.path.split(reverse_symlinks[symlink_target])
- _, folder_name = os.path.split(full_path)
- label = u'%s/%s (linked)' % (folder_name, symlink_name)
- item = Nautilus.MenuItem(
- name='Insync::%s' % uid,
- label=label,
- tip=label
- )
- submenu.append_item(item)
- uid += 1
- else:
- _, target_name = os.path.split(symlink_target)
- for insync_root in self._insync_roots:
- _, folder_name = os.path.split(insync_root)
- if target_name in os.listdir(insync_root):
- label = u'%s/%s (name in use)' % (folder_name, target_name)
- item = Nautilus.MenuItem(
- name='Insync::%s' % uid,
- label=label,
- tip=label
- )
- else:
- item = Nautilus.MenuItem(
- name='Insync::%s' % uid,
- label=folder_name,
- tip='create symlink in user root folder'
- )
- item.connect('activate', self.do_symlink, symlink_target, target_name, insync_root)
- submenu.append_item(item)
- uid += 1
- return toplevel
- def do_symlink(self, menu, path, target, insync_root):
- dest = os.path.join(insync_root, target)
- if DEV_MODE:
- print('<doing symlink>', path, dest)
- self._symlinks[path] = dest
- os.symlink(path, dest)
- if not ipc_insync(command='IS-ALIVE') and os.path.isdir(self.DEFAULT_INSYNC_CONFIG_PATH):
- symcache = os.path.join(self.DEFAULT_INSYNC_CONFIG_PATH, 'symlink-cache.json')
- with open(symcache, 'wb') as f:
- json.dump(self._symlinks, f)
- def do_action(self, menu, file, method):
- path = urllib.unquote(
- file.get_uri()[len('file://'):]
- )
- ipc_insync(method=method, full_path=path)
- def update_file_info_full(self, provider, handle, closure, file):
- if file.get_uri_scheme() != 'file':
- return
- filename = urllib.unquote(file.get_uri()[len('file://'):])
- status = ipc_insync(command='GET-FILE-STATUS', full_path=filename)
- if DEV_MODE:
- print filename, status
- if not status or status == 'UNKNOWN':
- return Nautilus.OperationResult.COMPLETE
- GObject.timeout_add(1, self.add_emblem, provider, handle, closure, file,
- filename, status)
- return Nautilus.OperationResult.IN_PROGRESS
- def add_emblem(self, provider, handle, closure, file, filename, status):
- emblem = _EMBLEM_KEYS[status]
- if os.path.isdir(filename):
- is_shared = ipc_insync(command='IS-FILE-SHARED', full_path=filename)
- if is_shared:
- emblem += '-shared'
- file.add_emblem(emblem)
- Nautilus.info_provider_update_complete_invoke(closure, provider, handle,
- Nautilus.OperationResult.COMPLETE)
- return False
- print(InsyncExtension)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement