Advertisement
lpugoy

Insync extension for Caja

May 23rd, 2013
95
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.86 KB | None | 0 0
  1. print('loading insync caja plugin..')
  2. import caja
  3. import gtk # gtk2
  4. import json
  5. import os
  6. import socket
  7. import sys
  8. import urllib
  9.  
  10. DEV_MODE = False
  11.  
  12. #http://www.gtk.org/tutorial1.2/gtk_tut-13.html
  13. #http://pygtk.org/docs/pygtk/class-gtkaction.html
  14. #gtk.Action(name, label, tooltip, stock_id)
  15.  
  16. FS_ENCODING = sys.getfilesystemencoding()   # C is 'ANSI_X3.4-1968'
  17.  
  18. def ipc_insync(**kw):
  19.   data = json.dumps(kw)
  20.   socket_file = u'/tmp/insync%r.sock' % os.getuid()
  21.   if not os.path.exists(socket_file):
  22.     if DEV_MODE:
  23.       print('WARN: insync socket not found')
  24.     return None
  25.   sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
  26.   try:
  27.     sock.connect(socket_file)
  28.   except:
  29.     return None
  30.   sock.send(data)
  31.   res = sock.recv(4096)
  32.   sock.close()
  33.   if res.strip():
  34.     return json.loads(res.strip())
  35.   else:
  36.     return None
  37.  
  38.  
  39. GDRIVE = ('Open in Google Drive', 'cm:open', 'web-browser-symbolic')
  40. SEPARATOR = (unicode(u'\u2015' * 10), None, None)
  41. SHARE =  ('Share', 'cm:share', 'send-to-symbolic')
  42. LINK =   ('Copy public Link', 'cm:public_link', 'document-save-symbolic')
  43.  
  44.  
  45. _EMBLEM_KEYS = {
  46.   'SYNCED': 'emblem-insync-synced',
  47.   'SYNCING': 'emblem-insync-syncing',
  48.   'ERROR': 'emblem-insync-error',
  49.   'DES_ERROR': 'emblem-insync-des-error',
  50.   'DES_SYNCING': 'emblem-insync-syncing',
  51. }
  52.  
  53.  
  54. class InsyncPlugin(caja.MenuProvider, caja.InfoProvider):
  55.  
  56.   DEFAULT_INSYNC_CONFIG_PATH = os.path.expanduser('~/.config/Insync')
  57.  
  58.   # Caja crashes if a plugin doesn't implement the __init__ method.
  59.   # See Bug #374958
  60.   def __init__(self):
  61.     self._insync_active = False
  62.     self._insync_roots = None
  63.     self._symlinks = {}  # link : target
  64.  
  65.   def load_insync_settings(self):
  66.     msg = ipc_insync(command='GET-INFO')
  67.     if msg:
  68.       if DEV_MODE:
  69.         print('INSYNC ACTIVE')
  70.         print(msg)
  71.       self._insync_active = True
  72.       self._insync_roots = msg
  73.     else:
  74.       self._insync_active = False
  75.     symcache = os.path.join(self.DEFAULT_INSYNC_CONFIG_PATH, 'symlink-cache.json')
  76.     if os.path.isfile(symcache):
  77.       f = open(symcache, 'rb')
  78.       self._symlinks = json.load(f)
  79.       f.close()
  80.  
  81.   def get_background_items(self, window, file):
  82.     ## when is this called?
  83.     print('get get_background_items', file)
  84.     top_menuitem = caja.MenuItem('ExampleMenuProvider::Foo', 'Foox', '')
  85.     submenu = caja.Menu()
  86.     top_menuitem.set_submenu(submenu)
  87.     sub_menuitem = caja.MenuItem('ExampleMenuProvider::Bar', 'Barxx', '')
  88.     submenu.append_item(sub_menuitem)
  89.     return top_menuitem,
  90.  
  91.   def get_file_items(self, window, files):
  92.     if DEV_MODE:
  93.       print('get file items', files)
  94.  
  95.     if len(files) != 1:
  96.       # TODO what can we do with mutiple files here
  97.       return None
  98.  
  99.     self.load_insync_settings()
  100.     if not self._insync_roots:
  101.       return None
  102.  
  103.     file = files[0]
  104.     path = urllib.unquote(file.get_uri()[len('file://'):])
  105.     path = unicode(path, FS_ENCODING)
  106.     print path
  107.     is_dir = os.path.isdir(path)
  108.  
  109.     roots = tuple([root + '/' for root in self._insync_roots])
  110.     if self._insync_active and path.startswith(roots):  ## this block requires insync daemon is running
  111.       tip = 'Insync folder actions' if is_dir else 'Insync file actions'
  112.       item = caja.MenuItem(
  113.         name="Insync",
  114.         label="Insync",
  115.         tip=tip,
  116.         icon='/usr/share/icons/insync/icons/insync.png'
  117.       )
  118.       sub_menu = caja.Menu()
  119.       item.set_submenu(sub_menu)
  120.  
  121.       for text, cmd, icon in [GDRIVE, SEPARATOR, SHARE, LINK]:
  122.         menu_item = caja.MenuItem(name=text, label=text, tip=text, icon=icon)
  123.         if cmd:
  124.           menu_item.connect('activate', self.do_action, file, cmd)
  125.         else:
  126.           menu_item.sensitive = False
  127.         sub_menu.append_item(menu_item)
  128.  
  129.       return [item]
  130.     elif not path.startswith(tuple(self._insync_roots)):  ## do not allow paths starting with ~/Insync to be symlinked inside itself
  131.       targets = self._symlinks.values()
  132.       print 'self._symlinks', self._symlinks
  133.  
  134.       symlink_target = unicode(
  135.         urllib.unquote(file.get_uri()[len('file://'):]),
  136.         FS_ENCODING
  137.       )
  138.  
  139.       toplevel = self.make_symlinks_menu(symlink_target)
  140.       return [toplevel]
  141.  
  142.   def make_symlinks_menu(self, symlink_target):
  143.     uid = 0   # this is lame
  144.  
  145.     toplevel = caja.MenuItem(
  146.       name="Insync::%s" % uid,
  147.       label="Add to Insync",
  148.       tip="Add to Insync - creates a symlink to this folder, in the user folder you select",
  149.       icon='/usr/share/icons/insync/icons/insync.png'
  150.     )
  151.     uid += 1
  152.  
  153.     submenu = caja.Menu()
  154.     toplevel.set_submenu(submenu)
  155.  
  156.     reverse_symlinks = dict((value, key) for key, value in self._symlinks.iteritems())
  157.     if symlink_target in reverse_symlinks:
  158.       full_path, symlink_name = os.path.split(reverse_symlinks[symlink_target])
  159.       _, folder_name = os.path.split(full_path)
  160.       label = u'%s/%s (linked)' % (folder_name, symlink_name)
  161.       item = caja.MenuItem(
  162.         name='Insync::%s' % uid,
  163.         label=label,
  164.         tip=label
  165.       )
  166.       submenu.append_item(item)
  167.       uid += 1
  168.     else:
  169.       _, target_name = os.path.split(symlink_target)
  170.  
  171.       for insync_root in self._insync_roots:
  172.         _, folder_name = os.path.split(insync_root)
  173.         if target_name in os.listdir(insync_root):
  174.           label = u'%s/%s (name in use)' % (folder_name, target_name)
  175.           item = caja.MenuItem(
  176.             name='Insync::%s' % uid,
  177.             label=label,
  178.             tip=label
  179.           )
  180.         else:
  181.           item = caja.MenuItem(
  182.             name='Insync::%s' % uid,
  183.             label=folder_name,
  184.             tip='create symlink in user root folder'
  185.           )
  186.           item.connect('activate', self.do_symlink, symlink_target, target_name, insync_root)
  187.  
  188.         submenu.append_item(item)
  189.         uid += 1
  190.  
  191.     return toplevel
  192.  
  193.   def do_symlink(self, menu, path, target, insync_root):
  194.     dest = os.path.join(insync_root, target)
  195.     print('<doing symlink>', path, dest)
  196.     self._symlinks[path] = dest
  197.     os.symlink(path, dest)
  198.     if not ipc_insync(command='IS-ALIVE') and os.path.isdir(self.DEFAULT_INSYNC_CONFIG_PATH):
  199.       symcache = os.path.join(self.DEFAULT_INSYNC_CONFIG_PATH, 'symlink-cache.json')
  200.       with open(symcache, 'wb') as f:
  201.         json.dump(self._symlinks, f)
  202.  
  203.   def do_action(self, menu, file, method):
  204.     path = urllib.unquote(
  205.       file.get_uri()[len('file://'):]
  206.     )
  207.     ipc_insync(method=method, full_path=path)
  208.  
  209.   def update_file_info(self, file):
  210.     if file.get_uri_scheme() != 'file':
  211.       return
  212.  
  213.     filename = urllib.unquote(file.get_uri()[len('file://'):])
  214.     status = ipc_insync(command='GET-FILE-STATUS', full_path=filename)
  215.     if DEV_MODE:
  216.       print filename, status
  217.     if status and status != 'UNKNOWN':
  218.       emblem = _EMBLEM_KEYS[status]
  219.       file.add_emblem(emblem)
  220.     file.invalidate_extension_info()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement