Browse Source

Useradmin actions are now in modal windows

Enrico Berti 13 years ago
parent
commit
84ba59dce9

+ 14 - 36
apps/useradmin/src/useradmin/templates/edit_group.mako

@@ -14,54 +14,32 @@
 ## See the License for the specific language governing permissions and
 ## limitations under the License.
 <%!
-from desktop.views import commonheader, commonfooter
+from desktop.views import commonheader_iframe, commonfooter_iframe
+import urllib
 %>
-<% import urllib %>
-
-<%namespace name="layout" file="layout.mako" />
-${layout.menubar(section='groups')}
-
-<div class="container-fluid">
-  % if name:
-	${commonheader('Edit Group: ' + name + ' -- Hue Groups', "useradmin", "100px")}
-	<h1>Edit Group: ${name} -- Hue Groups</h1>
-  % else:
-    ${commonheader('Create Group -- Hue Groups', "useradmin", "100px")}
-    <h1>Create Group -- Hue Groups</h1>
-  % endif
-	<form action="${urllib.quote(action)}" method="POST" class="jframe_padded">
+${commonheader_iframe()}
+	<form id="editForm" action="${urllib.quote(action)}" method="POST">
 		<fieldset>
-			<legend>
-			  % if name:
-		        Edit Group: ${name}
-		      % else:
-		        Create Group
-		      % endif
-			</legend>
         <%def name="render_field(field)">
 			<div class="clearfix">
 				${field.label_tag() | n}
 				<div class="input">
 					${unicode(field) | n}
+					% if len(field.errors):
+						${unicode(field.errors) | n}
+					% endif
 				</div>
-				% if len(field.errors):
-					${unicode(field.errors) | n}
-				% endif
 			</div>
 		</%def>
-
 		% for field in form:
 			${render_field(field)}
 		% endfor
         </fieldset>
-		<div class="actions">
-			<input type="submit" value="Save" class="btn primary"/>
-		</div>
 	</form>
-</div>
-<script type="text/javascript" charset="utf-8">
-	$(document).ready(function(){
-		$("#id_members").jHueSelector();
-	});
-</script>
-${commonfooter()}
+
+	<script type="text/javascript" charset="utf-8">
+		$(document).ready(function(){
+			$("#id_members").jHueSelector();
+		});
+	</script>
+${commonfooter_iframe()}

+ 27 - 0
apps/useradmin/src/useradmin/templates/edit_group_confirmation.mako

@@ -0,0 +1,27 @@
+## Licensed to Cloudera, Inc. under one
+## or more contributor license agreements.  See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership.  Cloudera, Inc. licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License.  You may obtain a copy of the License at
+##
+##     http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+<%!
+from desktop.views import commonheader_iframe, commonfooter_iframe
+import urllib
+%>
+${commonheader_iframe()}
+
+<script type="text/javascript" charset="utf-8">
+	$(document).ready(function(){
+		window.parent.location.href = "/useradmin/groups";
+	});
+</script>
+${commonfooter_iframe()}

+ 15 - 22
apps/useradmin/src/useradmin/templates/edit_permissions.mako

@@ -14,40 +14,33 @@
 ## See the License for the specific language governing permissions and
 ## limitations under the License.
 <%!
-from desktop.views import commonheader, commonfooter
-%>
-<% import urllib %>
-
-<%namespace name="layout" file="layout.mako" />
-${layout.menubar(section='permissions')}
-
-<div class="container-fluid">
-	${commonheader('Edit Permission -- ' + app + ' -- ' + priv, "useradmin", "100px")}
-	<h1>Edit Permission ${app}.${priv}</h1>
-	<form action="${urllib.quote(action)}" method="POST" class="jframe_padded">
+from desktop.views import commonheader_iframe, commonfooter_iframe
+import urllib %>
+${commonheader_iframe()}
+	<form id="editForm" action="${urllib.quote(action)}" method="POST">
 		<fieldset>
-			<legend>
-		        Edit Groups With This Permission
-			</legend>
-        <%def name="render_field(field)">
+	    <%def name="render_field(field)">
 			<div class="clearfix">
 				${field.label_tag() | n}
 				<div class="input">
 					${unicode(field) | n}
-				</div>
+
 				% if len(field.errors):
 					${unicode(field.errors) | n}
 				% endif
+				</div>
 			</div>
 		</%def>
 
 		% for field in form:
 			${render_field(field)}
 		% endfor
-        </fieldset>
-		<div class="actions">
-			<input type="submit" value="Save" class="btn primary"/>
-		</div>
+	    </fieldset>
 	</form>
-</div>
-${commonfooter()}
+
+	<script type="text/javascript" charset="utf-8">
+		$(document).ready(function(){
+			$("#id_groups").jHueSelector();
+		});
+	</script>
+${commonfooter_iframe()}

