[26802] in Source-Commits

home help back first fref pref prev next nref lref last post

/svn/athena r25763 - in trunk/debathena/debathena/nautilus-afs: . debian

daemon@ATHENA.MIT.EDU (Jonathan D Reed)
Thu Sep 13 14:37:08 2012

Date: Thu, 13 Sep 2012 14:37:00 -0400
From: Jonathan D Reed <jdreed@MIT.EDU>
Message-Id: <201209131837.q8DIb0XF014919@drugstore.mit.edu>
To: source-commits@MIT.EDU
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Author: jdreed
Date: 2012-09-13 14:37:00 -0400 (Thu, 13 Sep 2012)
New Revision: 25763

Added:
   trunk/debathena/debathena/nautilus-afs/afs-property-page.ui
Removed:
   trunk/debathena/debathena/nautilus-afs/afs-property-page.glade
Modified:
   trunk/debathena/debathena/nautilus-afs/afs-property-page.py
   trunk/debathena/debathena/nautilus-afs/debian/changelog
   trunk/debathena/debathena/nautilus-afs/debian/control
   trunk/debathena/debathena/nautilus-afs/debian/debathena-nautilus-afs.install
Log:
In nautilus-afs:
  * Complete rewrite for Nautilus 3 and Gtk3
  * Does not depend on pyafs


Modified: trunk/debathena/debathena/nautilus-afs/afs-property-page.py
===================================================================
--- trunk/debathena/debathena/nautilus-afs/afs-property-page.py	2012-09-12 17:57:33 UTC (rev 25762)
+++ trunk/debathena/debathena/nautilus-afs/afs-property-page.py	2012-09-13 18:37:00 UTC (rev 25763)
@@ -1,321 +1,469 @@
 import urllib
+import sys
 import os
-import gtk
-import gtk.glade
+import subprocess
+import errno
+import textwrap
+import pwd
 import re
-import nautilus
-import subprocess
+from gi.repository import Nautilus, Gtk, GObject, GLib, Gdk
 
-gladeFile = "/usr/share/debathena-nautilus-afs-integration/afs-property-page.glade"
+UI_FILE="/usr/share/debathena-nautilus-afs/afs-property-page.ui"
+# Paths that are likely in AFS.  This should be an inclusive list, and
+# we check for EINVAL when initially fetching the ACL.
+AFS_PATHS=("/afs", "/mit")
+# Valid rights for site-specific permissions
+VALID_SITE_RIGHTS="ABCDEFGH"
 
-import sys
-try:
-    import afs.acl
-except ImportError:
-    pass
+class AFSAclException(OSError):
+    def __init__(self, errnoOrMsg, message=None):
+        if message is None:
+            message = errnoOrMsg
+            errnoOrMsg = None
+        OSError.__init__(self, errnoOrMsg, message)
+        self.message = message
 
+class AFSAcl:
+    # Escape the ampersand because these are tooltips and go through Pango
+    # and maybe everything in Gtk3 does?
+    _specialEntities = { "system:anyuser": "Any anonymous user or web browser",
+                         "system:authuser": "Any MIT user",
+                         "system:expunge": "The IS&amp;T automated expunger",
+                         "system:administrators": "An IS&amp;T AFS administrator"}
+    
+    _englishRights = { "rlidwka": "all permissions",
+                       "rlidwk": "write permissions",
+                       "rl": "read permissions"}
+
+    def __init__(self, path):
+        if not os.path.exists(path):
+            raise AFSAclException(errno.ENOENT, "That path does not exist")
+        self.path = path
+        self._loadAcl()
+        
+    def _loadAcl(self):
+        fsla = subprocess.Popen(["fs", "listacl", "-path", self.path],
+                                stdout=subprocess.PIPE, 
+                                stderr=subprocess.PIPE)
+        (out, err) = fsla.communicate()
+        if fsla.returncode != 0:
+            if err.startswith("fs: Invalid argument"):
+                raise AFSAclException(errno.EINVAL, err.strip())
+            elif err.startswith("fs: You don't have the required access rights"):
+                raise AFSAclException(errno.EACCES, err.strip())
+            else:
+                raise AFSAclException(err.strip())
+        else:
+            self._parseACL(out)
+    
+    def _parseACL(self, fsla):
+        self.pos = {}
+        self.neg = {}
+        lines = fsla.splitlines()
+        # If a directory has no normal rights, we have no idea what's going on
+        if "Normal rights:" not in lines:
+            raise AFSAclException("No normal rights found while parsing?")
+        posidx = lines.index("Normal rights:")
+        try:
+            negidx = lines.index("Negative rights:")
+        except ValueError:
+            negidx = None
+        if negidx is None:
+            self.pos = self._parseEntries(lines[posidx+1:])
+        else:
+            self.pos = self._parseEntries(lines[posidx+1:negidx])
+            self.neg = self._parseEntries(lines[negidx+1:])
+
+    def _parseEntries(self, entList):
+        rv = {}
+        for i in entList:
+            (name, acl) = i.strip().split()
+            rv[name] = acl
+        return rv
+
+    def _setacl(self, entity, rights, negative=False):
+        cmdlist = ["fs", "setacl", "-dir", self.path, "-acl", entity, rights]
+        if negative:
+            cmdlist.append("-negative")
+        fsla = subprocess.Popen(cmdlist,
+                                stdout=subprocess.PIPE, 
+                                stderr=subprocess.PIPE)
+        (out, err) = fsla.communicate()
+        if fsla.returncode != 0:
+            if err.startswith("fs: Invalid argument"):
+                raise AFSAclException(errno.EINVAL, err.strip())
+            elif err.startswith("fs: You don't have the required access rights"):
+                raise AFSAclException(errno.EACCES, err.strip())
+            elif err.startswith("fs: You can not change a backup or readonly volume"):
+                raise AFSAclException(errno.EROFS, err.strip())
+            else:
+                raise AFSAclException(err.strip())
+    
+    @classmethod
+    def isDeactivatedUser(cls, ent):
+        if not ent.startswith('-'):
+            return False
+        try:
+            uid = int(ent)
+            return (uid < 0)
+        except ValueError:
+            pass
+        return False
+
+    @classmethod
+    def entityToEnglish(cls, ent):
+        if ent in cls._specialEntities:
+            return cls._specialEntities[ent]
+        if ent.startswith('system:'):
+            return "The Moira group " + ent
+        try:
+            pwent = pwd.getpwnam(ent)
+            return "%s (%s)" % (pwent[4].split(',')[0], ent)
+        except KeyError:
+            pass
+        return "The %suser '%s'" % ("deactivated " if cls.isDeactivatedUser(ent) else "", ent)
+
+    @classmethod
+    def rightsToEnglish(cls, rightString):
+        # TODO: str.format() or Formatter
+        rights=rightString
+        site=re.sub(r'[rlidwka]', '', rights)
+        if site:
+            rights=rightString.replace(site, '')
+        english=""
+        for right in sorted(cls._englishRights, None, None, True):
+            if right in rights:
+                english += cls._englishRights[right]
+                break
+        if re.search(r'[idwka]', rights):
+            if english and right == "rl":
+                english += " and '%s' permissions" % (rights)
+        if not english:
+            english += "'%s' permissions" % (rights)
+        if rights == "l":
+            english = "permission to list, but not read, files and directories"
+        if site:
+            english += "\nas well as site-specific permission(s) '%s'" % (site)
+        return english
+
+
+
 class AFSPermissionsPane():
     def __init__(self, fp):
         self.filepath = fp
         self.acl = None
-        self.noPermissions = False
-        self.inAFS = True
-        if 'afs.acl' not in sys.modules.keys():
-            if (not self.filepath.startswith("/afs")) and (not self.filepath.startswith("/mit")):
-                self.inAFS = False
-            b = gtk.VBox()
-            l = gtk.Label("Sorry, this feature is not available on this platform.\n(afs.acl module could not be imported.)\n")
-            l.set_single_line_mode(False)
-            l.show()
-            b.pack_start(l)
-            b.show()
-            self.rootWidget = b
+        self.rootWidget = None
+        self.inAFS = False
+        for item in AFS_PATHS:
+            if self.filepath.startswith(item):
+                self.inAFS=True
+        self.builder = Gtk.Builder()
+        ui = os.getenv('DA_AFS_PROPERTY_PAGE_UI')
+        if not ui:
+            ui = UI_FILE
+        try:
+            self.builder.add_from_file(ui)
+        except GLib.GError as e:
+            self.rootWidget = self._errorBox("Could not load the AFS user interface.", e.message)
             return
-        if not os.path.exists(gladeFile):
-            b = gtk.VBox()
-            l = gtk.Label("Cannot find Glade file:\n%s" % gladeFile)
-            l.set_single_line_mode(False)
-            l.show()
-            b.pack_start(l)
-            b.show()
-            self.rootWidget = b
-            return
-        self.widgets = gtk.glade.XML(gladeFile, "vboxMain")
-        self.rootWidget = self.widgets.get_widget("vboxMain")
+
         try:
-            self.acl = afs.acl.ACL.retrieve(self.filepath)
-        except OSError as (errno, errstr):
-            if errno == 13:
-                self.noPermissions = True
-                self.widgets = gtk.glade.XML(gladeFile, "vboxNoPerms")
-                self.rootWidget = self.widgets.get_widget("vboxNoPerms")
-            elif errno == 22:
+            self.acl = AFSAcl(self.filepath)
+        except OSError as e:
+            if e.errno == errno.EACCES:
+                self.rootWidget = self.builder.get_object("vboxNoPerms")
+                return
+            elif e.errno == errno.EINVAL:
                 self.inAFS = False
+                return
+            else:
+                self.rootWidget = self._errorBox("Unexpected error!", str(e))
+                return
+
+        self.rootWidget = self.builder.get_object("vboxMain")
+        handlers = {
+            "add_clicked_cb": self.addEntry,
+            "edit_clicked_cb": self.editEntry,
+            "remove_clicked_cb": self.removeEntry,
+            "access_combo_changed_cb": self.dlgAccessChanged,
+            "entity_combo_changed_cb": self.dlgEntityChanged,
+            "entity_text_changed_cb": self.dlgEntityTextChanged,
+            "group_checkbox_toggled_cb": self.groupToggled,
+            "aclview_row_activated_cb": self.editEntry,
+            "siteperms_entity_insert_text_cb": self.validateSitePerms,
+        }
+        self.builder.connect_signals(handlers)
+        self.addDlg = self.builder.get_object("aclDialog")
+        self._refreshAclUI()
         
-                
-        if self.acl != None:
-            self._initWidgets()
-            self.refreshACL()
+    # Used to fail gracefully when we can't find the UI.
+    def _errorBox(self, msg, longMsg=None):
+        vbox = Gtk.VBox(homogeneous=False, spacing=0)
+        vbox.show()
+        msgTxt = msg
+        if longMsg is not None:
+            msgTxt += "\n\nError details:\n"
+            msgTxt += textwrap.fill(longMsg, 60)
+        label = Gtk.Label(msgTxt)
+        label.show()
+        vbox.pack_start(label, True, True, 0)
+        return vbox
+    
+    # Reload the UI based on the ACL.
+    def _refreshAclUI(self):
+        self.builder.get_object("lblPath").set_text(self.filepath)
+        self.builder.get_object("lblPath").set_tooltip_text(self.filepath)
+        self.store = self.builder.get_object("aclListStore")
+        self.store.clear()
+        if self.acl is not None:
+            for i in self.acl.pos.items():
+                self.store.append(i + (False, AFSAcl.entityToEnglish(i[0]) + " has " + AFSAcl.rightsToEnglish(i[1]), i[1], 'gtk-yes', Gtk.IconSize.MENU))
+            for i in self.acl.neg.items():
+                self.store.append(i + (True, AFSAcl.entityToEnglish(i[0]) + " does <b>not</b> have " + AFSAcl.rightsToEnglish(i[1]), i[1] + "  <i>(negative rights)</i>",'gtk-no', Gtk.IconSize.MENU))
 
