Author: ivol37 at gmail.com
Date: Tue Dec 21 15:32:30 2010
New Revision: 522

Log:
[AMDATU-222] Initial drop of useradmin gadget. Supports pages view on users 
including CRUD. TODO: edit group memberships and small GUI fixes

Added:
   trunk/amdatu-authorization/useradmin-gadget/
   trunk/amdatu-authorization/useradmin-gadget/pom.xml
   trunk/amdatu-authorization/useradmin-gadget/src/
   trunk/amdatu-authorization/useradmin-gadget/src/main/
   trunk/amdatu-authorization/useradmin-gadget/src/main/java/
   trunk/amdatu-authorization/useradmin-gadget/src/main/java/org/
   trunk/amdatu-authorization/useradmin-gadget/src/main/java/org/amdatu/
   
trunk/amdatu-authorization/useradmin-gadget/src/main/java/org/amdatu/authorization/
   
trunk/amdatu-authorization/useradmin-gadget/src/main/java/org/amdatu/authorization/useradmin/
   
trunk/amdatu-authorization/useradmin-gadget/src/main/java/org/amdatu/authorization/useradmin/gadget/
   
trunk/amdatu-authorization/useradmin-gadget/src/main/java/org/amdatu/authorization/useradmin/gadget/osgi/
   
trunk/amdatu-authorization/useradmin-gadget/src/main/java/org/amdatu/authorization/useradmin/gadget/osgi/Activator.java
   
trunk/amdatu-authorization/useradmin-gadget/src/main/java/org/amdatu/authorization/useradmin/gadget/service/
   
trunk/amdatu-authorization/useradmin-gadget/src/main/java/org/amdatu/authorization/useradmin/gadget/service/UserAdminGadgetImpl.java
   trunk/amdatu-authorization/useradmin-gadget/src/main/resources/
   trunk/amdatu-authorization/useradmin-gadget/src/main/resources/jsp/
   
trunk/amdatu-authorization/useradmin-gadget/src/main/resources/jsp/UserAdminGadget.jsp
   trunk/amdatu-authorization/useradmin-gadget/src/main/resources/static/
   trunk/amdatu-authorization/useradmin-gadget/src/main/resources/static/css/
   
trunk/amdatu-authorization/useradmin-gadget/src/main/resources/static/css/useradmin.css
   trunk/amdatu-authorization/useradmin-gadget/src/main/resources/static/images/
   
trunk/amdatu-authorization/useradmin-gadget/src/main/resources/static/images/end.gif
   (contents, props changed)
   
trunk/amdatu-authorization/useradmin-gadget/src/main/resources/static/images/next.gif
   (contents, props changed)
   
trunk/amdatu-authorization/useradmin-gadget/src/main/resources/static/images/previous.gif
   (contents, props changed)
   
trunk/amdatu-authorization/useradmin-gadget/src/main/resources/static/images/remove.gif
   (contents, props changed)
   
trunk/amdatu-authorization/useradmin-gadget/src/main/resources/static/images/start.gif
   (contents, props changed)
   
trunk/amdatu-authorization/useradmin-gadget/src/main/resources/static/images/useradmin.png
   (contents, props changed)
   trunk/amdatu-authorization/useradmin-gadget/src/main/resources/static/js/
   
trunk/amdatu-authorization/useradmin-gadget/src/main/resources/static/js/useradmin_rest.js
   
trunk/amdatu-authorization/useradmin-rest/src/main/java/org/amdatu/authorization/useradmin/rest/bean/
   
trunk/amdatu-authorization/useradmin-rest/src/main/java/org/amdatu/authorization/useradmin/rest/bean/SearchResultBean.java
   
trunk/amdatu-authorization/useradmin-rest/src/main/java/org/amdatu/authorization/useradmin/rest/bean/UserBean.java
   
trunk/amdatu-core/config-filebased/src/main/resources/conf/org.amdatu.authorization.useradmin.gadget.cfg
Modified:
   trunk/amdatu-authorization/pom.xml
   trunk/amdatu-authorization/useradmin-rest/pom.xml
   
trunk/amdatu-authorization/useradmin-rest/src/main/java/org/amdatu/authorization/useradmin/rest/service/GroupsResource.java
   
trunk/amdatu-authorization/useradmin-rest/src/main/java/org/amdatu/authorization/useradmin/rest/service/ResourceBase.java
   
trunk/amdatu-authorization/useradmin-rest/src/main/java/org/amdatu/authorization/useradmin/rest/service/UsersResource.java
   
trunk/amdatu-libraries/utilities/src/main/java/org/amdatu/libraries/utilities/rest/AtomSyndicationLink.java
   trunk/amdatu-release/pom.xml
   trunk/src/main/resources/conf/felix-config.properties

Modified: trunk/amdatu-authorization/pom.xml
==============================================================================
--- trunk/amdatu-authorization/pom.xml  (original)
+++ trunk/amdatu-authorization/pom.xml  Tue Dec 21 15:32:30 2010
@@ -48,6 +48,7 @@
   <modules>
     <module>login-gadget</module>
     <module>login-service</module>
+    <module>useradmin-gadget</module>
     <module>useradmin-rest</module>
   </modules>
 

Added: trunk/amdatu-authorization/useradmin-gadget/pom.xml
==============================================================================
--- (empty file)
+++ trunk/amdatu-authorization/useradmin-gadget/pom.xml Tue Dec 21 15:32:30 2010
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.amdatu</groupId>
+    <artifactId>org.amdatu.authorization</artifactId>
+    <version>0.1.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.amdatu.authorization.useradmin</groupId>
+  <artifactId>gadget</artifactId>
+  <packaging>bundle</packaging>
+  <name>Amdatu Authorization - User Admin Gadget</name>
+  <description>Provides a UserAdmin gadget to manage users and 
groups</description>
+  
+  <dependencies>  
+    <dependency>
+      <groupId>org.amdatu.web</groupId>
+      <artifactId>httpcontext</artifactId>
+      <scope>provided</scope>
+      <type>bundle</type>
+    </dependency>
+    <dependency>
+      <groupId>org.amdatu.opensocial</groupId>
+      <artifactId>shindig</artifactId>
+      <scope>provided</scope>
+      <type>bundle</type>
+    </dependency>
+    <dependency>
+      <groupId>org.amdatu.opensocial</groupId>
+      <artifactId>gadgetmanagement</artifactId>
+      <scope>provided</scope>
+      <type>bundle</type>
+    </dependency>
+    <dependency>
+      <groupId>org.json</groupId>
+      <artifactId>json</artifactId>
+      <version>20090211</version>
+      <scope>compile</scope>
+    </dependency>       
+  </dependencies>
+  
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <configuration>
+          <instructions>
+            
<Bundle-Activator>org.amdatu.authorization.useradmin.gadget.osgi.Activator</Bundle-Activator>
+            <Bundle-SymbolicName> 
org.amdatu.authorization.useradmin.gadget</Bundle-SymbolicName>
+            <Embed-Dependency>*;scope=compile</Embed-Dependency>
+          </instructions>
+        </configuration>
+      </plugin>   
+    </plugins>        
+  </build> 
+</project>