+ 27 - 0
apps/useradmin/src/useradmin/templates/edit_permissions_confirmation.mako

@@ -0,0 +1,27 @@
+## Licensed to Cloudera, Inc. under one
+## or more contributor license agreements.  See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership.  Cloudera, Inc. licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License.  You may obtain a copy of the License at
+##
+##     http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+<%!
+from desktop.views import commonheader_iframe, commonfooter_iframe
+import urllib
+%>
+${commonheader_iframe()}
+
+<script type="text/javascript" charset="utf-8">
+	$(document).ready(function(){
+		window.parent.location.href = "/useradmin/permissions";
+	});
+</script>
+${commonfooter_iframe()}

+ 17 - 40
apps/useradmin/src/useradmin/templates/edit_user.mako

@@ -14,49 +14,26 @@
 ## See the License for the specific language governing permissions and
 ## limitations under the License.
 <%!
-from desktop.views import commonheader, commonfooter
-%>
-<% import urllib %>
+from desktop.views import commonheader_iframe, commonfooter_iframe
+import urllib %>
 
-<%namespace name="layout" file="layout.mako" />
-${layout.menubar(section='users')}
-
-<div class="container-fluid">
-  % if username:
-	${commonheader('Edit User: ' + username + ' -- Hue Users', "useradmin", "100px")}
-	<h1>Edit User: ${username} -- Hue Users</h1>
-  % else:
-    ${commonheader('Create User -- Hue Users', "useradmin", "100px")}
-    <h1>Create User -- Hue Users</h1>
-  % endif
-	<form action="${urllib.quote(action)}" method="POST" class="jframe_padded">
-		<fieldset>
-			<legend> 
-			  % if username:
-		        Edit User: ${username}
-		      % else:
-		        Create User 
-		      % endif
-			</legend>
-        <%def name="render_field(field)">
-			<div class="clearfix">
-				${field.label_tag() | n}
-				<div class="input">
-					${unicode(field) | n}
-				</div>
+${commonheader_iframe()}
+<form id="editForm" action="${urllib.quote(action)}" method="POST">
+	<fieldset>
+    <%def name="render_field(field)">
+		<div class="clearfix">
+			${field.label_tag() | n}
+			<div class="input">
+				${unicode(field) | n}
 				% if len(field.errors):
 					${unicode(field.errors) | n}
 				% endif
 			</div>
-		</%def>
-          
-		% for field in form:
-			${render_field(field)}
-		% endfor
-        </fieldset>
-		<div class="actions">
-			<input type="submit" value="Save" class="btn primary"/>
 		</div>
-	</form>
-</div>
-${commonfooter()}
+	</%def>
+	% for field in form:
+		${render_field(field)}
+	% endfor
+    </fieldset>
+</form>
+${commonfooter_iframe()}

+ 27 - 0
apps/useradmin/src/useradmin/templates/edit_user_confirmation.mako

@@ -0,0 +1,27 @@
+## Licensed to Cloudera, Inc. under one
+## or more contributor license agreements.  See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership.  Cloudera, Inc. licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License.  You may obtain a copy of the License at
+##
+##     http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+<%!
+from desktop.views import commonheader_iframe, commonfooter_iframe
+import urllib
+%>
+${commonheader_iframe()}
+
+<script type="text/javascript" charset="utf-8">
+	$(document).ready(function(){
+		window.parent.location.href = "/useradmin/users";
+	});
+</script>
+${commonfooter_iframe()}

+ 62 - 9
apps/useradmin/src/useradmin/templates/list_groups.mako

@@ -30,7 +30,7 @@ ${layout.menubar(section='groups')}
 	<div class="well">
 			Filter by name: <input id="filterInput"/> <a href="#" id="clearFilterBtn" class="btn">Clear</a>
 			<p class="pull-right">
-				<a href="${ url('useradmin.views.edit_group') }" class="btn">Add group</a>
+				<a id="addGroupBtn" href="#" class="btn">Add group</a>
 			</p>
 	</div>
       <table class="datatables">
@@ -49,7 +49,7 @@ ${layout.menubar(section='groups')}
             <td>${', '.join([user.username for user in group.user_set.all()])}</td>
             <td>${', '.join([perm.app + "." + perm.action for perm in group_permissions(group)])}</td>
             <td>
-              <a title="Edit ${group.name}" class="btn small" href="${ url('useradmin.views.edit_group', name=urllib.quote(group.name)) }">Edit</a>
+              <a title="Edit ${group.name}" class="btn small editGroupBtn" data-url="${ url('useradmin.views.edit_group', name=urllib.quote(group.name)) }" data-name="${group.name}">Edit</a>
               <a title="Delete ${group.name}" class="btn small confirmationModal" alt="Are you sure you want to delete ${group.name}?" href="javascript:void(0)" data-confirmation-url="${ url('useradmin.views.delete_group', name=urllib.quote_plus(group.name)) }">Delete</a>
             </td>
           </tr>