-    def _initWidgets(self):
-        self.listStore = gtk.ListStore(str, int, bool, str)
-        self.buttonActions = {"btnAdd": self.addItem,
-                              "btnEdit": self.editItem,
-                              "btnRemove": self.removeItem}
-        for btn in self.buttonActions:
-            self.widgets.get_widget(btn).connect("clicked", self.buttonHandler)
-        self.treeView = self.widgets.get_widget("tvACL")
-        self.treeView.set_model(self.listStore)
-        colUser = gtk.TreeViewColumn('User')
-        colBits = gtk.TreeViewColumn('Permissions')
-        self.treeView.append_column(colUser)
-        self.treeView.append_column(colBits)
-        cellRenderer = gtk.CellRendererText()
-        colUser.pack_start(cellRenderer)
-        colBits.pack_start(cellRenderer)
-        colUser.set_cell_data_func(cellRenderer, self.renderUser)
-        colBits.set_cell_data_func(cellRenderer, self.renderBitmask)
-        self.treeView.set_tooltip_column(3)
+    # Reload the ACL.  Note that the initial loading happens in the
+    # constructor, because if there are no permissions, we just want 
+    # display a vbox, not a dialog (which is obnoxious from a UI
+    # perspective
+    def _reloadAcl(self):
+        self.acl = None
+        try:
+            self.acl = AFSAcl(self.filepath)
+        except OSError as e:
+            if e.errno == errno.EACCES:
+                self._errDialog("You no longer have permissions to view the ACL for this diectory.")
+            else:
+                self._errDialog("Unexpected error!", "Full error text:\n" + str(e))
+            # Disable the UI, we can't continue at this point
+            self.rootWidget.set_sensitive(False)
+        # And refresh or clear the UI
+        self._refreshAclUI()
 
-    def getWidget(self):
-        if self.noPermissions:
-            return NoPermissionsPane()
+    # Get the currently selected entry
+    def _getSelectedEntry(self):
+        tree = self.builder.get_object("tvACL")
+        model, it = tree.get_selection().get_selected()
+        if it != None:
+            return model[it]
         else:
-            return self.widgets.get_widget("vboxMain")
+            return None
+    
+    # Apply and ACL and deal with the UI accordingly
+    def _setacl(self, entity, rights, negative=False):
+        try:
+            self.acl._setacl(entity, rights, negative)
+        except OSError as e:
+            if e.errno == errno.EACCES:
+                self._errDialog("You don't have permissions to change the ACL on this directory.")
+            elif e.errno == errno.EINVAL:
+                self._errDialog("Error: %s\n\n(This is typically caused by specifying a user or group that doesn't exist.)" % (e.message))
+            elif e.errno == errno.EROFS:
+                self._errDialog("This is a read-only filesystem and cannot be changed.","(Hint: If you're trying to change your OldFiles directory,\nyou can't, because it's a nightly snapshot.)")
+            else:
+                self._errDialog("Unexpected error!", e.message)
+        self._reloadAcl()
 
-    def renderBitmask(self, col, cell, model, iter, user_data=None):
-        rights = afs.acl.showRights(model.get_value(iter, 1))
-        if afs.acl.rightsToEnglish(rights):
-            rights += '  (' + afs.acl.rightsToEnglish(rights) + ')'
-        cell.set_property('text', rights)
+    # Set the "access" combobox from an ACL
+    def _setAccessComboFromACL(self, acl):
+        for row in self.builder.get_object("accessCombo").get_model():
+            if row[0] == acl:
+                self.builder.get_object("accessCombo").set_active_iter(row.iter)
+                break
 
-    def renderUser(self, col, cell, model, iter, user_data=None):
-        cell.set_property('strikethrough-set', True)
-        isNeg = model.get_value(iter, 2)
-        if isNeg:
-            cell.set_property('strikethrough',True)
-        else:
-            cell.set_property('strikethrough', False)
-        cell.set_property('text', model.get_value(iter, 0))
+    # Prepare the ACL dialog based on what we want to do
+    def _prepareAclDialog(self, editMode=False, entity=None, rights=None, negative=False):
+        self.addDlg.set_transient_for(self._getParentWindow())
+        self.addDlg.set_title("Change permissions for '%s'" % (entity) if editMode else "Add an entry")
+        # Set the "OK" button to unsensitive until something is in the text field
+        self.builder.get_object("aclDlgOK").set_sensitive(False)
+        # Clear the text field
+        self.builder.get_object("entityText").set_text("")
+        # Default to "specify manually" for the ACL:
+        self._setAccessComboFromACL("-" if editMode else "rl")
+        # No need to call dlgAccessChanged here, because set_active_iter will
+        # emit the signal.  .set-active() with a row number will not.
+        self.builder.get_object("accessNegative").set_active(negative)
+        sitePerms = ""
+        if rights:
+            sitePerms = re.sub(r'[rlidwka]', '', rights)
+        self.builder.get_object("sitePerms").set_text(sitePerms)
+        self.builder.get_object("sitePermsExpander").set_expanded(sitePerms != "")
+        self.builder.get_object("entityCombo").set_sensitive(not editMode)
+        self.builder.get_object("entityText").set_sensitive(not editMode)
+        self.builder.get_object("entityIsGroup").set_sensitive(not editMode)
+        self.builder.get_object("negPermsExpander").set_expanded(negative)
+        # We don't support turning negative rights to positive ones
+        self.builder.get_object("accessNegative").set_sensitive(not editMode)
+        if editMode:
+            self.dlgSetRightsFromString(rights, True)
+            self.builder.get_object("entityText").set_text(entity)
 
-    def refreshACL(self):
-        if self.acl != None:
-            self.widgets.get_widget("lblPath").set_text(self.filepath)
-            self.listStore.clear()
-            try:
-                self.acl = afs.acl.ACL.retrieve(self.filepath)
-            except OSError as (errno, errstr):
-                if errno == 13:
-                    msg = gtk.MessageDialog(None, gtk.DIALOG_MODAL,
-                                            gtk.MESSAGE_QUESTION, 
-                                            gtk.BUTTONS_OK,
-                                            "Cannot reload ACL for directory.  Perhaps your AFS tokens expired or someone else changed permissions?")
-                    msg.run()
-                    msg.destroy()
-            for i in self.acl.pos.items():
-                self.listStore.append(i + (False,i[0]))
-            for i in self.acl.neg.items():
-                self.listStore.append(i + (True,i[0] + ' (Negative Rights)'))
+    # Turn the dialog's state back into somthing that can be applied
+    def _applyRightsFromDialog(self):
+            entity = self.builder.get_object("entityText").get_text().strip()
+            rights=""
+            for widget in self.builder.get_object("rightsBox").get_children():
+                rights += widget.get_label() if widget.get_active() else ""
+            rights += self.builder.get_object("sitePerms").get_text().strip()
+            self._setacl(entity, rights, self.builder.get_object("accessNegative").get_active())
+        
+    # "Add" button callback
+    def addEntry(self, widget):
+        self._prepareAclDialog()
+        if self.addDlg.run() == Gtk.ResponseType.OK:
+            self._applyRightsFromDialog()
+        self.addDlg.hide()
 