Added: 
trunk/amdatu-authorization/useradmin-gadget/src/main/java/org/amdatu/authorization/useradmin/gadget/osgi/Activator.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-authorization/useradmin-gadget/src/main/java/org/amdatu/authorization/useradmin/gadget/osgi/Activator.java
     Tue Dec 21 15:32:30 2010
@@ -0,0 +1,52 @@
+/*
+    Copyright (C) 2010 Amdatu.org
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.amdatu.authorization.useradmin.gadget.osgi;
+
+import org.amdatu.authorization.useradmin.gadget.service.UserAdminGadgetImpl;
+import org.amdatu.opensocial.gadgetmanagement.GadgetManagement;
+import org.amdatu.web.httpcontext.HttpContextServiceFactory;
+import org.amdatu.web.httpcontext.ResourceProvider;
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+
+/**
+ * This is the OSGi activator for this gadget bundle.
+ * @author ivol
+ */
+public class Activator extends DependencyActivatorBase {
+    // The resource identifier for this bundle. Resources are only considered 
to be 'ours' when
+    // it is prefixed with this id
+    public final static String RESOURCE_ID = "gadget/useradmin";
+
+    @Override
+    public void init(BundleContext context, DependencyManager manager) throws 
Exception {
+        // Create the LoginServiceImpl which has dependencies with the Log 
service
+        // and HTTP service
+        
manager.add(createComponent().setInterface(ResourceProvider.class.getName(), 
null)
+            .setImplementation(UserAdminGadgetImpl.class)
+            
.add(createServiceDependency().setService(LogService.class).setRequired(true))
+            
.add(createServiceDependency().setService(GadgetManagement.class).setRequired(true))
+            
.add(createServiceDependency().setService(HttpContextServiceFactory.class).setRequired(true))
+            
.add(createConfigurationDependency().setPid(UserAdminGadgetImpl.PID)));
+    }
+
+    @Override
+    public void destroy(BundleContext arg0, DependencyManager arg1) throws 
Exception {
+    }
+}

Added: 
trunk/amdatu-authorization/useradmin-gadget/src/main/java/org/amdatu/authorization/useradmin/gadget/service/UserAdminGadgetImpl.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-authorization/useradmin-gadget/src/main/java/org/amdatu/authorization/useradmin/gadget/service/UserAdminGadgetImpl.java
        Tue Dec 21 15:32:30 2010