@@ -59,7 +59,7 @@ ${layout.menubar(section='groups')}
 
 
 
-<div id="deleteGroup" class="modal hide fade">
+<div id="deleteGroup" class="modal hide fade groupModal">
 	<form id="deleteGroupForm" action="" method="POST">
 	<div class="modal-header">
 		<a href="#" class="close">&times;</a>
@@ -71,7 +71,34 @@ ${layout.menubar(section='groups')}
 	</div>
 	</form>
 </div>
-</div>   
+
+<div id="addGroup" class="modal hide fade groupModal">
+	<div class="modal-header">
+		<a href="#" class="close">&times;</a>
+		<h3>Add group</h3>
+	</div>
+	<div id="addGroupBody" class="modal-body">
+		<iframe id="addGroupFrame"></iframe>
+	</div>
+	<div class="modal-footer">
+		<button id="addGroupSaveBtn" class="btn primary">Save</button>
+	</div>
+</div>
+
+<div id="editGroup" class="modal hide fade groupModal">
+	<div class="modal-header">
+		<a href="#" class="close">&times;</a>
+		<h3>Edit group <span class="groupName"></span></h3>
+	</div>
+	<div id="editGroupBody" class="modal-body">
+		<iframe id="editGroupFrame"></iframe>
+	</div>
+	<div class="modal-footer">
+		<button id="editGroupSaveBtn" class="btn primary">Save</button>
+	</div>
+</div>
+
+</div>
 
 	<script type="text/javascript" charset="utf-8">
 		$(document).ready(function(){
@@ -79,15 +106,22 @@ ${layout.menubar(section='groups')}
 				"bPaginate": false,
 			    "bLengthChange": false,
 				"bInfo": false,
-				"bFilter": false
+				"bFilter": false,
+				"aoColumns": [
+					{ "sWidth": "20%" },
+					{ "sWidth": "20%" },
+					null,
+					{ "sWidth": "120px" },
+				 ]
 			});
 			$(".dataTables_wrapper").css("min-height","0");
 			$(".dataTables_filter").hide();
 