-    def removeItem(self):
-        selectedRowIter = self.treeView.get_selection().get_selected()[1]
-        if selectedRowIter == None:
+    # "Edit" button callback
+    def editEntry(self, widget, row=None, treeCol=None):
+        row = self._getSelectedEntry()
+        if row is None:
             return
-        entity, isNeg = self.listStore.get(selectedRowIter, 0, 2)
-        if isNeg:
-            msg = gtk.MessageDialog(None,gtk.DIALOG_MODAL,
-                                    gtk.MESSAGE_INFO, gtk.BUTTONS_OK,
-                                    "Removing negative ACLS is not yet supported")
-            msg.run()
-            msg.destroy()
-            return
-        msg = 'Are you sure you want to remove %s from the AFS ACL for this directory?'
-        if (entity == os.getenv('ATHENA_USER')) or (entity == os.getenv('USER')):
-            msg += "\n\nWARNING: You are about to remove yourself from this ACL!"
-        question = gtk.MessageDialog(None, gtk.DIALOG_MODAL,
-                                     gtk.MESSAGE_QUESTION,
-                                     gtk.BUTTONS_YES_NO,
-                                     msg % entity)
-        response = question.run()
-        question.destroy()
-        if response == gtk.RESPONSE_YES:
-            self.acl.remove(entity)
-            self.applyChanges()
+        self._prepareAclDialog(True, row[0], row[1], row[2])
+        if self.addDlg.run() == Gtk.ResponseType.OK:
+            self._applyRightsFromDialog()
+        self.addDlg.hide()
 
-    def applyChanges(self):
-        try:
-            self.acl.apply(self.filepath)
-        except OSError as (errno, errstr):
-            msg = gtk.MessageDialog(None, gtk.DIALOG_MODAL,
-                                    gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
-                                    "Error: %s" % errstr)
-            msg.run()
-            msg.destroy()
-        self.refreshACL()
+    # "Remove" button callback
+    def removeEntry(self, widget):
+        row = self._getSelectedEntry()
+        if row is not None:
+            if row[0] == os.getenv("USER"):
+                if not self._confirmDialog("Are you sure you want to remove yourself from the ACL?"):
+                    return
+            self._setacl(row[0], "none", row[2])
 
-    def editItem(self):
-        selectedRowIter = self.treeView.get_selection().get_selected()[1]
-        if selectedRowIter == None:
-            return
-        entity, acl, isNeg = self.listStore.get(selectedRowIter, 0, 1, 2)
-        if isNeg:
-            msg = gtk.MessageDialog(None,gtk.DIALOG_MODAL,
-                                    gtk.MESSAGE_INFO, gtk.BUTTONS_OK,
-                                    "Editing negative ACLS is not yet supported")
-            msg.run()
-            msg.destroy()
-            return
-        add = AddEditACLDialog()
-        add.editMode(entity, afs.acl.showRights(acl))
-        response = add.run()
-        while (response == gtk.RESPONSE_OK) and not add.validate():
-            response=add.run()
-        if (response == gtk.RESPONSE_OK):
-            self.acl.set(add.getEntity(), afs.acl.readRights(add.getAcl()))
-            self.applyChanges()
-        add.destroy()
+    # Set the "rights" checkboxes from an ACL string.
+    # and whether they should be enabled or disabled
+    def dlgSetRightsFromString(self, rightString, enable):
+        for widget in self.builder.get_object("rightsBox").get_children():
+            widget.set_sensitive(enable)
+            if widget.get_label() in rightString:
+                widget.set_active(True)
+            else:
+                widget.set_active(False)
 
-    def addItem(self):
-        add = AddEditACLDialog()
-        response = add.run()
-        while (response == gtk.RESPONSE_OK) and not add.validate():
-            response=add.run()
-        if (response == gtk.RESPONSE_OK):
-            self.acl.set(add.getEntity(), afs.acl.readRights(add.getAcl()))
-            self.applyChanges()
-        add.destroy()
+    # callback for "changed" signal on combobox
+    # Update the checkboxes to match the combobox
+    def dlgAccessChanged(self, widget):
+        iter = widget.get_active_iter()
+        if iter is not None:
+            bits=widget.get_model()[iter][0]
+            self.dlgSetRightsFromString(bits, bits == "-")
 
-    def buttonHandler(self, btn):
-        self.buttonActions[btn.name]()
+    # Callback for the "toggled" signal on the "Is a group" checkbox
+    def groupToggled(self, widget):
+        ent = self.builder.get_object("entityText").get_text()
+        if widget.get_active():
+            if not ent.startswith("system:"):
+                self.builder.get_object("entityText").set_text("system:" + ent)
+        else:
+            self.builder.get_object("entityText").set_text(re.sub(r'^system:', '', ent))
 
-class AddEditACLDialog():
-    def __init__(self, editMode=False):
-        self.widgets = gtk.glade.XML(gladeFile, "dlgAdd")
-        self.window = self.widgets.get_widget("dlgAdd")
-        self.entry = self.widgets.get_widget("entryName")
-        for rb in self.widgets.get_widget_prefix("rb"):
-            rb.connect("toggled", self.toggleHandler)
+    # Callback for GtkEditable's "insert-text" signal on the "site permissions entry
+    # If it's a zero-length text insertion, then it's "valid"
+    # If it's a signal character, ensure it's in the valid set
+    # For anything else, display an excalmation point in the box and
+    # stop signal emission
+    def validateSitePerms(self, widget, text, text_len, ptr):
+        if text_len == 0:
+            return True
+        if text_len == 1:
+            if text in VALID_SITE_RIGHTS:
+                widget.set_icon_from_stock(Gtk.EntryIconPosition.SECONDARY, None)
+                return True
+        widget.set_icon_from_stock(Gtk.EntryIconPosition.SECONDARY, 'gtk-dialog-warning')
+        widget.set_icon_activatable(Gtk.EntryIconPosition.SECONDARY, False)
+        widget.set_icon_tooltip_text(Gtk.EntryIconPosition.SECONDARY, "Only the characters '%s' are allowed" % (VALID_SITE_RIGHTS))
+        widget.stop_emission('insert-text')
 
