[Freeipa-devel] [PATCH] extract shared dyamic edit code

Kevin McCarthy kmccarth at redhat.com
Fri Sep 28 18:58:59 UTC 2007


No new code here, just extracting shared code so I can use it on the
useredit page too.

Depends on:
freeipa-221-groupedit_use_dns.patch
freeipa-222-nestedgroups.patch
freeipa-225-editgroupui.patch
freeipa-226-groupmember_sorting.patch

-Kevin

-------------- next part --------------
# HG changeset patch
# User Kevin McCarthy <kmccarth at redhat.com>
# Date 1191005756 25200
# Node ID 27fb698fd454fe04913c9074c790c3163ca83d47
# Parent  ea4707434a517839c30c6f88f6ad302e91189e48
Extract the shared dynamic edit code.
Rename groupeditsearch.kid to dynamiceditsearch.kid

diff -r ea4707434a51 -r 27fb698fd454 ipa-server/ipa-gui/ipagui/controllers.py
--- a/ipa-server/ipa-gui/ipagui/controllers.py	Fri Sep 28 09:59:04 2007 -0700
+++ b/ipa-server/ipa-gui/ipagui/controllers.py	Fri Sep 28 11:55:56 2007 -0700
@@ -444,13 +444,11 @@ class Root(controllers.RootController):
             turbogears.flash("Group add failed: " + str(e) + "<br/>" + str(e.detail))
             return dict(form=group_new_form, tg_template='ipagui.templates.groupnew')
 