-			$("#deleteGroup").modal({
+			$(".groupModal").modal({
 				backdrop: "static",
 				keyboard: true
 			});
+
 			$(".confirmationModal").click(function(){
 				var _this = $(this);
 				$.getJSON(_this.attr("data-confirmation-url"), function(data){
@@ -99,11 +133,11 @@ ${layout.menubar(section='groups')}
 			$(".hideModal").click(function(){
 				$("#deleteGroup").modal("hide");
 			});
-			
+
 			$("#filterInput").keyup(function(){
 		        $.each($(".groupRow"), function(index, value) {
 
-		          if($(value).attr("data-search").toLowerCase().indexOf($("#filterInput").val().toLowerCase()) == -1 && $("#filterInput").val() != ""){
+		          if($(value).data("search").toLowerCase().indexOf($("#filterInput").val().toLowerCase()) == -1 && $("#filterInput").val() != ""){
 		            $(value).hide(250);
 		          }else{
 		            $(value).show(250);
@@ -118,7 +152,26 @@ ${layout.menubar(section='groups')}
 		            $(value).show(250);
 		        });
 		    });
-		   
+
+			$("#addGroupBtn").click(function(){
+				$("#addGroupFrame").css("height","400px").attr("src","${url('useradmin.views.edit_group')}");
+				$("#addGroup").modal("show");
+			});
+
+			$("#addGroupSaveBtn").click(function(){
+				$("#addGroupFrame").contents().find('form').submit();
+			});
+
+			$(".editGroupBtn").click(function(){
+				$("#editGroup").find(".groupName").text($(this).data("name"));
+				$("#editGroupFrame").css("height","400px").attr("src", $(this).data("url"));
+				$("#editGroup").modal("show");
+			});
+
+			$("#editGroupSaveBtn").click(function(){
+				$("#editGroupFrame").contents().find('form').submit();
+			});
+
 
 		});
 	</script>

+ 32 - 4
apps/useradmin/src/useradmin/templates/list_permissions.mako

@@ -47,7 +47,7 @@ ${layout.menubar(section='permissions')}
             <td>${perm.description}</td>
             <td>${', '.join([group.name for group in Group.objects.filter(grouppermission__hue_permission=perm).order_by('name')])}</td>
             <td>
-              <a title="Edit groups" class="btn small" href="${ url('useradmin.views.edit_permission', app=urllib.quote(perm.app), priv=urllib.quote(perm.action)) }">Edit</a>
+              <a title="Edit permission" class="btn small editPermissionBtn" data-url="${ url('useradmin.views.edit_permission', app=urllib.quote(perm.app), priv=urllib.quote(perm.action)) }" data-name="${perm.app}">Edit</a>
             </td>
           </tr>
         % endfor
@@ -56,6 +56,20 @@ ${layout.menubar(section='permissions')}
 
 </div>
 
+
+<div id="editPermission" class="modal hide fade">
+	<div class="modal-header">
+		<a href="#" class="close">&times;</a>
+		<h3>Edit permissions for <span class="applicationName"></span></h3>
+	</div>
+	<div id="editPermissionBody" class="modal-body">
+		<iframe id="editPermissionFrame"></iframe>
+	</div>
+	<div class="modal-footer">
+		<button id="editPermissionSaveBtn" class="btn primary">Save</button>
+	</div>
+</div>
+
 	<script type="text/javascript" charset="utf-8">
 		$(document).ready(function(){
 			$(".datatables").dataTable({
@@ -69,8 +83,7 @@ ${layout.menubar(section='permissions')}
 
 			$("#filterInput").keyup(function(){
 		        $.each($(".permissionRow"), function(index, value) {
-
-		          if($(value).attr("data-search").toLowerCase().indexOf($("#filterInput").val().toLowerCase()) == -1 && $("#filterInput").val() != ""){
+		          if($(value).data("search").toLowerCase().indexOf($("#filterInput").val().toLowerCase()) == -1 && $("#filterInput").val() != ""){
 		            $(value).hide(250);
 		          }else{
 		            $(value).show(250);
@@ -81,11 +94,26 @@ ${layout.menubar(section='permissions')}
 
 		    $("#clearFilterBtn").click(function(){
 		        $("#filterInput").val("");
-		        $.each($(".file-row"), function(index, value) {
+		        $.each($(".permissionRow"), function(index, value) {
 		            $(value).show(250);
 		        });
 		    });
 
+			$("#editPermission").modal({
+				backdrop: "static",
+				keyboard: true
+			});
+
+			$(".editPermissionBtn").click(function(){
+				$("#editPermission").find(".applicationName").text($(this).data("name"));
+				$("#editPermissionFrame").css("height","260px").attr("src", $(this).data("url"));
+				$("#editPermission").modal("show");
+			});
+
+			$("#editPermissionSaveBtn").click(function(){
+				$("#editPermissionFrame").contents().find('form').submit();
+			});
+
 
 		});
 	</script>

+ 65 - 20
apps/useradmin/src/useradmin/templates/list_users.mako

@@ -27,9 +27,9 @@ ${layout.menubar(section='users')}
 <div class="container-fluid">
 	<h1>Hue Users</h1>
 	<div class="well">
-			Filter: <input id="filterInput"/> <a href="#" id="clearFilterBtn" class="btn">Clear</a>
+			Filter by name: <input id="filterInput"/> <a href="#" id="clearFilterBtn" class="btn">Clear</a>
 			<p class="pull-right">
-				<a href="${ url('useradmin.views.edit_user') }" class="btn">Add user</a>
+				<a id="addUserBtn" href="#" class="btn">Add user</a>
 			</p>
 	</div>
       <table class="datatables">
@@ -42,7 +42,7 @@ ${layout.menubar(section='users')}
             <th>${_('Last Login')}</th>
 			<th>&nbsp;</th>
           </tr>
-        </thead>
+        </head>
         <tbody>
         % for user in users:
           <tr class="userRow" data-search="${user.username}${user.first_name}${user.last_name}${user.email}">
@@ -54,7 +54,7 @@ ${layout.menubar(section='users')}
               ${user.last_login.strftime('%c')}
             </td>
             <td>
-              <a title="Edit ${user.username}" class="btn small" href="${ url('useradmin.views.edit_user', username=urllib.quote(user.username)) }">Edit</a>
+              <a title="Edit ${user.username}" class="btn small editUserBtn" data-url="${ url('useradmin.views.edit_user', username=urllib.quote(user.username)) }" data-name="${user.username}">Edit</a>
               <a title="Delete ${user.username}" class="btn small confirmationModal" alt="Are you sure you want to delete ${user.username}?" href="javascript:void(0)" data-confirmation-url="${ url('useradmin.views.delete_user', username=urllib.quote_plus(user.username)) }">Delete</a>
             </td>
           </tr>
@@ -62,21 +62,47 @@ ${layout.menubar(section='users')}
         </tbody>
       </table>
 
+	<div id="addUser" class="modal hide fade userModal">
+		<div class="modal-header">
+			<a href="#" class="close">&times;</a>
+			<h3>Add user</h3>
+		</div>
+		<div id="addUserBody" class="modal-body">
+			<iframe id="addUserFrame" class="scroll"></iframe>
+		</div>
+		<div class="modal-footer">
+			<button id="addUserSaveBtn" class="btn primary">Save</button>
+		</div>
+	</div>
 
-
-<div id="deleteUser" class="modal hide fade">
-	<form id="deleteUserForm" action="" method="POST">
-	<div class="modal-header">
-		<a href="#" class="close">&times;</a>
-		<h3 id="deleteUserMessage">Confirm action</h3>
+	<div id="editUser" class="modal hide fade userModal">
+		<div class="modal-header">
+			<a href="#" class="close">&times;</a>
+			<h3>Edit user <span class="username"></span></h3>
+		</div>
+		<div id="editUserBody" class="modal-body">
+			<iframe id="editUserFrame" class="scroll"></iframe>
+		</div>
+		<div class="modal-footer">
+			<button id="editUserSaveBtn" class="btn primary">Save</button>
+		</div>
 	</div>
-	<div class="modal-footer">
-		<input type="submit" class="btn primary" value="Yes"/>
-		<a href="#" class="btn secondary hideModal">No</a>
+
+	<div id="deleteUser" class="modal hide fade userModal">
+		<form id="deleteUserForm" action="" method="POST">
+		<div class="modal-header">
+			<a href="#" class="close">&times;</a>
+			<h3 id="deleteUserMessage">Confirm action</h3>
+		</div>
+		<div class="modal-footer">
+			<input type="submit" class="btn primary" value="Yes"/>
+			<a href="#" class="btn secondary hideModal">No</a>
+		</div>
+		</form>
 	</div>
-	</form>
+
+
 </div>
-</div>   
 
 	<script type="text/javascript" charset="utf-8">
 		$(document).ready(function(){
@@ -89,10 +115,11 @@ ${layout.menubar(section='users')}
 			$(".dataTables_wrapper").css("min-height","0");
 			$(".dataTables_filter").hide();
 
-			$("#deleteUser").modal({
+			$(".userModal").modal({
 				backdrop: "static",
 				keyboard: true
 			});
+
 			$(".confirmationModal").click(function(){
 				var _this = $(this);
 				$.getJSON(_this.attr("data-confirmation-url"), function(data){
@@ -104,11 +131,10 @@ ${layout.menubar(section='users')}
 			$(".hideModal").click(function(){
 				$("#deleteUser").modal("hide");
 			});
-			
+
 			$("#filterInput").keyup(function(){
 		        $.each($(".userRow"), function(index, value) {
-
-		          if($(value).attr("data-search").toLowerCase().indexOf($("#filterInput").val().toLowerCase()) == -1 && $("#filterInput").val() != ""){
+		          if($(value).data("search").toLowerCase().indexOf($("#filterInput").val().toLowerCase()) == -1 && $("#filterInput").val() != ""){
 		            $(value).hide(250);
 		          }else{
 		            $(value).show(250);
@@ -123,7 +149,26 @@ ${layout.menubar(section='users')}
 		            $(value).show(250);
 		        });
 		    });
-		   
+
+			$("#addUserBtn").click(function(){
+				$("#addUserFrame").css("height","300px").attr("src","${ url('useradmin.views.edit_user') }");
+				$("#addUser").modal("show");
+			});
+
+			$("#addUserSaveBtn").click(function(){
+				$("#addUserFrame").contents().find('form').submit();
+			});
+
+			$(".editUserBtn").click(function(){
+				$("#editUser").find(".username").text($(this).data("name"));
+				$("#editUserFrame").css("height","300px").attr("src", $(this).data("url"));
+				$("#editUser").modal("show");
+			});
+
+			$("#editUserSaveBtn").click(function(){
+				$("#editUserFrame").contents().find('form').submit();
+			});
+
 
 		});
 	</script>

+ 11 - 40
apps/useradmin/src/useradmin/views.py

@@ -32,7 +32,7 @@ from desktop.lib.django_util import get_username_re_rule, get_groupname_re_rule,
 from django.core import urlresolvers
 
 from useradmin.models import GroupPermission, HuePermission, UserProfile, LdapGroup
-from useradmin.models import get_profile, get_default_user_group
+from useradmin.models import get_profile
 import ldap_access
 
 LOG = logging.getLogger(__name__)
@@ -75,16 +75,12 @@ def delete_user(request, username):
 def delete_group(request, name):
   if not request.user.is_superuser:
     raise PopupException("You must be a superuser to delete groups.")
-
   if request.method == 'POST':
     try:
       global groups_lock
       __groups_lock.acquire()
       try:
         group = Group.objects.get(name=name)
-        default_group = get_default_user_group()
-        if default_group is not None and default_group.name == name:
-          raise PopupException("The default user group may not be deleted.")
         group.delete()
       finally:
         __groups_lock.release()
@@ -113,7 +109,7 @@ class UserChangeForm(django.contrib.auth.forms.UserChangeForm):
   password2 = forms.CharField(label="Password confirmation", widget=forms.PasswordInput, required=False)
 
   class Meta(django.contrib.auth.forms.UserChangeForm.Meta):
-    fields = ["username", "first_name", "last_name", "email"]
+    fields = ["username", "first_name", "last_name", "email", "is_active", "is_superuser"]
 
   def clean_password2(self):
     password1 = self.cleaned_data.get("password1", "")
@@ -137,27 +133,8 @@ class UserChangeForm(django.contrib.auth.forms.UserChangeForm):
       user.set_password(self.cleaned_data["password1"])
     if commit:
       user.save()
-      # groups must be saved after the user
-      self.save_m2m()
     return user
 
-class SuperUserChangeForm(UserChangeForm):
-  class Meta(UserChangeForm.Meta):
-    fields = ["username", "is_active"] + UserChangeForm.Meta.fields + ["is_superuser", "groups"]
-
-  def __init__(self, *args, **kwargs):
-    super(SuperUserChangeForm, self).__init__(*args, **kwargs)
-    if self.instance.id:
-      # If the user exists already, we'll use its current group memberships
-      self.initial['groups'] = set(self.instance.groups.all())
-    else:
-      # If his is a new user, suggest the default group
-      default_group = get_default_user_group()
-      if default_group is not None:
-        self.initial['groups'] = set([default_group])
-      else:
-        self.initial['groups'] = []
-
 def edit_user(request, username=None):
   """
   edit_user(request, username = None) -> reply
@@ -174,13 +151,8 @@ def edit_user(request, username=None):
   else:
     instance = None
 
-  if request.user.is_superuser:
-    form_class = SuperUserChangeForm
-  else:
-    form_class = UserChangeForm
-
   if request.method == 'POST':
-    form = form_class(request.POST, instance=instance)
+    form = UserChangeForm(request.POST, instance=instance)
     if form.is_valid(): # All validation rules pass
       if instance is None:
         form.save()
@@ -212,10 +184,10 @@ def edit_user(request, username=None):
           __users_lock.release()
 
       request.path = urlresolvers.reverse(list_users)
-      return list_users(request)
+      return render('edit_user_confirmation.mako', request,
+	    dict(form=form, action=request.path, username=username))
   else:
-    form = form_class(instance=instance)
-
+    form = UserChangeForm(instance=instance)
   return render('edit_user.mako', request,
     dict(form=form, action=request.path, username=username))
 
@@ -244,7 +216,8 @@ def edit_group(request, name=None):
       form.save()
       request.flash.put('Group information updated')
       url = urlresolvers.reverse(list_groups)
-      return format_preserving_redirect(request, url)
+      return render('edit_group_confirmation.mako', request,
+	    dict(form=form, action=request.path, name=name))
 
   else:
     form = GroupEditForm(instance=instance)
@@ -275,7 +248,8 @@ def edit_permission(request, app=None, priv=None):
       form.save()
       request.flash.put('Permission information updated')
       url = urlresolvers.reverse(list_permissions)
-      return format_preserving_redirect(request, url)
+      return render('edit_permissions_confirmation.mako', request,
+	    dict(form=form, action=request.path, app=app, priv=priv))
 
   else:
     form = PermissionsEditForm(instance=instance)
@@ -419,9 +393,7 @@ def _import_ldap_group(groupname, import_members=False, import_by_dn=False):
       except User.DoesNotExist:
         continue
 
-    if user is None:
-      # There was a naming comflict, or for some other reason, we couldn't get
-      # at the user
+    if get_profile(user).creation_method == str(UserProfile.CreationMethod.HUE):
       continue
     LOG.debug("Adding user %s to group %s" % (member, group.name))
     group.user_set.add(user)
@@ -471,7 +443,6 @@ class GroupEditForm(forms.ModelForm):
     super(GroupEditForm, self).__init__(*args, **kwargs)
 
     if self.instance.id:
-      self.fields['name'].widget.attrs['readonly'] = True
       initial_members = User.objects.filter(groups=self.instance).order_by('username')
       initial_perms = HuePermission.objects.filter(grouppermission__group=self.instance).order_by('app','description')
     else:

+ 20 - 0
desktop/core/src/desktop/templates/common_footer.html

@@ -0,0 +1,20 @@
+{% comment %}
+Licensed to Cloudera, Inc. under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  Cloudera, Inc. licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+{% endcomment %}
+
+	</body>
+</html>

+ 20 - 0
desktop/core/src/desktop/templates/common_footer_iframe.html

@@ -0,0 +1,20 @@
+{% comment %}
+Licensed to Cloudera, Inc. under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  Cloudera, Inc. licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+{% endcomment %}
+
+	</body>
+</html>

+ 89 - 0
desktop/core/src/desktop/templates/common_header.html

@@ -0,0 +1,89 @@
+{% comment %}
+Licensed to Cloudera, Inc. under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  Cloudera, Inc. licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+{% endcomment %}
+{% load is_selected %}
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+	<meta name="viewport" content="width=device-width user-scalable=no initial-scale=1" />
+	<meta name="apple-mobile-web-app-capable" content="yes" />
+	<title>{{title}}</title>
+	<link rel="stylesheet" href="/static/ext/css/bootstrap.min.css" type="text/css" media="screen" title="no title" charset="utf-8" />
+	<link rel="stylesheet" href="/static/css/jhue.css" type="text/css" media="screen" title="no title" charset="utf-8" />
+	<link rel="stylesheet" href="/static/ext/css/fileuploader.css" type="text/css" media="screen" title="no title" charset="utf-8" />
+
+	<style type="text/css">
+      body {
+        padding-top: {{padding}};
+      }
+    </style>
+
+	<script src="/static/ext/js/jquery/jquery-1.7.min.js" type="text/javascript" charset="utf-8"></script>
+	<script src="/static/js/Source/jHue/jquery.showusername.js" type="text/javascript" charset="utf-8"></script>
+	<script src="/static/js/Source/jHue/jquery.filechooser.js" type="text/javascript" charset="utf-8"></script>
+	<script src="/static/js/Source/jHue/jquery.contextmenu.js" type="text/javascript" charset="utf-8"></script>
+	<script src="/static/js/Source/jHue/jquery.selector.js" type="text/javascript" charset="utf-8"></script>
+	<script src="/static/js/Source/jHue/jquery.alert.js" type="text/javascript" charset="utf-8"></script>
+	<script src="/static/ext/js/jquery/plugins/jquery.simpleplaceholder.js" type="text/javascript" charset="utf-8"></script>
+	<script src="/static/ext/js/jquery/plugins/jquery.dataTables.1.8.2.min.js" type="text/javascript" charset="utf-8"></script>
+	<script src="/static/ext/js/bootstrap-tabs.js" type="text/javascript" charset="utf-8"></script>
+	<script src="/static/ext/js/bootstrap-dropdown.js" type="text/javascript" charset="utf-8"></script>
+	<script src="/static/ext/js/bootstrap-tabs.js" type="text/javascript" charset="utf-8"></script>
+	<script src="/static/ext/js/bootstrap-modal.js" type="text/javascript" charset="utf-8"></script>
+	<script src="/static/ext/js/bootstrap-twipsy.js" type="text/javascript" charset="utf-8"></script>
+	<script src="/static/ext/js/bootstrap-popover.js" type="text/javascript" charset="utf-8"></script>
+	<script src="/static/ext/js/fileuploader.js" type="text/javascript" charset="utf-8"></script>
+
+	<script type="text/javascript" charset="utf-8">
+		$(document).ready(function(){
+			$("#username").showUsername();
+			$("input:text[placeholder]").simplePlaceholder();
+			$(".submitter").keydown(function(e){
+				if (e.keyCode==13){
+					$(this).closest("form").submit();
+				}
+			}).change(function(){
+				$(this).closest("form").submit();
+			});
+		});
+	</script>
+
+</head>
+<body>
+	<div class="topbar">
+		<div class="topbar-inner">
+			<div class="container-fluid">
+				<a class="brand" href="#">jHue</a>
+				<ul class="nav">
+					<li {% is_selected section "beeswax" %}><a href="/beeswax">Beeswax</a></li>
+
+					<li {% is_selected section "filebrowser" %}><a href="/filebrowser/">File Browser</a></li>
+					<li {% is_selected section "jobsub" %}><a href="/jobsub/">Job Designer</a></li>
+					<li {% is_selected section "jobbrowser" %}><a href="/jobbrowser/jobs/">Job Browser</a></li>
+					<li {% is_selected section "useradmin" %}><a href="/useradmin/">User Admin</a></li>
+					<li {% is_selected section "shell" %}><a href="/shell/">Shell</a></li>
+					<li {% is_selected section "help" %}><a href="/help/">Help</a></li>
+					<li {% is_selected section "about" %}><a href="/about/">About</a></li>
+				</ul>
+				<p class="pull-right">Logged in as <strong><span id="username">xxx</span></strong> - <a href="/accounts/logout">Sign out</a></p>
+			</div>
+		</div>
+	</div>
+

+ 47 - 0
desktop/core/src/desktop/templates/common_header_iframe.html

@@ -0,0 +1,47 @@
+{% comment %}
+Licensed to Cloudera, Inc. under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  Cloudera, Inc. licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+{% endcomment %}
+{% load is_selected %}
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+	<meta name="viewport" content="width=device-width user-scalable=no initial-scale=1" />
+	<link rel="stylesheet" href="/static/ext/css/bootstrap.min.css" type="text/css" media="screen" title="no title" charset="utf-8" />
+	<link rel="stylesheet" href="/static/css/jhue.css" type="text/css" media="screen" title="no title" charset="utf-8" />
+	<link rel="stylesheet" href="/static/ext/css/fileuploader.css" type="text/css" media="screen" title="no title" charset="utf-8" />
+
+	<script src="/static/ext/js/jquery/jquery-1.7.min.js" type="text/javascript" charset="utf-8"></script>
+	<script src="/static/js/Source/jHue/jquery.showusername.js" type="text/javascript" charset="utf-8"></script>
+	<script src="/static/js/Source/jHue/jquery.filechooser.js" type="text/javascript" charset="utf-8"></script>
+	<script src="/static/js/Source/jHue/jquery.contextmenu.js" type="text/javascript" charset="utf-8"></script>
+	<script src="/static/js/Source/jHue/jquery.selector.js" type="text/javascript" charset="utf-8"></script>
+	<script src="/static/js/Source/jHue/jquery.alert.js" type="text/javascript" charset="utf-8"></script>
+	<script src="/static/ext/js/jquery/plugins/jquery.simpleplaceholder.js" type="text/javascript" charset="utf-8"></script>
+	<script src="/static/ext/js/jquery/plugins/jquery.dataTables.1.8.2.min.js" type="text/javascript" charset="utf-8"></script>
+	<script src="/static/ext/js/bootstrap-tabs.js" type="text/javascript" charset="utf-8"></script>
+	<script src="/static/ext/js/bootstrap-dropdown.js" type="text/javascript" charset="utf-8"></script>
+	<script src="/static/ext/js/bootstrap-tabs.js" type="text/javascript" charset="utf-8"></script>
+	<script src="/static/ext/js/bootstrap-modal.js" type="text/javascript" charset="utf-8"></script>
+	<script src="/static/ext/js/bootstrap-twipsy.js" type="text/javascript" charset="utf-8"></script>
+	<script src="/static/ext/js/bootstrap-popover.js" type="text/javascript" charset="utf-8"></script>
+	<script src="/static/ext/js/fileuploader.js" type="text/javascript" charset="utf-8"></script>
+
+</head>
+<body>

+ 14 - 2
desktop/core/src/desktop/views.py

@@ -263,7 +263,7 @@ def commonheader(title, section, padding="60px"):
   """
   Returns the rendered common header
   """
-  return render_to_string("commonheader.html", dict(
+  return render_to_string("common_header.html", dict(
     title=title,
     section=section,
     padding=padding
@@ -273,7 +273,19 @@ def commonfooter():
   """
   Returns the rendered common footer
   """
-  return render_to_string("commonfooter.html")
+  return render_to_string("common_footer.html")
+
+def commonheader_iframe():
+  """
+  Returns the rendered common header for iframes
+  """
+  return render_to_string("common_header_iframe.html")
+
+def commonfooter_iframe():
+  """
+  Returns the rendered common footer for iframes
+  """
+  return render_to_string("common_footer_iframe.html")
 
 # If the app's conf.py has a config_validator() method, call it.
 CONFIG_VALIDATOR = 'config_validator'

+ 13 - 2
desktop/core/static/css/jhue.css

@@ -195,7 +195,7 @@ code {
 
 ul.errorlist {
 	list-style: none outside none;
-	margin-left: 0;
+	margin: 0;
 }
 
 ul.errorlist li {
@@ -771,7 +771,7 @@ div.box {
 .jHueSelectorHeader input[type=text] {
 	float:right;
 	width:100px;
-	height:20px;
+	height:11px;
 }
 
 .jHueSelectorBody {
@@ -789,4 +789,15 @@ div.box {
 .jHueSelector li.selectorDivider {
 	background-color:#EEE;
 	padding:4px;
+}
+
+.modal-body iframe {
+	border:0;
+	width:100%;
+	height:100%;
+	overflow:hidden;
+}
+
+.modal-body iframe.scroll {
+	overflow:auto;
 }

+ 18 - 0
desktop/core/static/js/Source/jHue/jquery.alert.js

@@ -0,0 +1,18 @@
+/*
+* jHue alert plugin
+*/
+var jHueAlert = function(message){
+
+		if ($("#jHueAlertDialog").length==0){
+			var mainDialog = $("<div>").attr("id", "jHueAlertDialog").addClass("modal").addClass("hide").addClass("fade");
+			mainDialog.html('<div class="modal-header"><a href="#" class="close">&times;</a></div><div class="modal-body"></div>');
+			mainDialog.appendTo("body");
+			mainDialog.modal({
+				backdrop: "static",
+				keyboard: true
+			});
+		}
+		var alertDialog= $("#jHueAlertDialog");
+		alertDialog.find(".modal-body").text(message);
+		alertDialog.modal("show");
+};