[Rt-commit] rt branch, 4.0/rights-editor-tab-state, created. rt-4.0.8-213-gf69b46c

Thomas Sibley trs at bestpractical.com
Tue Dec 4 01:23:52 EST 2012


The branch, 4.0/rights-editor-tab-state has been created
        at  f69b46cda25bef472237d08332c289b1792ee7f9 (commit)

- Log -----------------------------------------------------------------
commit 869de6884d7a2e3ab9ea9dda1b810a6ad810ed07
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Mon Dec 3 17:16:37 2012 -0800

    Default to the previously selected user/group in the rights editor
    
    Prevents confusion across form submission by keeping the rights editor
    looking at the same principal panel.  Resolves [rt3 #19205].
    
    This works even for newly added principals, which need translation from
    a submitted Name to a principal ID.  Part of this translation/lookup
    logic is refactored out of ProcessACLs() so that both the backend and UI
    piece can use the same code (_ParseACLNewPrincipal).
    
    Anchor generation is changed to omit the grouping name to cleanly
    separate this code from the principal map generated by
    GetPrincipalsMap(), rather than adding assumptions.
    
    Based on an initial implementation by sunnavy.

diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index 3385c49..514a105 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -2206,19 +2206,8 @@ sub ProcessACLs {
 
     # Check if we want to grant rights to a previously rights-less user
     for my $type (qw(user group)) {
-        my $key = "AddPrincipalForRights-$type";
-
-        next unless $ARGSref->{$key};
-
-        my $principal;
-        if ( $type eq 'user' ) {
-            $principal = RT::User->new( $session{'CurrentUser'} );
-            $principal->LoadByCol( Name => $ARGSref->{$key} );
-        }
-        else {
-            $principal = RT::Group->new( $session{'CurrentUser'} );
-            $principal->LoadUserDefinedGroup( $ARGSref->{$key} );
-        }
+        my $principal = _ParseACLNewPrincipal($ARGSref, $type)
+            or next;
 
         unless ($principal->PrincipalId) {
             push @results, loc("Couldn't load the specified principal");
@@ -2318,7 +2307,34 @@ sub ProcessACLs {
     return (@results);
 }
 
+=head2 _ParseACLNewPrincipal
+
+Takes a hashref of C<%ARGS> and a principal type (C<user> or C<group>).  Looks
+for the presence of rights being added on a principal of the specified type,
+and returns undef if no new principal is being granted rights.  Otherwise loads
+up an L<RT::User> or L<RT::Group> object and returns it.  Note that the object
+may not be successfully loaded, and you should check C<->id> yourself.
+
+=cut
+
+sub _ParseACLNewPrincipal {
+    my $ARGSref = shift;
+    my $type    = lc shift;
+    my $key     = "AddPrincipalForRights-$type";
+
+    return unless $ARGSref->{$key};
 
+    my $principal;
+    if ( $type eq 'user' ) {
+        $principal = RT::User->new( $session{'CurrentUser'} );
+        $principal->LoadByCol( Name => $ARGSref->{$key} );
+    }
+    elsif ( $type eq 'group' ) {
+        $principal = RT::Group->new( $session{'CurrentUser'} );
+        $principal->LoadUserDefinedGroup( $ARGSref->{$key} );
+    }
+    return $principal;
+}
 
 
 =head2 UpdateRecordObj ( ARGSRef => \%ARGS, Object => RT::Record, AttributesRef => \@attribs)
diff --git a/share/html/Admin/Elements/EditRights b/share/html/Admin/Elements/EditRights
index e673593..db653c5 100644
--- a/share/html/Admin/Elements/EditRights
+++ b/share/html/Admin/Elements/EditRights
@@ -66,6 +66,18 @@ unless ( $AddPrincipal ) {
         $AddPrincipal = 'user'; # loc
     }
 }
+
+my $anchor = $DECODED_ARGS->{Anchor} || '';
+if ($anchor =~ /AddPrincipal/) {
+    for my $type ("group", "user") {
+        my $record = _ParseACLNewPrincipal($DECODED_ARGS, $type)
+            or next;
+        if ($record->PrincipalId) {
+            $anchor = "#acl-" . $record->PrincipalId;
+            last;
+        }
+    }
+}
 </%init>
 %# Principals is an array of arrays, where the inner arrays are like:
 %#      [ 'Category name' => $CollectionObj => 'DisplayColumn' => 1 ]
@@ -74,16 +86,26 @@ unless ( $AddPrincipal ) {
 
 <script type="text/javascript">
   jQuery(function() {
+      function sync_anchor(hash) {
+          if (!hash.length) return;
+          window.location.hash = hash;
+          jQuery(".rights-editor input[name=Anchor]").val(hash);
+      }
+      sync_anchor(<% $anchor |n,j %>);
+
       jQuery(".rights-editor").tabs({
           select: function(ev, ui) {
-              window.location.hash = ui.tab.hash;
+              sync_anchor(ui.tab.hash);
           }
       });
+
       jQuery(".rights-editor .category-tabs").tabs();
   });
 </script>
 
 <div class="rights-editor clearfix">
+  <input type="hidden" value="" name="Anchor" />
+
   <ul>
 <%perl>
 for my $category (@$Principals) {
@@ -93,8 +115,7 @@ for my $category (@$Principals) {
 <%perl>
     while ( my $obj = $collection->Next ) {
         my $display = ref $col eq 'CODE' ? $col->($obj) : $obj->$col;
-        my $id = "acl-$name-" . $obj->PrincipalId;
-        $id =~ s/[^a-zA-Z0-9\-]/_/g;
+        my $id = "acl-" . $obj->PrincipalId;
 </%perl>
 <li><a href="#<% $id %>"><% $loc ? loc($display) : $display %></a></li>
 <%perl>
@@ -136,8 +157,7 @@ for my $category (@$Principals) {
     my ($name, $collection, $col, $loc) = @$category;
     while ( my $obj = $collection->Next ) {
         my $display = ref $col eq 'CODE' ? $col->($obj) : $obj->$col;
-        my $id = "acl-$name-" . $obj->PrincipalId;
-        $id =~ s/[^a-zA-Z0-9\-]/_/g;
+        my $id = "acl-" . $obj->PrincipalId;
 </%perl>
 
   <div id="<% $id %>">

commit 4cd6bb6727d0902a583067d51004cb03543e1878
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Mon Dec 3 21:42:25 2012 -0800

    Stay on the same rights subtab when switching between principals
    
    By hooking the "show" event instead of the "select" event we avoid an
    infinite loop when we then select the new tab in each tabset: by the
    time "show" fires, the selected tab is already correct.
    
    Based on an initial implementation by sunnavy, which differed primarily
    in hooking the "select" event and unregistering/re-registering to work
    around the loop.
    
    Resolves [rt3 #19769].

diff --git a/share/html/Admin/Elements/EditRights b/share/html/Admin/Elements/EditRights
index db653c5..c4a17fc 100644
--- a/share/html/Admin/Elements/EditRights
+++ b/share/html/Admin/Elements/EditRights
@@ -99,7 +99,16 @@ if ($anchor =~ /AddPrincipal/) {
           }
       });
 
-      jQuery(".rights-editor .category-tabs").tabs();
+      jQuery(".rights-editor .category-tabs").tabs({
+          show: function(ev, ui) {
+              jQuery(".rights-editor .category-tabs").not(this).each(function() {
+                  var item     = jQuery(this);
+                  var selected = item.tabs("option", "selected") || 0;
+                  if (selected != ui.index)
+                      item.tabs("select", ui.index);
+              });
+          }
+      });
   });
 </script>
 

commit f69b46cda25bef472237d08332c289b1792ee7f9
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Mon Dec 3 22:09:56 2012 -0800

    Preserve the selected rights category tab across page reloads
    
    For example: granting a right to "Privileged" from the "Rights for
    Administrators" tab will return you to both tabs after submission.
    
    We still need the anchor framework for the principal tabs in order to
    translate newly added principals to their correct tab server-side.  The
    non-newly-added-principal side could be replaced with similar cookie
    usage, but it seems not as worthwhile since we'll still be stuck
    wrangling anchors.  One slightly verbose method is better than two
    competing methods.

diff --git a/etc/RT_Config.pm.in b/etc/RT_Config.pm.in
index 0df3e2b..dcc68cc 100755
--- a/etc/RT_Config.pm.in
+++ b/etc/RT_Config.pm.in
@@ -873,6 +873,7 @@ Set(@JSFiles, qw/
     jquery-ui-1.8.4.custom.min.js
     jquery-ui-timepicker-addon.js
     jquery-ui-patch-datepicker.js
+    jquery.cookie.js
     titlebox-state.js
     util.js
     userautocomplete.js
diff --git a/share/html/Admin/Elements/EditRights b/share/html/Admin/Elements/EditRights
index c4a17fc..895e015 100644
--- a/share/html/Admin/Elements/EditRights
+++ b/share/html/Admin/Elements/EditRights
@@ -100,6 +100,7 @@ if ($anchor =~ /AddPrincipal/) {
       });
 
       jQuery(".rights-editor .category-tabs").tabs({
+          cookie: { name: "rights-category-tab" /* saves current tab in cookie */ },
           show: function(ev, ui) {
               jQuery(".rights-editor .category-tabs").not(this).each(function() {
                   var item     = jQuery(this);
diff --git a/share/html/NoAuth/js/jquery.cookie.js b/share/html/NoAuth/js/jquery.cookie.js
new file mode 100644
index 0000000..7b3e701
--- /dev/null
+++ b/share/html/NoAuth/js/jquery.cookie.js
@@ -0,0 +1,89 @@
+/*jslint browser: true */ /*global jQuery: true */
+
+/**
+ * jQuery Cookie plugin
+ *
+ * Copyright (c) 2010 Klaus Hartl (stilbuero.de)
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ */
+
+// TODO JsDoc
+
+/**
+ * Create a cookie with the given key and value and other optional parameters.
+ *
+ * @example $.cookie('the_cookie', 'the_value');
+ * @desc Set the value of a cookie.
+ * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true });
+ * @desc Create a cookie with all available options.
+ * @example $.cookie('the_cookie', 'the_value');
+ * @desc Create a session cookie.
+ * @example $.cookie('the_cookie', null);
+ * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain
+ *       used when the cookie was set.
+ *
+ * @param String key The key of the cookie.
+ * @param String value The value of the cookie.
+ * @param Object options An object literal containing key/value pairs to provide optional cookie attributes.
+ * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object.
+ *                             If a negative value is specified (e.g. a date in the past), the cookie will be deleted.
+ *                             If set to null or omitted, the cookie will be a session cookie and will not be retained
+ *                             when the the browser exits.
+ * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie).
+ * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie).
+ * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will
+ *                        require a secure protocol (like HTTPS).
+ * @type undefined
+ *
+ * @name $.cookie
+ * @cat Plugins/Cookie
+ * @author Klaus Hartl/klaus.hartl at stilbuero.de
+ */
+
+/**
+ * Get the value of a cookie with the given key.
+ *
+ * @example $.cookie('the_cookie');
+ * @desc Get the value of a cookie.
+ *
+ * @param String key The key of the cookie.
+ * @return The value of the cookie.
+ * @type String
+ *
+ * @name $.cookie
+ * @cat Plugins/Cookie
+ * @author Klaus Hartl/klaus.hartl at stilbuero.de
+ */
+jQuery.cookie = function (key, value, options) {
+
+    // key and value given, set cookie...
+    if (arguments.length > 1 && (value === null || typeof value !== "object")) {
+        options = jQuery.extend({}, options);
+
+        if (value === null) {
+            options.expires = -1;
+        }
+
+        if (typeof options.expires === 'number') {
+            var days = options.expires, t = options.expires = new Date();
+            t.setDate(t.getDate() + days);
+        }
+
+        return (document.cookie = [
+            encodeURIComponent(key), '=',
+            options.raw ? String(value) : encodeURIComponent(String(value)),
+            options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
+            options.path ? '; path=' + options.path : '',
+            options.domain ? '; domain=' + options.domain : '',
+            options.secure ? '; secure' : ''
+        ].join(''));
+    }
+
+    // key and possibly options given, get cookie...
+    options = value || {};
+    var result, decode = options.raw ? function (s) { return s; } : decodeURIComponent;
+    return (result = new RegExp('(?:^|; )' + encodeURIComponent(key) + '=([^;]*)').exec(document.cookie)) ? decode(result[1]) : null;
+};

-----------------------------------------------------------------------


More information about the Rt-commit mailing list