-    def editMode(self, user, acl):
-        self.window.set_title('Edit ACL Entry')
-        self.entry.set_text(user)
-        self.entry.set_editable(False)
-        self.widgets.get_widget('lblType').set_property('visible', False)
-        for rb in self.widgets.get_widget_prefix("rb"):
-            rb.set_property('visible', False)
-        self.widgets.get_widget("cbAcl").get_child().set_text(acl)
+    # Callback for the "changed" signal on the "entity" Entry.
+    def dlgEntityTextChanged(self, widget):
+        self.builder.get_object("entityIsGroup").set_active(widget.get_text().startswith("system:"))
+        self.builder.get_object("aclDlgOK").set_sensitive(widget.get_text().strip() != "")
 
-    def run(self):
-        return self.window.run()
+    # Callback for the "entity" combobox
+    def dlgEntityChanged(self, widget):
+        iter = widget.get_active_iter()
+        if iter is not None:
+            name=widget.get_model()[iter][0]
+            if name == "-":
+                self.builder.get_object("entityText").set_sensitive(True)
+                self.builder.get_object("entityIsGroup").set_sensitive(True)
+            else:
+                self.builder.get_object("entityText").set_sensitive(False)
+                self.builder.get_object("entityText").set_text(name)
+                self.builder.get_object("entityIsGroup").set_sensitive(False)
 
-    def destroy(self):
-        self.window.destroy()
+    # Convenience function to get the parent window, since we don't have 
+    # access to it directly.
+    def _getParentWindow(self):
+        # Probably not the best idea
+        parent = self.rootWidget.get_parent()
+        while parent is not None:
+            if isinstance(parent, Gtk.Window):
+                break
+            parent = parent.get_parent()
+        return parent
 
-    def toggleHandler(self, radioBtn, data=None):
-        if not radioBtn.get_active():
-            return
-        if radioBtn.name == "rbUser":
-            self.entry.set_text("")
-            self.entry.set_editable(True)
-        if radioBtn.name == "rbGroup":
-            # Do this in the GUI, so that people can override it if they
-            # really know what they're doing, or are using user groups or
-            # something.  
-            if re.search(":", self.entry.get_text()) == None:
-                self.entry.set_text("system:")
-            self.entry.set_editable(True)
-        if radioBtn.name == "rbAuthuser":
-            self.entry.set_text("system:authuser")
-            self.entry.set_editable(False)
-        if radioBtn.name == "rbAnyuser":
-            self.entry.set_text("system:anyuser")
-            self.entry.set_editable(False)
 
-    def getEntity(self):
-        return self.entry.get_text().strip()
+    # Convenience functions
+    def _errDialog(self, message, secondaryMsg=None):
+        dlg = Gtk.MessageDialog(self._getParentWindow(),
+                                Gtk.DialogFlags.DESTROY_WITH_PARENT,
+                                Gtk.MessageType.ERROR,
+                                Gtk.ButtonsType.CLOSE,
+                                message)
+        dlg.set_title("Error")
+        if secondaryMsg:
+            dlg.format_secondary_text(secondaryMsg)
+        dlg.run()
+        dlg.destroy()
 
-    def getAcl(self):
-        combobox = self.widgets.get_widget("cbAcl")
-        model = combobox.get_model()
-        active = combobox.get_active()
-        if active < 0:
-            # in case someone typos whitespace in the box, which would parse
-            # to "none".   
-            acl = combobox.get_child().get_text().strip()
-        else:
-            acl = model[active][0]
-        return acl
+    def _confirmDialog(self, message, secondaryMsg=None):
+        dlg = Gtk.MessageDialog(self._getParentWindow(),
+                                Gtk.DialogFlags.DESTROY_WITH_PARENT,
+                                Gtk.MessageType.QUESTION,
+                                Gtk.ButtonsType.YES_NO,
+                                message)
+        dlg.set_title("Confirm")
+        if secondaryMsg:
+            dlg.format_secondary_text(secondaryMsg)
+        rval = dlg.run()
+        dlg.destroy()
+        return (rval == Gtk.ResponseType.YES)
 
-    def validate(self):
-        if not self.getEntity():
-            msg = gtk.MessageDialog(self.window,gtk.DIALOG_MODAL,
-                                    gtk.MESSAGE_INFO, gtk.BUTTONS_OK,
-                                    "You did not specify the user or group.")
-            msg.run()
-            msg.destroy()
-            return False
 
-        acl = self.getAcl()
-        if not acl:
-            msg = gtk.MessageDialog(self.window,gtk.DIALOG_MODAL,
-                                    gtk.MESSAGE_INFO, gtk.BUTTONS_OK,
-                                    "You did not specify the permissions.")
-            msg.run()
-            msg.destroy()
-            return False
-        try:
-            r = afs.acl.readRights(acl)
-        except ValueError:
-            msg = gtk.MessageDialog(self.window,gtk.DIALOG_MODAL,
-                                    gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
-                                    "Invalid permissions '%s'.  Select one from the drop down list or type a valid AFS permission string into the box provided." % acl)
-            msg.run()
-            msg.destroy()
-            return False
-        if re.search('[widka]', afs.acl.showRights(r)) and self.widgets.get_widget("rbAnyuser").get_active():
-            msg = gtk.MessageDialog(self.window,gtk.DIALOG_MODAL,
-                                    gtk.MESSAGE_WARNING, gtk.BUTTONS_YES_NO,
-                                    "WARNING:  You are attempting to assign '%s' permissions to system:anyuser (i.e. any user, anywhere on the Internet).  This is EXTREMELY DANGEROUS.  You may experience data loss and your directory may be used by spammers to create malicious websites.  MIT IS&T reserves the right to disable access to any AFS directories with these permissions.  Consider selecting 'All MIT Users' instead.\n\nAre you absolutely sure you want to continue?" % acl)
-
-            response = msg.run()
-            msg.destroy()
-            if response != gtk.RESPONSE_YES:
-                return False
-        return True
-
-
-class AFSPropertyPage(nautilus.PropertyPageProvider):
+class AFSPropertyPage(GObject.GObject, Nautilus.PropertyPageProvider):
     def __init__(self):