@@ -0,0 +1,115 @@
+/*
+    Copyright (C) 2010 Amdatu.org
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.amdatu.authorization.useradmin.gadget.service;
+
+import java.net.URL;
+import java.util.Dictionary;
+
+import org.amdatu.authorization.useradmin.gadget.osgi.Activator;
+import org.amdatu.opensocial.gadgetmanagement.GadgetManagement;
+import org.amdatu.opensocial.shindig.GadgetCategory;
+import org.amdatu.opensocial.shindig.GadgetDefinition;
+import org.amdatu.web.httpcontext.HttpContextServiceFactory;
+import org.amdatu.web.httpcontext.ResourceProvider;
+import org.apache.felix.dm.Component;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.log.LogService;
+
+/**
+ * This service provides the UserAdmin gadget.
+ * @author ivol
+ */
+public class UserAdminGadgetImpl implements ResourceProvider, ManagedService {
+    // The PID and configuration properties
+    public final static String PID = 
"org.amdatu.authorization.useradmin.gadget";
+    private final static String HOSTNAME = "hostname";
+    private final static String PORTNR = "portnr";
+
+    // Service dependencies, injected by the Felix dependency manager
+    private volatile LogService m_logService;
+    private volatile BundleContext m_bundleContext;
+    private volatile HttpContextServiceFactory m_httpContextServiceFactory;
+    private volatile GadgetManagement m_gadgetManagement;
+
+    // The private HTTP context service for this bundle
+    private Component m_httpContextComponent;
+
+    // Configruation entries
+    private volatile String m_hostname;
+    private volatile String m_portnr;
+
+    /**
+     * The init() method is invoked by the Felix dependency manager.
+     */
+    public void start() {
+        // Create our own http context service which registers static 
resources and JSPs automatically
+        m_httpContextComponent = 
m_httpContextServiceFactory.create(m_bundleContext, this);
+
+        if (m_hostname == null || m_portnr == null) {
+            // FIXME: Because of a bug in the Felix dependency manager, 
update() is not invoked when the bundle
+            // is stopped/started. Hence m_hostname and m_portnr will be null. 
To prevent serious problems as
+            // described in issue 
http://jira.amdatu.org/jira/browse/AMDATU-199 we implement this check for now.
+            // For the bug description, see 
http://jira.amdatu.org/jira/browse/AMDATU-174
+            // and https://issues.apache.org/jira/browse/FELIX-2696
+        } else {
+            // Register the gadget with the Gadget management service. Note 
that we can do this as
+            // many times as we want, since the gadget URL is the unique 
identifier
+            String gadgetSpecUrl =
+                "http://"; + m_hostname + ":" + m_portnr + "/" + 
Activator.RESOURCE_ID + "/jsp/UserAdminGadget.jsp";
+            GadgetDefinition gadgetDef = new GadgetDefinition(gadgetSpecUrl, 
GadgetCategory.AMDATU_PLATFORM, false);
+            gadgetDef.setRank(0);
+            m_gadgetManagement.addGadget(gadgetDef);
+            m_logService.log(LogService.LOG_INFO, "Login gadget registered on 
URL '" + gadgetSpecUrl + "'");
+        }
+
+        m_logService.log(LogService.LOG_INFO, getClass().getName() + " service 
started");
+    }
+
+    // The destroy() method is automatically invoked by the Felix dependency 
manager
+    public void destroy() {
+        // Stop the HTTP context service we created ourselves
+        m_httpContextComponent.stop();
+    }
+
+    public URL getResource(String name) {
+        return null;
+    }
+
+    public String getResourceId() {
+        return Activator.RESOURCE_ID;
+    }
+
+    @SuppressWarnings("unchecked")
+    public void updated(Dictionary dictionary) throws ConfigurationException {
+        if (dictionary != null) {
+            checkAvailability(dictionary, new String[] { HOSTNAME, PORTNR });
+            m_hostname = (String) dictionary.get(HOSTNAME);
+            m_portnr = (String) dictionary.get(PORTNR);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private void checkAvailability(Dictionary dictionary, String[] 
mandatoryKeys) throws ConfigurationException {
+        for (String mandatoryKey : mandatoryKeys) {
+            if (dictionary.get(mandatoryKey) == null) {
+                throw new ConfigurationException("Missing configuration key", 
mandatoryKey);
+            }
+        }
+    }
+}

Added: 
trunk/amdatu-authorization/useradmin-gadget/src/main/resources/jsp/UserAdminGadget.jsp
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-authorization/useradmin-gadget/src/main/resources/jsp/UserAdminGadget.jsp
      Tue Dec 21 15:32:30 2010
@@ -0,0 +1,264 @@
+<%@ page language="java" session="false" buffer="none" %>
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"; %>
+
+<c:set var="baseUrl" 
value="http://${pageContext.request.serverName}:${pageContext.request.serverPort}"/>
+<c:set var="restUrl" value="${baseUrl}/rest/services/users/users"/>
+<c:set var="gadgetBaseUrl" value="${baseUrl}/gadget/useradmin"/>
+<c:set var="imgBaseUrl" value="${baseUrl}/gadget/useradmin/static/images" />
+<c:set var="jsBaseUrl" value="${baseUrl}/gadget/useradmin/static/js" />
+<c:set var="cssBaseUrl" value="${baseUrl}/gadget/useradmin/static/css" />
+
+<?xml version="1.0" encoding="UTF-8" ?>
+<Module>
+  <ModulePrefs 
+    title="Amdatu User Admin Gadget" 
+    description="User and Group management"
+    author="Ivo Ladage-van Doorn"
+    screenshot="${gadgetBaseUrl}/static/images/useradmin.png"
+    icon="${gadgetBaseUrl}/static/images/useradmin.png" 
+    height="400">
+    <Require feature="osapi"/>       
+    <Require feature="dynamic-height"/>
+  </ModulePrefs>
+  <Content type="html">
+    <![CDATA[
+    <link rel="stylesheet" href="/dashboard/static/css/dashboard.css">
+    <link rel="stylesheet" href="${cssBaseUrl}/useradmin.css">
+    
+    <script type="text/javascript" 
src="/dashboard/static/js/lib/jquery-1.4.2.min.js"></script>
+    <script type="text/javascript" 
src="/dashboard/static/js/lib/jquery-ui-1.8.2.custom.min.js"></script>
+    <script type="text/javascript" 
src="${jsBaseUrl}/useradmin_rest.js"></script>
+    
+    <p>
+      <div id="users"></div>
+      
+      <hr/>
+      
+      <input type="button" value="Add user" id="adduserbutton" 
onclick="javascript:showNewUser();"/>
+      
+      <fieldset id="details" style="display:none;visibility:hidden">
+        <legend id="details_legend">Details</legend>
+          <table width="95%">
+            <tr><td>Username</td><td><input type="edit" id="username" value="" 
disabled="disabled"/></td></tr>
+            <tr><td id="password_label">New password</td><td><input 
type="password" id="password" value="" 
onkeyup="javascript:validatePassword();"/></td></tr>
+          </table>
+          <input type="submit" id="save" value="Save" 
onclick="javascript:saveUser();" disabled="disabled"/>
+          <input type="button" value="Cancel" 
onclick="javascript:showAddButton();"/>
+          <input type="hidden" id="method" value="update"/>
+          <div id="message"></div>
+      </fieldset>
+      
+    
+    <script type="text/javascript"> 
+      // Callback function invoked when users have been loaded
+      function onUsersLoaded(response) {
+        var jsonData = response.data["searchresult"];
+        var pageStartIndex = jsonData["pageStartIndex"];
+        var pageEndIndex = jsonData["pageEndIndex"];
+        var resultCount = jsonData["resultCount"];
+        
+        var links = jsonData["links"];
+        for (i=0; i<links.length; i++) {
+          if (links[i]["rel"] == "Start") {
+             var start = getImageLink("start.gif", links[i]["href"]);
+          } else if (links[i]["rel"] == "Prev") {
+             var prev = getImageLink("previous.gif", links[i]["href"]);
+          } else if (links[i]["rel"] == "Next") {
+             var next = getImageLink("next.gif", links[i]["href"]);
+          } else if (links[i]["rel"] == "End") {
+             var end = getImageLink("end.gif", links[i]["href"]);
+          } 
+        }
+        
+        var html = "";
+        
+        // First add the table header
+        var users = jsonData["users"];
+        if (users) {
+          html += "<table class='stripeMe'>";
+          html += "<tr><th>Username</th><th>Delete</th></tr>";    
+          for (i=0; i<users.length; i++) {
+            var user = users[i];
+            var link = user["link"].href;
+            var editUser = " onclick=\"javascript:editUser('" + link + "');\"";
+            var deleteUser = " onclick=\"javascript:confirmDeleteUser('" + 
link + "', '" + user["name"] + "');\"";
+            html += "<tr><td><a href='#'" + editUser + ">" + user["name"] + 
"</a></td>";
+            html += "<td align='center'><a href='#'" + deleteUser + "><img 
src='/gadget/useradmin/static/images/remove.gif'/></a></td></tr>";
+          }
+          html += "</table></center></p>";
+        }
+        html += "<p><center><table><tr>";
+        html += "<td>" + start + "</td>";
+        html += "<td>" + prev + "</td>";
+        html += "<td>Displaying results <b>" + pageStartIndex + "-" + 
pageEndIndex + "<b> of <b>" + resultCount + "</b></td>";
+        html += "<td>" + next + "</td>";
+        html += "<td>" + end + "</td></table>"; 
+        
+        var resultDiv = document.getElementById("users");
+        resultDiv.innerHTML = html;
+        
+        // make the table zebra-style
+        $(".stripeMe tr").mouseover(function() {$(this).addClass("over");});
+        $(".stripeMe tr").mouseout(function() {$(this).removeClass("over");});
+        $(".stripeMe tr:even").addClass("alt");
+
+        $("div.users").wrap("<fieldset><legend>User</legend></fieldset>");
+
+        gadgets.window.adjustHeight();
+      }          
+      
+      // Callback function invoked when one specific user details have been 
loaded
+      function onUserLoaded(response) {
+        if (response.rc == 200) {
+          var user = response.data["users"];  
+          document.getElementById('username').value = user["name"];
+          gadgets.window.adjustHeight();
+         } else if (response.rc == 404) {
+          showError("User could not be found.");
+        } else {
+          showError(response.errors);
+        }   
+      }
+      
+      // Callback function invoked when the password of the user has been 
changed
+      function onPasswordChanged(response) {
+        if (response.rc == 200) {
+          showInfo('Password changed successfully');
+        } else if (response.rc == 404) {
+          showError("User to update credential for could not be found.");
+        } else {
+          showError(response.errors);
+        } 
+      }
+      
+      // Callback function invoked when the the user has been removed
+      function onUserDeleted(response) {
+        if (response.rc == 200) {
+          // User added successfully
+          loadUsers("${restUrl}?maxResults=5", onUsersLoaded);
+          gadgets.window.adjustHeight();
+        } else if (response.rc == 304) {
+          showError("User to delete could not be found.");
+        } else {
+          showError(response.errors);
+        } 
+      }
+      
+      function onUserAdded(response) {
+        if (response.rc == 200) {
+          // User added successfully
+          showInfo('User added successfully');
+          loadUsers("${restUrl}?maxResults=5", onUsersLoaded);
+          gadgets.window.adjustHeight();
+        } else if (response.rc == 304) {
+          showError("A user with this name already exists.");
+        } else {
+          showError(response.errors);
+        }    
+      }
+      
+      function saveUser() {
+        var method = document.getElementById("method").value;
+        var username = document.getElementById("username").value;
+        var newpassword = document.getElementById('password').value
+        var url = "${baseUrl}/rest/services/users/users/" + username;
+        if (method == "update") {
+          url += "/credentials/password";
+          updatePassword(url, onPasswordChanged, newpassword);
+        } else {
+          addUser(url, onUserAdded);
+        }
+      }
+      
+      function validatePassword() {
+        if (document.getElementById("password").value != "") {
+          document.getElementById("save").disabled = "";
+        } else {
+          document.getElementById("save").disabled = "disabled";
+        }
+      }
+      
+      function getImageLink(image, href) {
+        var result = "<img src='${imgBaseUrl}/" + image + "' 
onClick='javascript:loadUsers(\"${baseUrl}" + href + "\", onUsersLoaded)'/>";
+        return result;
+      }
+      
+      function editUser(link) {
+        loadUser('${baseUrl}' + link, onUserLoaded);
+        showEditUser();
+      }
+      
+      function confirmDeleteUser(link, username) {
+        if (confirm('Are you sure you want to delete "' + username + '"?')) {
+          deleteUser('${baseUrl}' + link, onUserDeleted);
+        }
+      }
+      
+      function showInfo(msg) {
+        if (msg != "") {
+          document.getElementById('message').innerHTML = "<p><font 
color='green'>" + msg + "</font></p>";
+        } else {
+          document.getElementById('message').innerHTML = "";
+        }
+        gadgets.window.adjustHeight();
+      }
+      
+      function showError(msg) {
+        if (msg != "") {
+          document.getElementById('message').innerHTML = "<p><font 
color='red'>" + msg + "</font></p>";
+        } else {
+          document.getElementById('message').innerHTML = "";
+        }
+        gadgets.window.adjustHeight();
+      }
+      
+      function showAddButton() {
+        show('adduserbutton');
+        hide('details');
+        gadgets.window.adjustHeight();
+      }
+      
+      function showNewUser() {
+        document.getElementById('details_legend').innerHTML = "New user";
+        document.getElementById('password_label').innerHTML = "Password";
+        document.getElementById('username').value = "";
+        document.getElementById('username').disabled = "";
+        document.getElementById('password').value = "";
+        document.getElementById('method').value = "add";
+        showError("");
+        show('details');
+        hide('adduserbutton');
+        gadgets.window.adjustHeight();
+      }
+      
+      function showEditUser() {
+       document.getElementById('details_legend').innerHTML = "User details";
+        document.getElementById('password_label').innerHTML = "New password";
+        document.getElementById('username').disabled = "disabled";
+        document.getElementById('method').value = "update";
+        showError("");
+        show('details');
+        hide('adduserbutton');
+        gadgets.window.adjustHeight();
+      }
+      
+      function show(id) {
+        var obj = document.getElementById(id);
+        obj.style.visibility = "";
+        obj.style.display = "";
+      }
+      
+      function hide(id) {
+        var obj = document.getElementById(id);
+        obj.style.visibility = "hidden";
+        obj.style.display = "none";
+      }
+      
+      $(document).ready(function(){
+        loadUsers("${restUrl}?maxResults=5", onUsersLoaded);
+      });
+      
+    </script>
+    ]]>
+  </Content>
+</Module>

Added: 
trunk/amdatu-authorization/useradmin-gadget/src/main/resources/static/css/useradmin.css
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-authorization/useradmin-gadget/src/main/resources/static/css/useradmin.css
     Tue Dec 21 15:32:30 2010
@@ -0,0 +1,37 @@
+body {
+  background: #fff;
+}
+
+table {
+  border-collapse: collapse;
+  font-size: 1.1em;
+  width: 100%;
+}
+
+th {
+  background: #3e83c9;
+  color: #fff;
+  font-weight: bold;
+  padding: 2px 11px;
+  text-align: left;
+  border-right: 1px solid #fff;
+  line-height: 1.2;
+}
+
+td {
+  padding: 2px 1px;
+  border-bottom: 1px solid #95bce2;
+  vertical-align: top;
+}
+
+td * {
+  padding: 2px 1px;
+}
+
+tr.alt td {
+  background: #ecf6fc;
+}
+
+tr.over td {
+  background: #bcd4ec;
+}

Added: 
trunk/amdatu-authorization/useradmin-gadget/src/main/resources/static/images/end.gif
==============================================================================
Binary file. No diff available.

Added: 
trunk/amdatu-authorization/useradmin-gadget/src/main/resources/static/images/next.gif
==============================================================================
Binary file. No diff available.

Added: 
trunk/amdatu-authorization/useradmin-gadget/src/main/resources/static/images/previous.gif
==============================================================================
Binary file. No diff available.

Added: 
trunk/amdatu-authorization/useradmin-gadget/src/main/resources/static/images/remove.gif
==============================================================================
Binary file. No diff available.

Added: 
trunk/amdatu-authorization/useradmin-gadget/src/main/resources/static/images/start.gif
==============================================================================
Binary file. No diff available.

Added: 
trunk/amdatu-authorization/useradmin-gadget/src/main/resources/static/images/useradmin.png
==============================================================================
Binary file. No diff available.

Added: 
trunk/amdatu-authorization/useradmin-gadget/src/main/resources/static/js/useradmin_rest.js
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-authorization/useradmin-gadget/src/main/resources/static/js/useradmin_rest.js
  Tue Dec 21 15:32:30 2010
@@ -0,0 +1,55 @@
+// Loads users and invokes the callback function with the result
+function loadUsers(url, callback) {
+  var params = {};
+  params[gadgets.io.RequestParameters.CONTENT_TYPE] = 
gadgets.io.ContentType.JSON;
+  params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.GET;
+  url = addNoCache(url);
+  gadgets.io.makeRequest(url, callback, params);
+}
+
+// Loads one specific user and invokes the callback function with the result
+function loadUser(url, callback) {
+  var params = {};
+  params[gadgets.io.RequestParameters.CONTENT_TYPE] = 
gadgets.io.ContentType.JSON;
+  params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.GET;
+  url = addNoCache(url);  
+  gadgets.io.makeRequest(url, callback, params);
+}
+
+// Updates the password credential of the user
+function updatePassword(url, callback, newpassword) {
+  var params = {};
+  params[gadgets.io.RequestParameters.CONTENT_TYPE] = 
gadgets.io.ContentType.JSON;
+  params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.POST;
+  var postdata = {value : newpassword};
+  params[gadgets.io.RequestParameters.POST_DATA] = 
gadgets.io.encodeValues(postdata);
+  url = addNoCache(url);  
+  gadgets.io.makeRequest(url, callback, params);
+}
+
+// Adds a new user
+ function addUser(url, callback) {
+  var params = {};
+  params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.PUT;
+  url = addNoCache(url);  
+  gadgets.io.makeRequest(url, callback, params);
+}
+      
+// Deletes the specified user
+function deleteUser(url, callback) {
+  var params = {};
+  params[gadgets.io.RequestParameters.CONTENT_TYPE] = 
gadgets.io.ContentType.JSON;
+  params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.DELETE;
+  url = addNoCache(url);
+  gadgets.io.makeRequest(url, callback, params);
+}
+
+function addNoCache(url) {
+  var ts = new Date().getTime();
+  if (url.indexOf("?") == -1) {
+    url += "?nocache=" + ts;
+  } else {
+    url += "&nocache=" + ts;
+  }
+  return url;
+}
\ No newline at end of file

Modified: trunk/amdatu-authorization/useradmin-rest/pom.xml
==============================================================================
--- trunk/amdatu-authorization/useradmin-rest/pom.xml   (original)
+++ trunk/amdatu-authorization/useradmin-rest/pom.xml   Tue Dec 21 15:32:30 2010
@@ -32,7 +32,13 @@
       <artifactId>json</artifactId>
       <version>20090211</version>
       <scope>compile</scope>
-    </dependency>       
+    </dependency>
+    <dependency>
+      <groupId>org.amdatu.libraries</groupId>
+      <artifactId>utilities</artifactId>
+      <version>${platform.version}</version>
+      <scope>compile</scope>
+    </dependency>    
   </dependencies>
   
   <build>

Added: 
trunk/amdatu-authorization/useradmin-rest/src/main/java/org/amdatu/authorization/useradmin/rest/bean/SearchResultBean.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-authorization/useradmin-rest/src/main/java/org/amdatu/authorization/useradmin/rest/bean/SearchResultBean.java
  Tue Dec 21 15:32:30 2010
@@ -0,0 +1,114 @@
+/*
+    Copyright (C) 2010 Amdatu.org
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.amdatu.authorization.useradmin.rest.bean;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.amdatu.libraries.utilities.rest.AtomSyndicationLink;
+import org.osgi.service.useradmin.Role;
+
+ at SuppressWarnings("restriction")
+ at XmlRootElement(name = "searchresult")
+ at XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
+public class SearchResultBean {
+    // Atom links
+    private List<AtomSyndicationLink> m_links = new 
ArrayList<AtomSyndicationLink>();
+
+    // Paged result properties
+    private int m_pageStartIndex; // Index of first result displayed on 
current page 
+    private int m_pageEndIndex; // End index of last result displayed on 
current page
+    private int m_pageCount; // Amount of results displayed on current page
+    private int m_pageMax;  // Maximum number of results displayed on one page
+
+    // Result count: total amount of available results (unpaged)
+    private int m_resultCount;
+    
+    // The actual results
+    private List<UserBean> m_users = new ArrayList<UserBean>();
+
+    public int getPageStartIndex() {
+        return m_pageStartIndex;
+    }
+
+    public void setPageStartIndex(int pageStartIndex) {
+        m_pageStartIndex = pageStartIndex;
+    }
+
+    public int getPageEndIndex() {
+        return m_pageEndIndex;
+    }
+
+    public void setPageEndIndex(int pageEndIndex) {
+        m_pageEndIndex = pageEndIndex;
+    }
+
+    public int getPageCount() {
+        return m_pageCount;
+    }
+
+    public void setPageCount(int pageCount) {
+        m_pageCount = pageCount;
+    }
+
+    public int getPageMax() {
+        return m_pageMax;
+    }
+
+    public void setPageMax(int pageMax) {
+        m_pageMax = pageMax;
+    }
+
+    public int getResultCount() {
+        return m_resultCount;
+    }
+
+    public void setResultCount(int resultCount) {
+        m_resultCount = resultCount;
+    }
+
+    public List<AtomSyndicationLink> getLinks() {
+        return m_links;
+    }
+
+    public void setLinks(List<AtomSyndicationLink> links) {
+        m_links = links;
+    }
+
+    public void addLink(AtomSyndicationLink link) {
+        m_links.add(link);
+    }
+
+    public List<UserBean> getUsers() {
+        return m_users;
+    }
+
+    public void setUsers(List<UserBean> users) {
+        m_users = users;
+    }
+    
+    public void addUsers(List<Role> users) {
+        for (Role role : users) {
+            UserBean user = UserBean.fromRole(role);
+            m_users.add(user);
+        }
+    }
+}

Added: 
trunk/amdatu-authorization/useradmin-rest/src/main/java/org/amdatu/authorization/useradmin/rest/bean/UserBean.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-authorization/useradmin-rest/src/main/java/org/amdatu/authorization/useradmin/rest/bean/UserBean.java
  Tue Dec 21 15:32:30 2010
@@ -0,0 +1,66 @@
+/*
+    Copyright (C) 2010 Amdatu.org
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.amdatu.authorization.useradmin.rest.bean;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.amdatu.libraries.utilities.rest.AtomSyndicationLink;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+
+ at SuppressWarnings("restriction")
+ at XmlRootElement(name = "users")
+ at XmlAccessorType(XmlAccessType.NONE)
+public class UserBean {
+    @XmlAttribute(name = "name")
+    private String m_name;
+
+    @XmlElement(name = "link")
+    private AtomSyndicationLink m_link;
+
+    public UserBean() {
+    }
+
+    public String getName() {
+        return m_name;
+    }
+
+    public void setName(String name) {
+        m_name = name;
+    }
+
+    public AtomSyndicationLink getLink() {
+        return m_link;
+    }
+
+    public void setLink(AtomSyndicationLink link) {
+        m_link = link;
+    }
+
+    public static UserBean fromRole(Role role) {
+        UserBean user = new UserBean();
+        String name = ((User) role).getName();
+        user.setName(name);
+        String href = "/rest/services/users/users/" + name;
+        user.setLink(new 
AtomSyndicationLink().setHref(href).setRel("Alternate").setType("application/json"));
+        return user;
+    }
+}

Modified: 
trunk/amdatu-authorization/useradmin-rest/src/main/java/org/amdatu/authorization/useradmin/rest/service/GroupsResource.java
==============================================================================
--- 
trunk/amdatu-authorization/useradmin-rest/src/main/java/org/amdatu/authorization/useradmin/rest/service/GroupsResource.java
 (original)
+++ 
trunk/amdatu-authorization/useradmin-rest/src/main/java/org/amdatu/authorization/useradmin/rest/service/GroupsResource.java
 Tue Dec 21 15:32:30 2010
@@ -277,4 +277,8 @@
             return buildNotFound();
         }
     }
+    
+    protected String getBaseUrl() {
+        return "/rest/services/groups/groups";
+    }
 }

Modified: 
trunk/amdatu-authorization/useradmin-rest/src/main/java/org/amdatu/authorization/useradmin/rest/service/ResourceBase.java
==============================================================================
--- 
trunk/amdatu-authorization/useradmin-rest/src/main/java/org/amdatu/authorization/useradmin/rest/service/ResourceBase.java
   (original)
+++ 
trunk/amdatu-authorization/useradmin-rest/src/main/java/org/amdatu/authorization/useradmin/rest/service/ResourceBase.java
   Tue Dec 21 15:32:30 2010
@@ -17,7 +17,7 @@
 package org.amdatu.authorization.useradmin.rest.service;
 
 import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 
@@ -25,6 +25,9 @@
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 
+import org.amdatu.authorization.useradmin.rest.bean.SearchResultBean;
+import org.amdatu.authorization.useradmin.rest.bean.UserBean;
+import org.amdatu.libraries.utilities.rest.AtomSyndicationLink;
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.osgi.framework.InvalidSyntaxException;
@@ -38,7 +41,7 @@
     // Service dependencies injected by the depedency manager
     protected volatile LogService m_logService;
     protected volatile UserAdmin m_userAdmin;
-    
+
     // Disable HTTP caching in this REST interface
     private static CacheControl m_cacheControl;
     static {
@@ -48,6 +51,7 @@
 
     /**
      * Returns the roles (users or groups) for the specified filter options.
+     * 
      * @param filter the filter to pass to UserAdmin
      * @param sortOrder The sort order ('ascending' or 'descending')
      * @param startIndex The startindex
@@ -73,91 +77,108 @@
             if (roles != null) {
                 for (Role role : roles) {
                     if (role.getType() == roleType) {
-                        if (filter == null || role.getName().matches(".*" + 
filter + ".*")) {
+                        if (filter == null || role.getName().matches("?i:.*" + 
filter + ".*")) {
                             filteredRoles.add(role);
                         }
                     }
                 }
             }
 
-            // Now filter from startIndex - endIndex
-            List<Role> roleRange = new ArrayList<Role>();
-            for (int i = (startIndex - 1); i < (startIndex + maxResults - 1) 
&& i < filteredRoles.size(); i++) {
-                roleRange.add(filteredRoles.get(i));
-            }
-
             // Finally sort it
             Comparator<Role> userComparator = new Comparator<Role>() {
                 public int compare(Role r1, Role r2) {
                     if (descending) {
                         return -r1.getName().compareTo(r2.getName());
-                    } else {
-                        return r1.getName().compareTo(r2.getName());
                     }
+                    return r1.getName().compareTo(r2.getName());
                 }
             };
-            Arrays.sort(roleRange.toArray(new Role[roleRange.size()]), 
userComparator);
+            Collections.sort(filteredRoles, userComparator);
 
-            JSONObject jsonObject = new JSONObject();
-            for (Role role : roleRange) {
-                JSONObject jsonUser = getJSON(role);
-                jsonObject.append("entry", jsonUser);
+            // Now filter from startIndex - endIndex
+            List<Role> roleRange = new ArrayList<Role>();
+            for (int i = (startIndex - 1); i < (startIndex + maxResults - 1) 
&& i < filteredRoles.size(); i++) {
+                roleRange.add(filteredRoles.get(i));
             }
 
-            jsonObject.put("startIndex", roleRange.size() > 0 ? Math.max(1, 
startIndex) : 0);
-            jsonObject.put("endIndex", (startIndex + roleRange.size() - 1));
-            jsonObject.put("firstStartIndex", roleRange.size() > 0 ? 1 : 0);
-            if (filteredRoles.size() % maxResults == 0) {
-                jsonObject.put("lastStartIndex", Math.max(0, 
filteredRoles.size() - maxResults + 1));
-            } else {
-                jsonObject.put("lastStartIndex", 1 + filteredRoles.size() - 
(filteredRoles.size() % maxResults));
-            }
-            jsonObject.put("maxResults", maxResults);
-            jsonObject.put("resultCount", roleRange.size());
-            jsonObject.put("totalCount", filteredRoles.size());
+            SearchResultBean result = new SearchResultBean();
+            result.addUsers(roleRange);
 
-            return buildOK(jsonObject);
-        } catch (InvalidSyntaxException e) {
-            m_logService.log(LogService.LOG_ERROR, "Unable to retrieve roles 
for filter '" + filter + "', startIndex '"
-                    + startIndex + "' maxResults '" + maxResults + "'", e);
-        } catch (JSONException e) {
+            // Calculate paging
+            Paging paging = new Paging();
+            paging.calculate(startIndex, roleRange.size(), maxResults, 
filteredRoles.size());
+            String debugMsg = "Calculated paging results for total of '" + 
paging.resultCount + "' results:\n";
+            debugMsg += "    Current page=" + paging.pageStartIndex + "-" + 
paging.pageEndIndex + "\n";
+            debugMsg += "    Start page=" + paging.firstStartIndex + "-" + 
paging.firstEndIndex + "\n";
+            debugMsg += "    Previous page=" + paging.previousStartIndex + "-" 
+ paging.previousEndIndex + "\n";
+            debugMsg += "    Next page=" + paging.nextStartIndex + "-" + 
paging.nextEndIndex + "\n";
+            debugMsg += "    End page=" + paging.lastStartIndex + "-" + 
paging.lastEndIndex;
+            m_logService.log(LogService.LOG_DEBUG, debugMsg);
+
+            // Set the 4 Atom links first, previous, next and start
+            String url = "";
+            
+            // Start
+            url = getBaseUrl() + "?startIndex=" + paging.firstStartIndex + 
"&endIndex=" + paging.firstEndIndex + "&maxResults=" + maxResults;
+            result.addLink(new 
AtomSyndicationLink().setHref(url).setRel("Start").setType("application/json"));
+
+            // Prev
+            url = getBaseUrl() + "?startIndex=" + paging.previousStartIndex + 
"&endIndex=" + paging.previousEndIndex + "&maxResults=" + maxResults;
+            result.addLink(new 
AtomSyndicationLink().setHref(url).setRel("Prev").setType("application/json"));
+
+            // Next
+            url = getBaseUrl() + "?startIndex=" + paging.nextStartIndex + 
"&endIndex=" + paging.nextEndIndex + "&maxResults=" + maxResults;
+            result.addLink(new 
AtomSyndicationLink().setHref(url).setRel("Next").setType("application/json"));
+            
+            // Last
+            url = getBaseUrl() + "?startIndex=" + paging.lastStartIndex + 
"&endIndex=" + paging.lastEndIndex + "&maxResults=" + maxResults;
+            result.addLink(new 
AtomSyndicationLink().setHref(url).setRel("End").setType("application/json"));
+
+            
+            result.setPageMax(paging.pageMax);
+            result.setPageStartIndex(paging.pageStartIndex);
+            result.setPageEndIndex(paging.pageEndIndex);
+            result.setPageCount(paging.pageCount);
+            result.setResultCount(paging.resultCount);
+
+            return Response.ok(result).cacheControl(m_cacheControl).build();
+        }
+        catch (InvalidSyntaxException e) {
             m_logService.log(LogService.LOG_ERROR, "Unable to retrieve roles 
for filter '" + filter + "', startIndex '"
-                    + startIndex + "' maxResults '" + maxResults + "'", e);
+                + startIndex + "' maxResults '" + maxResults + "'", e);
         }
         return buildServerError();
     }
 
     protected Response getRole(String name, int roleType) {
-        try {
-            Role role = m_userAdmin.getRole(name);
-            if (role != null && role.getType() == roleType) {
-                JSONObject jsonRole = getJSON(role);
-                return buildOK(jsonRole);
-            } else {
-                return buildNotFound();
-            }
-        } catch (JSONException e) {
-            m_logService.log(LogService.LOG_ERROR, "Unable to retrieve role 
named '" + name + "'", e);
+        Role role = m_userAdmin.getRole(name);
+        if (role != null && role.getType() == roleType) {
+            UserBean user = UserBean.fromRole(role);
+            return Response.ok(user).cacheControl(m_cacheControl).build();
+        }
+        else {
+            return buildNotFound();
         }
-        return buildServerError();
     }
 
     protected Response createRole(String name, int roleType) {
         Role role = m_userAdmin.createRole(name, roleType);
         if (role != null) {
             return buildOK();
-        } else {
+        }
+        else {
             return buildNotModified("Role could not be created");
         }
     }
-    
+
     @SuppressWarnings("unchecked")
     protected Response setCredential(String name, String key, String value, 
int roleType) {
         Role role = m_userAdmin.getRole(name);
         if (role != null && role.getType() == roleType) {
             ((User) role).getCredentials().put(key, value);
             return buildOK();
-        } else {
+        }
+        else {
             return buildNotFound();
         }
     }
@@ -168,7 +189,8 @@
         if (role != null && role.getType() == roleType) {
             role.getProperties().put(key, value);
             return buildOK();
-        } else {
+        }
+        else {
             return buildNotFound();
         }
     }
@@ -178,10 +200,12 @@
         if (role != null && role.getType() == roleType) {
             if (m_userAdmin.removeRole(name)) {
                 return buildOK();
-            } else {
+            }
+            else {
                 return buildNotModified("Role to remove not found");
             }
-        } else {
+        }
+        else {
             return buildNotModified("Role to remove not found");
         }
     }
@@ -190,6 +214,7 @@
         JSONObject jsonRole = new JSONObject();
         jsonRole.put("name", role.getName());
         jsonRole.put("properties", role.getProperties());
+
         if (role.getType() == Role.GROUP) {
             Role[] members = ((Group) role).getMembers();
             JSONObject jsonMembers = new JSONObject();
@@ -213,24 +238,92 @@
         }
         return jsonRole;
     }
-    
+
     protected Response buildOK() {
         return Response.ok().cacheControl(m_cacheControl).build();
     }
-    
+
     protected Response buildOK(JSONObject jsonObject) {
         return Response.ok(jsonObject.toString(), 
MediaType.APPLICATION_JSON_TYPE).cacheControl(m_cacheControl).build();
     }
-    
+
     protected Response buildNotFound() {
         return 
Response.status(Response.Status.NOT_FOUND).cacheControl(m_cacheControl).build();
     }
-    
+
     protected Response buildNotModified(String msg) {
         return Response.notModified(msg).cacheControl(m_cacheControl).build();
     }
-    
+
     protected Response buildServerError() {
         return Response.serverError().cacheControl(m_cacheControl).build();
     }
+
+    class Paging {
+        int pageStartIndex; // Index of first result displayed on current page
+        int pageEndIndex; // End index of last result displayed on current page
+        int pageCount; // Amount of results displayed on current page
+        int pageMax; // Maximum number of results displayed on one page
+        int resultCount; // Total amount of available results;
+        
+        int firstStartIndex; // Start index of first page
+        int firstEndIndex; // Last index of first page
+        int previousStartIndex; // // Start index of previous page
+        int previousEndIndex; // Last index of previous page
+        int nextStartIndex; // Start index of next page
+        int nextEndIndex; // Last index of next page
+        int lastStartIndex; // // Start index of last page
+        int lastEndIndex; // Last index of last page
+
+        /**
+         * Calculate paging
+         * @param currentStartIndex The requested start index to display
+         * @param pageResults The amount of results in the current selection
+         * @param maxResults The maximum amount of results displayed on one 
page
+         * @param totalResults The total amount of available results
+         */
+        public void calculate(int currentStartIndex, int pageResults, int 
maxResults, int totalResults) {
+            resultCount = totalResults;
+            pageCount = pageResults;
+            pageMax = maxResults;
+            pageStartIndex = pageResults > 0 ? Math.max(1, currentStartIndex) 
: 0;
+            pageEndIndex = pageStartIndex + (pageResults - 1);
+            
+            // Calculate first page
+            firstStartIndex = pageResults > 0 ? 1 : 0;
+            firstEndIndex = Math.min(pageResults, maxResults);
+            
+            // Calculate previous page
+            if (currentStartIndex - maxResults < firstStartIndex) {
+                previousStartIndex = firstStartIndex;
+                previousEndIndex = firstEndIndex;
+            } else {
+                previousStartIndex = currentStartIndex - maxResults;
+                previousEndIndex = currentStartIndex - 1;
+            }
+            
+            // Calculate last page
+            if (totalResults % maxResults == 0) {
+                lastStartIndex = Math.max(0, totalResults - maxResults + 1);
+            }
+            else {
+                lastStartIndex = 1 + totalResults - (totalResults % 
maxResults);
+            }
+            lastEndIndex = totalResults;
+            
+            // Calculate next page
+            if (currentStartIndex + maxResults > lastStartIndex) {
+                nextStartIndex = lastStartIndex;
+            } else {
+                nextStartIndex = currentStartIndex + maxResults;
+            }
+            if (nextStartIndex + maxResults > lastEndIndex) {
+                nextEndIndex = lastEndIndex;
+            } else {
+                nextEndIndex = Math.min(nextStartIndex + maxResults, 
totalResults);
+            }
+        }
+    }
+
+    protected abstract String getBaseUrl();
 }

Modified: 
trunk/amdatu-authorization/useradmin-rest/src/main/java/org/amdatu/authorization/useradmin/rest/service/UsersResource.java
==============================================================================
--- 
trunk/amdatu-authorization/useradmin-rest/src/main/java/org/amdatu/authorization/useradmin/rest/service/UsersResource.java
  (original)
+++ 
trunk/amdatu-authorization/useradmin-rest/src/main/java/org/amdatu/authorization/useradmin/rest/service/UsersResource.java
  Tue Dec 21 15:32:30 2010
@@ -20,6 +20,7 @@
 import javax.ws.rs.DefaultValue;
 import javax.ws.rs.FormParam;
 import javax.ws.rs.GET;
+import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
@@ -130,7 +131,7 @@
      * @return a 400 response in case the credential was updated.<br/>
      *         a 404 response in case the user with the specified name does 
not exist.
      */
-    @PUT
+    @POST
     @Path("{name}/credentials/{key}")
     @Produces({MediaType.APPLICATION_JSON})
     public Response setCredential(@PathParam("name") final String name,
@@ -172,4 +173,8 @@
     public Response removeUser(@PathParam("name") final String name) {
         return super.removeRole(name, Role.USER);
     }
+    
+    protected String getBaseUrl() {
+        return "/rest/services/users/users";
+    }
 }

Added: 
trunk/amdatu-core/config-filebased/src/main/resources/conf/org.amdatu.authorization.useradmin.gadget.cfg
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-core/config-filebased/src/main/resources/conf/org.amdatu.authorization.useradmin.gadget.cfg
    Tue Dec 21 15:32:30 2010
@@ -0,0 +1,3 @@
+# The hostname and portnr
+hostname=${server.hostname}
+portnr=${server.port}
\ No newline at end of file

Modified: 
trunk/amdatu-libraries/utilities/src/main/java/org/amdatu/libraries/utilities/rest/AtomSyndicationLink.java
==============================================================================
--- 
trunk/amdatu-libraries/utilities/src/main/java/org/amdatu/libraries/utilities/rest/AtomSyndicationLink.java
 (original)
+++ 
trunk/amdatu-libraries/utilities/src/main/java/org/amdatu/libraries/utilities/rest/AtomSyndicationLink.java
 Tue Dec 21 15:32:30 2010
@@ -56,7 +56,7 @@
      * get new information or change the state of the application.
      *
      * @param href the href attribute of an Atom Syndication link
-     * @return the Aton Syndication Link itself, according to the Builder 
pattern
+     * @return the Atom Syndication Link itself, according to the Builder 
pattern
      */
     public AtomSyndicationLink setHref(String href) {
         m_href = href;
@@ -80,7 +80,7 @@
      * http://www.w3.org/TR/html4/struct/links.html#adef-rel
      *
      * @param rel The rel attribute of an Atom Syndication link
-     * @return the Aton Syndication Link itself, according to the Builder 
pattern
+     * @return the Atom Syndication Link itself, according to the Builder 
pattern
      */
     public AtomSyndicationLink setRel(String rel) {
         m_rel = rel;
@@ -102,7 +102,7 @@
      * resource the URL points to.
      *
      * @param type The type attribute of an Atom Syndication link
-     * @return the Aton Syndication Link itself, according to the Builder 
pattern
+     * @return the Atom Syndication Link itself, according to the Builder 
pattern
      */
     public AtomSyndicationLink setType(String type) {
         m_type = type;

Modified: trunk/amdatu-release/pom.xml
==============================================================================
--- trunk/amdatu-release/pom.xml        (original)
+++ trunk/amdatu-release/pom.xml        Tue Dec 21 15:32:30 2010
@@ -72,6 +72,13 @@
     </dependency>
     <dependency>
       <groupId>org.amdatu.authorization.useradmin</groupId>
+      <artifactId>gadget</artifactId>
+      <version>${platform.version}</version>
+      <scope>compile</scope>
+      <type>bundle</type>
+    </dependency>    
+    <dependency>
+      <groupId>org.amdatu.authorization.useradmin</groupId>
       <artifactId>rest</artifactId>
       <version>${platform.version}</version>
       <scope>compile</scope>

Modified: trunk/src/main/resources/conf/felix-config.properties
==============================================================================
--- trunk/src/main/resources/conf/felix-config.properties       (original)
+++ trunk/src/main/resources/conf/felix-config.properties       Tue Dec 21 
15:32:30 2010
@@ -109,6 +109,7 @@
           
reference:file:amdatu-application/org.amdatu.web.rest.wink-${platform.version}.jar
 
 
felix.auto.start.10=reference:file:amdatu-application/org.amdatu.authorization.login.gadget-${platform.version}.jar
 \
           
reference:file:amdatu-application/org.amdatu.authorization.login.service-${platform.version}.jar
 \
+          
reference:file:amdatu-application/org.amdatu.authorization.useradmin.gadget-${platform.version}.jar
 \
           
reference:file:amdatu-application/org.amdatu.authorization.useradmin.rest-${platform.version}.jar
 \
           
reference:file:amdatu-application/org.amdatu.authentication.oauth.api-${platform.version}.jar
 \
           
reference:file:amdatu-application/org.amdatu.authentication.oauth.client-${platform.version}.jar
 \

Reply via email to