-    @expose("ipagui.templates.groupeditsearch")
+    @expose("ipagui.templates.dynamiceditsearch")
     @identity.require(identity.not_anonymous())
     def groupedit_search(self, **kw):
         """Searches for users+groups and displays list of results in a table.
-           This method is used for the ajax search on the group edit page.
-           It's not re-usable because the ajax/dom manipulation is tightly
-           bound to the groupedit page"""
+           This method is used for the ajax search on the group edit page."""
         client.set_krbccache(os.environ["KRB5CCNAME"])
         users = []
         groups = []
diff -r ea4707434a51 -r 27fb698fd454 ipa-server/ipa-gui/ipagui/static/javascript/dynamicedit.js
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ipa-server/ipa-gui/ipagui/static/javascript/dynamicedit.js	Fri Sep 28 11:55:56 2007 -0700
@@ -0,0 +1,171 @@
+/**
+ * dynamicedit.js
+ *
+ * Shared code, data, and functions for the dynamic add/remove lists on the
+ * edit group/user pages.
+ *
+ * These functions have specific expectations of the page they are used on:
+ *
+ * - If you want to preserve the dn_to_info_hash on round trip:
+ *     - The form must have a 'form_dn_to_info_json' hidden field.
+ *     - The form must have onsubmit="preSubmit()" set in its tag.
+ *     - Restoring the contents of add/remove lists on round trip unfortunately
+ *       can't be shared because it is a mixture of python and javascript.  See
+ *       the bottom part editgroup.kid for example code on this.
+ *
+ * - The page must have a div: 'newmembers'
+ *   that new members are dynamically added to.
+ *
+ * - The page must have a div: 'delmembers'
+ *   that removed members are dynamically added to.
+ *
+ * - Hidden fields called 'dnadd' and 'dndel' will be dynamically created,
+ *   holding the values of the 'dn' passed to addmember() and removemember()
+ *
+ * Other Notes:
+ *
+ * - Many of the fields refer to 'dn'.  There is no intrinsic reason this has
+ *   to be a dn (it can hold any "unique id" for the objects to add/remove)
+ *
+ * - Similarly, the word 'member' is used because the code was originally
+ *   written for editgroup.  A 'member' is just a 'thing' to add/remove.
+ *   On the useredit pages, for example, a 'member' is actually a group.
+ */
+
+// Stored as the values in the dn_to_info_hash
+MemberDisplayInfo = Class.create();
+MemberDisplayInfo.prototype = {
+  initialize: function(name, descr, type) {
+    this.name = name;
+    this.descr = descr;
+    this.type = type;
+  },
+};
+
+
+// this is used for round-trip recontruction of the names.
+// the hidden fields only contain dns.
+var dn_to_info_hash = new Hash();
+
+// used to filter search results.
+// records dns already in the group
+var member_hash = new Hash();
+
+// used to prevent double adding
+// records dns to be added
+var added_hash = new Hash();
+
+// Tracks the div ids that each member belongs to.
+// Since dn's will contain illegal characters for div ids, this is used
+// to map them to the correct div
+var dn_to_member_div_id = new Hash();
+
+
+
+/*
+ * Renders the information about the member into the passed in
+ * element.  This is used by addmember and removemember to
+ * consistently create the dom for the member information
+ * (name, descr) and add icons/font changes correct for each type.
+ */
+function renderMemberInfo(newdiv, info) {
+  if (info.type == "user") {
+    newdiv.appendChild(document.createTextNode(
+      info.name.escapeHTML() + " " + info.descr.escapeHTML() + " "));
+  } else if (info.type == "group") {
+    ital = document.createElement('i');
+    ital.appendChild(document.createTextNode(
+      info.name.escapeHTML() + " " + 
+      info.descr.escapeHTML() + " "));
+    newdiv.appendChild(ital);
+  }
+}
+
+/*
+ * Callback used for afterFinish in scriptaculous effect
+ */
+function removeElement(effect) {
+  Element.remove(effect.element);
+}
+
+function addmember(dn, info) {
+  dn_to_info_hash[dn] = info;
+
+  if ((added_hash[dn] == 1) || (member_hash[dn] == 1)) {
+    return null;
+  }
+  added_hash[dn] = 1;
+
+  var newdiv = document.createElement('div');
+  renderMemberInfo(newdiv, info);
+
+  var undolink = document.createElement('a');
+  undolink.setAttribute('href', '');
+  undolink.setAttribute('onclick',
+    'new Effect.Fade(Element.up(this), {afterFinish: removeElement});' +
+    'added_hash.remove("' + jsStringEscape(dn) + '");' +
+    'return false;');
+  undolink.appendChild(document.createTextNode("undo"));
+  newdiv.appendChild(undolink);
+
+  var dnInfo = document.createElement('input');
+  dnInfo.setAttribute('type', 'hidden');
+  dnInfo.setAttribute('name', 'dnadd');
+  dnInfo.setAttribute('value', dn);
+  newdiv.appendChild(dnInfo);
+
+  newdiv.style.display = 'none';
+  $('newmembers').appendChild(newdiv);
+
+  return newdiv
+}
+
+function addmemberHandler(element, dn, info) {
+  var newdiv = addmember(dn, info)
+  if (newdiv != null) {
+    new Effect.Fade(Element.up(element));
+    new Effect.Appear(newdiv);
+    /* Element.up(element).remove(); */
+  }
+}
+
+function removemember(dn, info) {
+  dn_to_info_hash[dn] = info;
+
+  var newdiv = document.createElement('div');
+  renderMemberInfo(newdiv, info);
+
+  orig_div_id = dn_to_member_div_id[dn];
+  var undolink = document.createElement('a');
+  undolink.setAttribute('href', '');
+  undolink.setAttribute('onclick',
+    'new Effect.Fade(Element.up(this), {afterFinish: removeElement});' +
+    "new Effect.Appear($('" + orig_div_id + "'));" +
+    'return false;');
+  undolink.appendChild(document.createTextNode("undo"));
+  newdiv.appendChild(undolink);
+
+  var dnInfo = document.createElement('input');
+  dnInfo.setAttribute('type', 'hidden');
+  dnInfo.setAttribute('name', 'dndel');
+  dnInfo.setAttribute('value', dn);
+  newdiv.appendChild(dnInfo);
+
+  newdiv.style.display = 'none';
+  $('delmembers').appendChild(newdiv);
+
+  return newdiv
+}
+
+function removememberHandler(element, dn, info) {
+  var newdiv = removemember(dn, info);
+  new Effect.Fade(Element.up(element));
+  new Effect.Appear(newdiv);
+  /* Element.up(element).remove(); */
+}
+
+function preSubmit() {
+  var json = dn_to_info_hash.toJSON();
+  $('form_dn_to_info_json').value = json;
+  return true;
+}
diff -r ea4707434a51 -r 27fb698fd454 ipa-server/ipa-gui/ipagui/templates/dynamiceditsearch.kid
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ipa-server/ipa-gui/ipagui/templates/dynamiceditsearch.kid	Fri Sep 28 11:55:56 2007 -0700
@@ -0,0 +1,79 @@
+<div xmlns:py="http://purl.org/kid/ns#">
+
+<?python
+from ipagui.helpers import ipahelper
+#
+# This file is used to render the results from an AJAX search onto a page.
+# It has many expectations of the page being rendered into:
+#   - Source page must have included dynamicedit.js and followed all of its
+#     requirements
+#
+?>
+    <div id="search-results-count">
+    </div>
+    <?python
+    criteria_esc = ipahelper.javascript_string_escape(criteria)
+    ?>
+    <script type="text/javascript">
+      search_string = "${criteria_esc}";
+      results_counter = 0;
+    </script>
+    <?python search_div_counter = 1 ?>
+    <div py:for="entities in (users, groups)">
+      <div py:if='(entities != None) and (len(entities) > 0)'>
+          <div py:for="entity in entities" id="search-${search_div_counter}">
+            <?python
+            ent_dn_esc = ipahelper.javascript_string_escape(entity.dn)
+            ent_uid = entity.uid
+            if ent_uid:
+                ent_name = "%s %s" % (entity.givenName, entity.sn)
+                ent_descr = "(%s)" % entity.uid
+                ent_type = "user"
+            else:
+                ent_name = entity.cn
+                ent_descr = "[group]"
+                ent_type = "group"
+            ent_name_esc = ipahelper.javascript_string_escape(ent_name)
+            ent_descr_esc = ipahelper.javascript_string_escape(ent_descr)
+            ent_type_esc = ipahelper.javascript_string_escape(ent_type)
+            ?>
+            <span id="search-info-${search_div_counter}"></span>
+            <script type="text/javascript">
+              if ((added_hash["${ent_dn_esc}"] == 1) ||
+                  (member_hash["${ent_dn_esc}"] == 1)) {
+                $("search-${search_div_counter}").style.display = 'none';
+              } else {
+                results_counter = results_counter + 1;
+              }
+
+              renderMemberInfo($('search-info-${search_div_counter}'),
+                           new MemberDisplayInfo('${ent_name_esc}',
+                                                 '${ent_descr_esc}',
+                                                 '${ent_type_esc}'));
+            </script>
+            <a href=""
+              onclick="addmemberHandler(this, '${ent_dn_esc}',
+                           new MemberDisplayInfo('${ent_name_esc}',
+                                                 '${ent_descr_esc}',
+                                                 '${ent_type_esc}'));
+                      return false;"
+            >add</a>
+            <?python
+            search_div_counter = search_div_counter + 1
+            ?>
+          </div>
+      </div>
+    </div>
+    <script type="text/javascript">
+      if (results_counter == 0) {
+        var message = "No results found for " + search_string;
+      } else {
+        var message =  results_counter + " results found:";
+      }
+      $('search-results-count').appendChild(document.createTextNode(message));
+    </script>
+    <script py:if="counter < 0">
+      $('search-results-count').appendChild(document.createTextNode(
+        " (truncated)"));
+    </script>
+</div>
diff -r ea4707434a51 -r 27fb698fd454 ipa-server/ipa-gui/ipagui/templates/groupeditform.kid
--- a/ipa-server/ipa-gui/ipagui/templates/groupeditform.kid	Fri Sep 28 09:59:04 2007 -0700
+++ b/ipa-server/ipa-gui/ipagui/templates/groupeditform.kid	Fri Sep 28 11:55:56 2007 -0700
@@ -7,40 +7,12 @@ from ipagui.helpers import ipahelper
 from ipagui.helpers import ipahelper
 ?>
 
+  <script type="text/javascript" charset="utf-8"
+    src="${tg.url('/static/javascript/dynamicedit.js')}"></script>
 
   <?python searchurl = tg.url('/groupedit_search') ?>
 
   <script type="text/javascript">
-
-    // Stored as the values in the dn_to_info_hash
-    MemberDisplayInfo = Class.create();
-    MemberDisplayInfo.prototype = {
-      initialize: function(name, descr, type) {
-        this.name = name;
-        this.descr = descr;
-        this.type = type;
-      },
-    };
-
-
-    // this is used for round-trip recontruction of the names.
-    // the hidden fields only contain dns.
-    var dn_to_info_hash = new Hash();
-
-    // used to filter search results.
-    // records dns already in the group
-    var member_hash = new Hash();
-
-    // used to prevent double adding
-    // records dns to be added
-    var added_hash = new Hash();
-
-    // Tracks the div ids that each member belongs to.
-    // Since dn's will contain illegal characters for div ids, this is used
-    // to map them to the correct div
-    var dn_to_member_div_id = new Hash();
-
-
     function toggleProtectedFields(checkbox) {
       var gidnumberField = $('form_gidnumber');
       if (checkbox.checked) {
@@ -50,108 +22,6 @@ from ipagui.helpers import ipahelper
         gidnumberField.disabled = true;
         $('form_editprotected').value = '';
       }
-    }
-
-    /*
-     * Renders the information about the member into the passed in
-     * element.  This is used by addmember and removemember to
-     * consistently create the dom for the member information
-     * (name, descr) and add icons/font changes correct for each type.
-     */
-    function renderMemberInfo(newdiv, info) {
-      if (info.type == "user") {
-        newdiv.appendChild(document.createTextNode(
-          info.name.escapeHTML() + " " + info.descr.escapeHTML() + " "));
-      } else if (info.type == "group") {
-        ital = document.createElement('i');
-        ital.appendChild(document.createTextNode(
-          info.name.escapeHTML() + " " + 
-          info.descr.escapeHTML() + " "));
-        newdiv.appendChild(ital);
-      }
-    }
-
-    /*
-     * Callback used for afterFinish in scriptaculous effect
-     */
-    function removeElement(effect) {
-      Element.remove(effect.element);
-    }
-
-    function addmember(dn, info) {
-      dn_to_info_hash[dn] = info;
-
-      if ((added_hash[dn] == 1) || (member_hash[dn] == 1)) {
-        return null;
-      }
-      added_hash[dn] = 1;
-
-      var newdiv = document.createElement('div');
-      renderMemberInfo(newdiv, info);
-
-      var undolink = document.createElement('a');
-      undolink.setAttribute('href', '');
-      undolink.setAttribute('onclick',
-        'new Effect.Fade(Element.up(this), {afterFinish: removeElement});' +
-        'added_hash.remove("' + jsStringEscape(dn) + '");' +
-        'return false;');
-      undolink.appendChild(document.createTextNode("undo"));
-      newdiv.appendChild(undolink);
-
-      var dnInfo = document.createElement('input');
-      dnInfo.setAttribute('type', 'hidden');
-      dnInfo.setAttribute('name', 'dnadd');
-      dnInfo.setAttribute('value', dn);
-      newdiv.appendChild(dnInfo);
-
-      newdiv.style.display = 'none';
-      $('newmembers').appendChild(newdiv);
-
-      return newdiv
-    }
-
-    function addmemberHandler(element, dn, info) {
-      var newdiv = addmember(dn, info)
-      if (newdiv != null) {
-        new Effect.Fade(Element.up(element));
-        new Effect.Appear(newdiv);
-        /* Element.up(element).remove(); */
-      }
-    }
-
-    function removemember(dn, info) {
-      dn_to_info_hash[dn] = info;
-
-      var newdiv = document.createElement('div');
-      renderMemberInfo(newdiv, info);
-
-      orig_div_id = dn_to_member_div_id[dn];
-      var undolink = document.createElement('a');
-      undolink.setAttribute('href', '');
-      undolink.setAttribute('onclick',
-        'new Effect.Fade(Element.up(this), {afterFinish: removeElement});' +
-        "new Effect.Appear($('" + orig_div_id + "'));" +
-        'return false;');
-      undolink.appendChild(document.createTextNode("undo"));
-      newdiv.appendChild(undolink);
-
-      var dnInfo = document.createElement('input');
-      dnInfo.setAttribute('type', 'hidden');
-      dnInfo.setAttribute('name', 'dndel');
-      dnInfo.setAttribute('value', dn);
-      newdiv.appendChild(dnInfo);
-
-      newdiv.style.display = 'none';
-      $('delmembers').appendChild(newdiv);
-
-      return newdiv
-    }
-
-    function removememberHandler(element, dn, info) {
-      var newdiv = removemember(dn, info);
-      new Effect.Fade(Element.up(element));
-      new Effect.Appear(newdiv);
-      /* Element.up(element).remove(); */
     }
 
     function enterDoSearch(e) {
@@ -177,12 +47,6 @@ from ipagui.helpers import ipahelper
              parameters: { criteria: $('criteria').value },
              evalScripts: true });
       return false;
-    }
-
-    function preSubmit() {
-      var json = dn_to_info_hash.toJSON();
-      $('form_dn_to_info_json').value = json;
-      return true;
     }
   </script>
 
diff -r ea4707434a51 -r 27fb698fd454 ipa-server/ipa-gui/ipagui/templates/groupeditsearch.kid
--- a/ipa-server/ipa-gui/ipagui/templates/groupeditsearch.kid	Fri Sep 28 09:59:04 2007 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-<div xmlns:py="http://purl.org/kid/ns#">
-
-<?python
-from ipagui.helpers import ipahelper
-?>
-    <div id="search-results-count">
-    </div>
-    <?python
-    criteria_esc = ipahelper.javascript_string_escape(criteria)
-    ?>
-    <script type="text/javascript">
-      search_string = "${criteria_esc}";
-      results_counter = 0;
-    </script>
-    <?python search_div_counter = 1 ?>
-    <div py:for="entities in (users, groups)">
-      <div py:if='(entities != None) and (len(entities) > 0)'>
-          <div py:for="entity in entities" id="search-${search_div_counter}">
-            <?python
-            ent_dn_esc = ipahelper.javascript_string_escape(entity.dn)
-            ent_uid = entity.uid
-            if ent_uid:
-                ent_name = "%s %s" % (entity.givenName, entity.sn)
-                ent_descr = "(%s)" % entity.uid
-                ent_type = "user"
-            else:
-                ent_name = entity.cn
-                ent_descr = "[group]"
-                ent_type = "group"
-            ent_name_esc = ipahelper.javascript_string_escape(ent_name)
-            ent_descr_esc = ipahelper.javascript_string_escape(ent_descr)
-            ent_type_esc = ipahelper.javascript_string_escape(ent_type)
-            ?>
-            <span id="search-info-${search_div_counter}"></span>
-            <script type="text/javascript">
-              if ((added_hash["${ent_dn_esc}"] == 1) ||
-                  (member_hash["${ent_dn_esc}"] == 1)) {
-                $("search-${search_div_counter}").style.display = 'none';
-              } else {
-                results_counter = results_counter + 1;
-              }
-
-              renderMemberInfo($('search-info-${search_div_counter}'),
-                           new MemberDisplayInfo('${ent_name_esc}',
-                                                 '${ent_descr_esc}',
-                                                 '${ent_type_esc}'));
-            </script>
-            <a href=""
-              onclick="addmemberHandler(this, '${ent_dn_esc}',
-                           new MemberDisplayInfo('${ent_name_esc}',
-                                                 '${ent_descr_esc}',
-                                                 '${ent_type_esc}'));
-                      return false;"
-            >add</a>
-            <?python
-            search_div_counter = search_div_counter + 1
-            ?>
-          </div>
-      </div>
-    </div>
-    <script type="text/javascript">
-      if (results_counter == 0) {
-        var message = "No results found for " + search_string;
-      } else {
-        var message =  results_counter + " results found:";
-      }
-      $('search-results-count').appendChild(document.createTextNode(message));
-    </script>
-    <script py:if="counter < 0">
-      $('search-results-count').appendChild(document.createTextNode(
-        " (truncated)"));
-    </script>
-</div>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/x-pkcs7-signature
Size: 4054 bytes
Desc: not available
URL: <http://listman.redhat.com/archives/freeipa-devel/attachments/20070928/89d6c7a5/attachment.bin>


More information about the Freeipa-devel mailing list