-        self.property_label = gtk.Label('AFS')
-
+        pass
+    
     def get_property_pages(self, files):
-        # Does not work for multiple selections
+        # Not supported for multiple selections
         if len(files) != 1:
             return
         
         file = files[0]
-        # Does not work on non-file:// URIs
+        # Not supported for other URIs
         if file.get_uri_scheme() != 'file':
             return
 
         # Only works on directories
+        # TODO: symlinks?
         if not file.is_directory():
             return
 
-#        if 'afs.acl' not in sys.modules.keys():
-#            return
+        # Should probably use urlparse, but meh
+        filepath = urllib.unquote(file.get_uri()[7:])
 
-        filepath = urllib.unquote(file.get_uri()[7:])
-        
+        self.property_label = Gtk.Label('AFS Permissions')
+        self.property_label.show()
+
         pane = AFSPermissionsPane(filepath)
 
-        # If the file does not appear to be in AFS, don't show the pane
-        if not pane.inAFS:
+        if not pane.inAFS or pane.rootWidget is None:
             return
-        
-        self.property_label.show()
 
-        return nautilus.PropertyPage("NautilusPython::afs",
-                                     self.property_label, 
-                                     pane.rootWidget),
-
+        return Nautilus.PropertyPage(name="NautilusPython::afs",
+                                     label=self.property_label, 
+                                     page=pane.rootWidget),

Added: trunk/debathena/debathena/nautilus-afs/afs-property-page.ui
===================================================================
--- trunk/debathena/debathena/nautilus-afs/afs-property-page.ui	                        (rev 0)
+++ trunk/debathena/debathena/nautilus-afs/afs-property-page.ui	2012-09-13 18:37:00 UTC (rev 25763)
@@ -0,0 +1,829 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.0 -->
+  <object class="GtkDialog" id="aclDialog">
+    <property name="can_focus">False</property>
+    <property name="border_width">5</property>
+    <property name="modal">True</property>
+    <property name="destroy_with_parent">True</property>
+    <property name="type_hint">dialog</property>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="dialog-vbox2">
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox" id="dialog-action_area2">
+            <property name="can_focus">False</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="button2">
+                <property name="label">gtk-cancel</property>
+                <property name="use_action_appearance">False</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_action_appearance">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="aclDlgOK">
+                <property name="label">gtk-ok</property>
+                <property name="use_action_appearance">False</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_action_appearance">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkBox" id="box1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkFrame" id="frame1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label_xalign">0</property>
+                <property name="shadow_type">none</property>
+                <child>
+                  <object class="GtkAlignment" id="alignment1">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="left_padding">12</property>
+                    <child>
+                      <object class="GtkBox" id="box2">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="orientation">vertical</property>
+                        <child>
+                          <object class="GtkBox" id="box5">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <child>
+                              <object class="GtkLabel" id="label3">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkComboBox" id="entityCombo">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="model">entityListStore</property>
+                                <property name="active">0</property>
+                                <signal name="changed" handler="entity_combo_changed_cb" swapped="no"/>
+                                <child>
+                                  <object class="GtkCellRendererText" id="cellrenderertext4"/>
+                                  <attributes>
+                                    <attribute name="text">1</attribute>
+                                  </attributes>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="expand">True</property>
+                                <property name="fill">True</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkEntry" id="entityText">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="invisible_char">•</property>
+                            <signal name="changed" handler="entity_text_changed_cb" swapped="no"/>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkCheckButton" id="entityIsGroup">
+                            <property name="label" translatable="yes">This is a Moira group</property>
+                            <property name="use_action_appearance">False</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="use_action_appearance">False</property>
+                            <property name="xalign">0</property>
+                            <property name="draw_indicator">True</property>
+                            <signal name="toggled" handler="group_checkbox_toggled_cb" swapped="no"/>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">2</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object class="GtkLabel" id="label1">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">&lt;b&gt;Entity&lt;/b&gt;</property>
+                    <property name="use_markup">True</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="padding">3</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkFrame" id="frame2">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label_xalign">0</property>
+                <property name="shadow_type">none</property>
+                <child>
+                  <object class="GtkAlignment" id="alignment2">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="left_padding">12</property>
+                    <child>
+                      <object class="GtkBox" id="box3">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="orientation">vertical</property>
+                        <child>
+                          <object class="GtkComboBox" id="accessCombo">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="model">rightsListStore</property>
+                            <property name="active">0</property>
+                            <signal name="changed" handler="access_combo_changed_cb" swapped="no"/>
+                            <child>
+                              <object class="GtkCellRendererText" id="cellrenderertext3"/>
+                              <attributes>
+                                <attribute name="text">1</attribute>
+                              </attributes>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkBox" id="rightsBox">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="homogeneous">True</property>
+                            <child>
+                              <object class="GtkCheckButton" id="checkbutton2">
+                                <property name="label" translatable="yes">r</property>
+                                <property name="use_action_appearance">False</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="use_action_appearance">False</property>
+                                <property name="xalign">0</property>
+                                <property name="draw_indicator">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkCheckButton" id="checkbutton3">
+                                <property name="label" translatable="yes">l</property>
+                                <property name="use_action_appearance">False</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="use_action_appearance">False</property>
+                                <property name="xalign">0</property>
+                                <property name="draw_indicator">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkCheckButton" id="checkbutton4">
+                                <property name="label" translatable="yes">i</property>
+                                <property name="use_action_appearance">False</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="use_action_appearance">False</property>
+                                <property name="xalign">0</property>
+                                <property name="draw_indicator">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">2</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkCheckButton" id="checkbutton5">
+                                <property name="label" translatable="yes">d</property>
+                                <property name="use_action_appearance">False</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="use_action_appearance">False</property>
+                                <property name="xalign">0</property>
+                                <property name="draw_indicator">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">3</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkCheckButton" id="checkbutton6">
+                                <property name="label" translatable="yes">w</property>
+                                <property name="use_action_appearance">False</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="use_action_appearance">False</property>
+                                <property name="xalign">0</property>
+                                <property name="draw_indicator">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">4</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkCheckButton" id="checkbutton7">
+                                <property name="label" translatable="yes">k</property>
+                                <property name="use_action_appearance">False</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="use_action_appearance">False</property>
+                                <property name="xalign">0</property>
+                                <property name="draw_indicator">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">5</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkCheckButton" id="checkbutton8">
+                                <property name="label" translatable="yes">a</property>
+                                <property name="use_action_appearance">False</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="use_action_appearance">False</property>
+                                <property name="xalign">0</property>
+                                <property name="draw_indicator">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">6</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkSeparator" id="separator1">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">2</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkExpander" id="negPermsExpander">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <child>
+                              <object class="GtkBox" id="box6">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="margin_left">10</property>
+                                <property name="orientation">vertical</property>
+                                <child>
+                                  <object class="GtkCheckButton" id="accessNegative">
+                                    <property name="label" translatable="yes">Negative permissions</property>
+                                    <property name="use_action_appearance">False</property>
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="receives_default">False</property>
+                                    <property name="use_action_appearance">False</property>
+                                    <property name="xalign">0</property>
+                                    <property name="draw_indicator">True</property>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
+                              </object>
+                            </child>
+                            <child type="label">
+                              <object class="GtkLabel" id="label7">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="label" translatable="yes">Advanced Options</property>
+                              </object>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="padding">5</property>
+                            <property name="position">3</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkExpander" id="sitePermsExpander">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <child>
+                              <object class="GtkBox" id="box7">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="margin_left">10</property>
+                                <property name="spacing">5</property>
+                                <child>
+                                  <object class="GtkEntry" id="sitePerms">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="invisible_char">•</property>
+                                    <property name="width_chars">8</property>
+                                    <property name="invisible_char_set">True</property>
+                                    <signal name="insert-text" handler="siteperms_entity_insert_text_cb" swapped="no"/>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkLabel" id="label10">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <property name="label" translatable="yes">(not generally needed)</property>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">1</property>
+                                  </packing>
+                                </child>
+                              </object>
+                            </child>
+                            <child type="label">
+                              <object class="GtkLabel" id="label4">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="label" translatable="yes">Site-specific Permissions</property>
+                              </object>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">4</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object class="GtkLabel" id="label2">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">&lt;b&gt;Access&lt;/b&gt;</property>
+                    <property name="use_markup">True</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="padding">3</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="-6">button2</action-widget>
+      <action-widget response="-5">aclDlgOK</action-widget>
+    </action-widgets>
+  </object>
+  <object class="GtkListStore" id="aclListStore">
+    <columns>
+      <!-- column-name entity -->
+      <column type="gchararray"/>
+      <!-- column-name rights -->
+      <column type="gchararray"/>
+      <!-- column-name strike -->
+      <column type="gboolean"/>
+      <!-- column-name tooltip -->
+      <column type="gchararray"/>
+      <!-- column-name rightsMarkup -->
+      <column type="gchararray"/>
+      <!-- column-name icon-id -->
+      <column type="gchararray"/>
+      <!-- column-name iconsize -->
+      <column type="GtkIconSize"/>
+    </columns>
+  </object>
+  <object class="GtkListStore" id="entityListStore">
+    <columns>
+      <!-- column-name entity -->
+      <column type="gchararray"/>
+      <!-- column-name english -->
+      <column type="gchararray"/>
+    </columns>
+    <data>
+      <row>
+        <col id="0" translatable="yes">-</col>
+        <col id="1" translatable="yes">Specify manually:</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">system:anyuser</col>
+        <col id="1" translatable="yes">The general public</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">system:authuser</col>
+        <col id="1" translatable="yes">Anyone at MIT</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">system:htaccess.mit</col>
+        <col id="1" translatable="yes">Use .htaccess.mit file</col>
+      </row>
+    </data>
+  </object>
+  <object class="GtkListStore" id="rightsListStore">
+    <columns>
+      <!-- column-name rights -->
+      <column type="gchararray"/>
+      <!-- column-name english -->
+      <column type="gchararray"/>
+    </columns>
+    <data>
+      <row>
+        <col id="0" translatable="yes">-</col>
+        <col id="1" translatable="yes">Specify manually:</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">rlidwka</col>
+        <col id="1" translatable="yes">All permissions</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">rlidwk</col>
+        <col id="1" translatable="yes">Write and edit files</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">rl</col>
+        <col id="1" translatable="yes">Read files</col>
+      </row>
+    </data>
+  </object>
+  <object class="GtkVBox" id="vboxMain">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="border_width">5</property>
+    <property name="spacing">5</property>
+    <child>
+      <object class="GtkHBox" id="hbox5">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="spacing">3</property>
+        <child>
+          <object class="GtkLabel" id="label5">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="label" translatable="yes">AFS Permissions for</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="lblPath">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="label" translatable="yes">&lt;path&gt;</property>
+            <property name="ellipsize">middle</property>
+            <property name="max_width_chars">45</property>
+            <attributes>
+              <attribute name="style" value="italic"/>
+            </attributes>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+        <property name="position">0</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkScrolledWindow" id="scrolledwindow1">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="hscrollbar_policy">never</property>
+        <property name="shadow_type">in</property>
+        <child>
+          <object class="GtkTreeView" id="tvACL">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="model">aclListStore</property>
+            <property name="tooltip_column">3</property>
+            <signal name="row-activated" handler="aclview_row_activated_cb" swapped="no"/>
+            <child internal-child="selection">
+              <object class="GtkTreeSelection" id="treeview-selection1"/>
+            </child>
+            <child>
+              <object class="GtkTreeViewColumn" id="treeviewcolumn1">
+                <property name="title" translatable="yes">Entity</property>
+                <property name="sort_indicator">True</property>
+                <property name="sort_column_id">0</property>
+                <child>
+                  <object class="GtkCellRendererPixbuf" id="cellrendererpixbuf1"/>
+                  <attributes>
+                    <attribute name="stock-id">5</attribute>
+                    <attribute name="stock-size">6</attribute>
+                  </attributes>
+                </child>
+                <child>
+                  <object class="GtkCellRendererText" id="cellrenderertext1"/>
+                  <attributes>
+                    <attribute name="text">0</attribute>
+                  </attributes>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkTreeViewColumn" id="treeviewcolumn2">
+                <property name="title" translatable="yes">Access</property>
+                <child>
+                  <object class="GtkCellRendererText" id="cellrenderertext2"/>
+                  <attributes>
+                    <attribute name="markup">4</attribute>
+                  </attributes>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">True</property>
+        <property name="fill">True</property>
+        <property name="position">1</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkHButtonBox" id="hbuttonbox1">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <child>
+          <object class="GtkButton" id="btnAdd">
+            <property name="label">gtk-add</property>
+            <property name="use_action_appearance">False</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="can_default">True</property>
+            <property name="receives_default">False</property>
+            <property name="use_action_appearance">False</property>
+            <property name="use_stock">True</property>
+            <signal name="clicked" handler="add_clicked_cb" swapped="no"/>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="btnEdit">
+            <property name="label">gtk-edit</property>
+            <property name="use_action_appearance">False</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="can_default">True</property>
+            <property name="receives_default">False</property>
+            <property name="use_action_appearance">False</property>
+            <property name="use_stock">True</property>
+            <signal name="clicked" handler="edit_clicked_cb" swapped="no"/>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="btnRemove">
+            <property name="label">gtk-remove</property>
+            <property name="use_action_appearance">False</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="can_default">True</property>
+            <property name="receives_default">False</property>
+            <property name="use_action_appearance">False</property>
+            <property name="use_stock">True</property>
+            <signal name="clicked" handler="remove_clicked_cb" swapped="no"/>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">False</property>
+        <property name="position">2</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkHSeparator" id="hseparator3">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">False</property>
+        <property name="position">3</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkBox" id="box4">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <child>
+          <object class="GtkImage" id="image2">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="xpad">3</property>
+            <property name="stock">gtk-dialog-info</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="label6">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="label" translatable="yes">Tip: Hold the pointer over a line for a full 
+explanation of permissions.</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+        <property name="position">4</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkHBox" id="hbox6">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <child>
+          <object class="GtkLabel" id="label8">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="label" translatable="yes">For more information on AFS ACLs, please</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLinkButton" id="helpBtn">
+            <property name="label" translatable="yes">click here</property>
+            <property name="use_action_appearance">False</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="has_tooltip">True</property>
+            <property name="use_action_appearance">False</property>
+            <property name="relief">none</property>
+            <property name="focus_on_click">False</property>
+            <property name="xalign">0</property>
+            <property name="uri">http://kb.mit.edu/confluence/x/up07</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+        <property name="position">5</property>
+      </packing>
+    </child>
+  </object>
+  <object class="GtkVBox" id="vboxNoPerms">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <child>
+      <object class="GtkLabel" id="label9">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="label" translatable="yes">You do not have sufficient permissions 
+to view the ACL for this directory.</property>
+        <property name="justify">center</property>
+      </object>
+      <packing>
+        <property name="expand">True</property>
+        <property name="fill">True</property>
+        <property name="position">0</property>
+      </packing>
+    </child>
+  </object>
+</interface>

Modified: trunk/debathena/debathena/nautilus-afs/debian/changelog
===================================================================
--- trunk/debathena/debathena/nautilus-afs/debian/changelog	2012-09-12 17:57:33 UTC (rev 25762)
+++ trunk/debathena/debathena/nautilus-afs/debian/changelog	2012-09-13 18:37:00 UTC (rev 25763)
@@ -1,3 +1,10 @@
+debathena-nautilus-afs (2.0) unstable; urgency=low
+
+  * Complete rewrite for Nautilus 3 and Gtk3
+  * Does not depend on pyafs
+
+ -- Jonathan Reed <jdreed@mit.edu>  Thu, 13 Sep 2012 14:29:35 -0400
+
 debathena-nautilus-afs (1.0) unstable; urgency=low
 
   * Initial release

Modified: trunk/debathena/debathena/nautilus-afs/debian/control
===================================================================
--- trunk/debathena/debathena/nautilus-afs/debian/control	2012-09-12 17:57:33 UTC (rev 25762)
+++ trunk/debathena/debathena/nautilus-afs/debian/control	2012-09-13 18:37:00 UTC (rev 25763)
@@ -3,11 +3,12 @@
 Priority: extra
 Build-Depends: cdbs, debhelper
 Maintainer: Debathena Project <debathena@mit.edu>
-Standards-Version: 3.8.4 
+Standards-Version: 3.9.1
 
 Package: debathena-nautilus-afs
+X-Debathena-Build-For: precise quantal
 Architecture: all
-Depends: ${misc:Depends}, python-nautilus (>= 0.5.0-0ubuntu3), python-gtk2, python
+Depends: ${misc:Depends}, python-nautilus (>= 1.1-1), python, gir1.2-glib-2.0, gir1.2-gtk-3.0, openafs-client
 Description: Provide a Nautilus GUI for AFS ACLs
  This package provides a Nautilus GUI for viewing and modifying AFS
  ACLs.

Modified: trunk/debathena/debathena/nautilus-afs/debian/debathena-nautilus-afs.install
===================================================================
--- trunk/debathena/debathena/nautilus-afs/debian/debathena-nautilus-afs.install	2012-09-12 17:57:33 UTC (rev 25762)
+++ trunk/debathena/debathena/nautilus-afs/debian/debathena-nautilus-afs.install	2012-09-13 18:37:00 UTC (rev 25763)
@@ -1,2 +1,2 @@
-afs-property-page.glade usr/share/debathena-nautilus-afs-integration
-afs-property-page.py usr/lib/nautilus/extensions-2.0/python
+afs-property-page.ui usr/share/debathena-nautilus-afs
+afs-property-page.py usr/share/nautilus-python/extensions


home help back first fref pref prev next nref lref last post