Shinsuke Sugaya 9 tahun lalu
induk
melakukan
409e0eddd3
65 mengubah file dengan 4405 tambahan dan 321 penghapusan
  1. 1 0
      dbflute_fess/freegen/ControlFreeGen.vm
  2. 14 0
      dbflute_fess/freegen/elasticsearch/AbstractBehavior.vm
  3. 4 0
      dbflute_fess/freegen/elasticsearch/BsBehavior.vm
  4. 5 3
      dbflute_fess/freegen/elasticsearch/BsEntity.vm
  5. 6 1
      dbflute_fess/freegen/elasticsearch/DBMeta.vm
  6. 1 1
      pom.xml
  7. 2 2
      src/main/config/es/fess_user.json
  8. 132 0
      src/main/java/org/codelibs/fess/app/pager/GroupPager.java
  9. 132 0
      src/main/java/org/codelibs/fess/app/pager/RolePager.java
  10. 136 0
      src/main/java/org/codelibs/fess/app/pager/UserPager.java
  11. 125 0
      src/main/java/org/codelibs/fess/app/service/GroupService.java
  12. 125 0
      src/main/java/org/codelibs/fess/app/service/RoleService.java
  13. 129 0
      src/main/java/org/codelibs/fess/app/service/UserService.java
  14. 3 3
      src/main/java/org/codelibs/fess/app/web/RootForm.java
  15. 1 1
      src/main/java/org/codelibs/fess/app/web/admin/crawl/AdminCrawlAction.java
  16. 271 0
      src/main/java/org/codelibs/fess/app/web/admin/group/AdminGroupAction.java
  17. 48 0
      src/main/java/org/codelibs/fess/app/web/admin/group/GroupEditForm.java
  18. 8 14
      src/main/java/org/codelibs/fess/app/web/admin/group/GroupSearchForm.java
  19. 271 0
      src/main/java/org/codelibs/fess/app/web/admin/role/AdminRoleAction.java
  20. 48 0
      src/main/java/org/codelibs/fess/app/web/admin/role/RoleEditForm.java
  21. 31 0
      src/main/java/org/codelibs/fess/app/web/admin/role/RoleSearchForm.java
  22. 0 1
      src/main/java/org/codelibs/fess/app/web/admin/suggestelevateword/AdminSuggestelevatewordAction.java
  23. 361 0
      src/main/java/org/codelibs/fess/app/web/admin/user/AdminUserAction.java
  24. 60 0
      src/main/java/org/codelibs/fess/app/web/admin/user/UserEditForm.java
  25. 31 0
      src/main/java/org/codelibs/fess/app/web/admin/user/UserSearchForm.java
  26. 0 4
      src/main/java/org/codelibs/fess/app/web/base/FessAdminAction.java
  27. 5 1
      src/main/java/org/codelibs/fess/app/web/base/FessBaseAction.java
  28. 1 2
      src/main/java/org/codelibs/fess/app/web/base/FessSearchAction.java
  29. 9 8
      src/main/java/org/codelibs/fess/app/web/search/SearchAction.java
  30. 4 2
      src/main/java/org/codelibs/fess/app/web/search/SearchForm.java
  31. 14 0
      src/main/java/org/codelibs/fess/es/bsbhv/AbstractBehavior.java
  32. 2 2
      src/main/java/org/codelibs/fess/es/bsbhv/BsUserBhv.java
  33. 20 20
      src/main/java/org/codelibs/fess/es/bsentity/BsUser.java
  34. 12 12
      src/main/java/org/codelibs/fess/es/bsentity/dbmeta/UserDbm.java
  35. 4 4
      src/main/java/org/codelibs/fess/es/cbean/bs/BsUserCB.java
  36. 96 96
      src/main/java/org/codelibs/fess/es/cbean/cf/bs/BsUserCF.java
  37. 112 112
      src/main/java/org/codelibs/fess/es/cbean/cq/bs/BsUserCQ.java
  38. 1 2
      src/main/java/org/codelibs/fess/helper/QueryHelper.java
  39. 36 0
      src/main/java/org/codelibs/fess/mylasta/action/FessHtmlPath.java
  40. 438 12
      src/main/java/org/codelibs/fess/mylasta/action/FessLabels.java
  41. 51 0
      src/main/java/org/codelibs/fess/mylasta/action/FessMessages.java
  42. 1 8
      src/main/java/org/codelibs/fess/mylasta/direction/FessFwAssistantDirector.java
  43. 1 1
      src/main/java/org/codelibs/fess/util/QueryResponseList.java
  44. 4 0
      src/main/resources/fess_es.xml
  45. 2 0
      src/main/resources/fess_indices/.fess_user/role.bulk
  46. 2 0
      src/main/resources/fess_indices/.fess_user/user.bulk
  47. 2 2
      src/main/resources/fess_indices/.fess_user/user.json
  48. 76 1
      src/main/resources/fess_label.properties
  49. 76 1
      src/main/resources/fess_label_en.properties
  50. 76 1
      src/main/resources/fess_label_ja.properties
  51. 3 0
      src/main/resources/fess_message.properties
  52. 3 0
      src/main/resources/fess_message_en.properties
  53. 144 0
      src/main/webapp/WEB-INF/view/admin/group/confirm.jsp
  54. 117 0
      src/main/webapp/WEB-INF/view/admin/group/edit.jsp
  55. 50 0
      src/main/webapp/WEB-INF/view/admin/group/error.jsp
  56. 126 0
      src/main/webapp/WEB-INF/view/admin/group/index.jsp
  57. 144 0
      src/main/webapp/WEB-INF/view/admin/role/confirm.jsp
  58. 117 0
      src/main/webapp/WEB-INF/view/admin/role/edit.jsp
  59. 50 0
      src/main/webapp/WEB-INF/view/admin/role/error.jsp
  60. 126 0
      src/main/webapp/WEB-INF/view/admin/role/index.jsp
  61. 176 0
      src/main/webapp/WEB-INF/view/admin/user/confirm.jsp
  62. 147 0
      src/main/webapp/WEB-INF/view/admin/user/edit.jsp
  63. 50 0
      src/main/webapp/WEB-INF/view/admin/user/error.jsp
  64. 126 0
      src/main/webapp/WEB-INF/view/admin/user/index.jsp
  65. 36 4
      src/main/webapp/WEB-INF/view/common/admin2/sidebar.jsp

+ 1 - 0
dbflute_fess/freegen/ControlFreeGen.vm

@@ -44,6 +44,7 @@ $manager.makeDirectory($request.generateDirPath)
   #end
 #end
 #elseif ($request.isResourceTypeElasticsearch())
+#set ($arrayColumnNames = ["groups", "roles"])
 #if ($request.requestName.startsWith("Elasticsearch"))
   ##
   ## <<<  Elasticsearch Schema Gen  >>>

+ 14 - 0
dbflute_fess/freegen/elasticsearch/AbstractBehavior.vm

@@ -25,6 +25,7 @@ import org.dbflute.cbean.ConditionBean;
 import org.dbflute.cbean.coption.CursorSelectOption;
 import org.dbflute.cbean.result.ListResultBean;
 import org.dbflute.exception.IllegalBehaviorStateException;
+import org.dbflute.util.DfTypeUtil;
 import org.elasticsearch.action.bulk.BulkItemResponse;
 import org.elasticsearch.action.bulk.BulkRequestBuilder;
 import org.elasticsearch.action.bulk.BulkResponse;
@@ -346,6 +347,19 @@ public abstract class AbstractBehavior<ENTITY extends Entity, CB extends Conditi
         return results;
     }
 
+    public static String[] toStringArray(final Object value) {
+        if (value instanceof String[]) {
+            return (String[]) value;
+        } else if (value instanceof List) {
+            return ((List<?>) value).stream().map(v -> v.toString()).toArray(n -> new String[n]);
+        }
+        String str = DfTypeUtil.toString(value);
+        if (str == null) {
+            return null;
+        }
+        return new String[] { str };
+    }
+
     public static class BulkList<E> implements List<E> {
 
         private final List<E> parent;

+ 4 - 0
dbflute_fess/freegen/elasticsearch/BsBehavior.vm

@@ -58,8 +58,12 @@ public abstract class Bs${table.camelizedName}Bhv extends AbstractBehavior<${tab
 #foreach ($column in $table.columnList)
 #if ($column.isNormalColumn)
 #set ($javaNative = ${column.type})
+#if ($arrayColumnNames.contains($column.name))
+            result.set${column.capCamelName}(to${javaNative}Array(source.get("${column.name}")));
+#else
             result.set${column.capCamelName}(DfTypeUtil.to$javaNative(source.get("${column.name}")));
 #end
+#end
 #end
             return result;
         } catch (InstantiationException | IllegalAccessException e) {

+ 5 - 3
dbflute_fess/freegen/elasticsearch/BsEntity.vm

@@ -39,9 +39,10 @@ public class Bs${table.camelizedName} extends AbstractEntity {
 #elseif ($column.isRefColumn)
 #set ($javaNative = ${column.camelizedName})
 #end
+#if ($arrayColumnNames.contains($column.name))#set($arrayType = "[]")#else#set($arrayType = "")#end
 #if ($column.name != $idColumn)
     /** ${column.name} */
-    protected ${javaNative} ${column.uncapCamelName};
+    protected ${javaNative}${arrayType} ${column.uncapCamelName};
 
 #end
 #end
@@ -59,6 +60,7 @@ public class Bs${table.camelizedName} extends AbstractEntity {
 #elseif ($column.isRefColumn)
 #set ($javaNative = ${column.camelizedName})
 #end
+#if ($arrayColumnNames.contains($column.name))#set($arrayType = "[]")#else#set($arrayType = "")#end
 #if ($column.name != $idColumn)
 #if ($javaNative == "boolean")
     public ${javaNative} is${column.capCamelName}() {
@@ -66,13 +68,13 @@ public class Bs${table.camelizedName} extends AbstractEntity {
         return ${column.uncapCamelName};
     }
 #else
-    public ${javaNative} get${column.capCamelName}() {
+    public ${javaNative}${arrayType} get${column.capCamelName}() {
         checkSpecifiedProperty("${column.uncapCamelName}");
         return ${column.uncapCamelName};
     }
 #end
 
-    public void set${column.capCamelName}(${javaNative} value) {
+    public void set${column.capCamelName}(${javaNative}${arrayType} value) {
         registerModifiedProperty("${column.uncapCamelName}");
         this.${column.uncapCamelName} = value;
     }

+ 6 - 1
dbflute_fess/freegen/elasticsearch/DBMeta.vm

@@ -44,7 +44,11 @@ public class ${table.camelizedName}Dbm extends AbstractDBMeta {
 #elseif ($col.isRefColumn)
 #set ($javaNative = ${col.camelizedName})
 #end
+#if ($arrayColumnNames.contains($col.name))
+        setupEpg(_epgMap, et-> ((${table.camelizedName})et).get${col.camelizedName}(),(et,vl)->((${table.camelizedName}) et).set${col.camelizedName}((${javaNative}[])vl), "${col.uncapCamelName}");
+#else
         setupEpg(_epgMap, et-> ((${table.camelizedName})et).get${col.camelizedName}(),(et,vl)->((${table.camelizedName}) et).set${col.camelizedName}(DfTypeUtil.to${javaNative}(vl)), "${col.uncapCamelName}");
+#end
 #end
     }
 
@@ -62,7 +66,8 @@ public class ${table.camelizedName}Dbm extends AbstractDBMeta {
 #elseif ($col.isRefColumn)
 #set ($javaNative = ${col.camelizedName})
 #end
-    protected final ColumnInfo _column${col.camelizedName} = cci("${col.name}", "${col.name}", null, null, ${javaNative}.class, "${col.uncapCamelName}", null, false, false, false, "${col.type}", 0, 0, null, false, null, null, null, null, null, false);
+#if ($arrayColumnNames.contains($col.name))#set($arrayType = "[]")#else#set($arrayType = "")#end
+    protected final ColumnInfo _column${col.camelizedName} = cci("${col.name}", "${col.name}", null, null, ${javaNative}${arrayType}.class, "${col.uncapCamelName}", null, false, false, false, "${col.type}", 0, 0, null, false, null, null, null, null, null, false);
 #end
 
 #foreach ($col in $table.columnList)

+ 1 - 1
pom.xml

@@ -39,7 +39,7 @@
 
 		<!-- Main Framework -->
 		<dbflute.version>1.1.0-sp8</dbflute.version>
-		<lastaflute.version>0.6.1</lastaflute.version>
+		<lastaflute.version>0.6.2-A-SNAPSHOT</lastaflute.version>
 		<lasta.taglib.version>0.6.0</lasta.taglib.version>
 		<servlet.version>3.1.0</servlet.version>
 		<jsp.version>2.3.1</jsp.version>

+ 2 - 2
src/main/config/es/fess_user.json

@@ -27,7 +27,7 @@
           "path" : "id"
         },
         "properties" : {
-          "group" : {
+          "groups" : {
             "type" : "string",
             "index" : "not_analyzed"
           },
@@ -43,7 +43,7 @@
             "type" : "string",
             "index" : "not_analyzed"
           },
-          "role" : {
+          "roles" : {
             "type" : "string",
             "index" : "not_analyzed"
           }

+ 132 - 0
src/main/java/org/codelibs/fess/app/pager/GroupPager.java

@@ -0,0 +1,132 @@
+/*
+ * Copyright 2009-2015 the CodeLibs Project and the Others.
+ *
+ * Licensed 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.
+ */
+
+package org.codelibs.fess.app.pager;
+
+import java.io.Serializable;
+import java.util.List;
+
+import org.codelibs.fess.Constants;
+
+public class GroupPager implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final int DEFAULT_PAGE_SIZE = 20;
+
+    public static final int DEFAULT_CURRENT_PAGE_NUMBER = 1;
+
+    private int allRecordCount;
+
+    private int allPageCount;
+
+    private boolean existPrePage;
+
+    private boolean existNextPage;
+
+    private List<Integer> pageNumberList;
+
+    private int pageSize;
+
+    private int currentPageNumber;
+
+    public String id;
+
+    public String name;
+
+    public String versionNo;
+
+    public void clear() {
+        pageSize = getDefaultPageSize();
+        currentPageNumber = getDefaultCurrentPageNumber();
+
+        id = null;
+        name = null;
+        versionNo = null;
+
+    }
+
+    protected int getDefaultCurrentPageNumber() {
+        return DEFAULT_CURRENT_PAGE_NUMBER;
+    }
+
+    public int getAllRecordCount() {
+        return allRecordCount;
+    }
+
+    public void setAllRecordCount(final int allRecordCount) {
+        this.allRecordCount = allRecordCount;
+    }
+
+    public int getAllPageCount() {
+        return allPageCount;
+    }
+
+    public void setAllPageCount(final int allPageCount) {
+        this.allPageCount = allPageCount;
+    }
+
+    public boolean isExistPrePage() {
+        return existPrePage;
+    }
+
+    public void setExistPrePage(final boolean existPrePage) {
+        this.existPrePage = existPrePage;
+    }
+
+    public boolean isExistNextPage() {
+        return existNextPage;
+    }
+
+    public void setExistNextPage(final boolean existNextPage) {
+        this.existNextPage = existNextPage;
+    }
+
+    public int getPageSize() {
+        if (pageSize <= 0) {
+            pageSize = getDefaultPageSize();
+        }
+        return pageSize;
+    }
+
+    public void setPageSize(final int pageSize) {
+        this.pageSize = pageSize;
+    }
+
+    public int getCurrentPageNumber() {
+        if (currentPageNumber <= 0) {
+            currentPageNumber = getDefaultCurrentPageNumber();
+        }
+        return currentPageNumber;
+    }
+
+    public void setCurrentPageNumber(final int currentPageNumber) {
+        this.currentPageNumber = currentPageNumber;
+    }
+
+    public List<Integer> getPageNumberList() {
+        return pageNumberList;
+    }
+
+    public void setPageNumberList(final List<Integer> pageNumberList) {
+        this.pageNumberList = pageNumberList;
+    }
+
+    protected int getDefaultPageSize() {
+        return Constants.DEFAULT_ADMIN_PAGE_SIZE;
+    }
+
+}

+ 132 - 0
src/main/java/org/codelibs/fess/app/pager/RolePager.java

@@ -0,0 +1,132 @@
+/*
+ * Copyright 2009-2015 the CodeLibs Project and the Others.
+ *
+ * Licensed 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.
+ */
+
+package org.codelibs.fess.app.pager;
+
+import java.io.Serializable;
+import java.util.List;
+
+import org.codelibs.fess.Constants;
+
+public class RolePager implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final int DEFAULT_PAGE_SIZE = 20;
+
+    public static final int DEFAULT_CURRENT_PAGE_NUMBER = 1;
+
+    private int allRecordCount;
+
+    private int allPageCount;
+
+    private boolean existPrePage;
+
+    private boolean existNextPage;
+
+    private List<Integer> pageNumberList;
+
+    private int pageSize;
+
+    private int currentPageNumber;
+
+    public String id;
+
+    public String name;
+
+    public String versionNo;
+
+    public void clear() {
+        pageSize = getDefaultPageSize();
+        currentPageNumber = getDefaultCurrentPageNumber();
+
+        id = null;
+        name = null;
+        versionNo = null;
+
+    }
+
+    protected int getDefaultCurrentPageNumber() {
+        return DEFAULT_CURRENT_PAGE_NUMBER;
+    }
+
+    public int getAllRecordCount() {
+        return allRecordCount;
+    }
+
+    public void setAllRecordCount(final int allRecordCount) {
+        this.allRecordCount = allRecordCount;
+    }
+
+    public int getAllPageCount() {
+        return allPageCount;
+    }
+
+    public void setAllPageCount(final int allPageCount) {
+        this.allPageCount = allPageCount;
+    }
+
+    public boolean isExistPrePage() {
+        return existPrePage;
+    }
+
+    public void setExistPrePage(final boolean existPrePage) {
+        this.existPrePage = existPrePage;
+    }
+
+    public boolean isExistNextPage() {
+        return existNextPage;
+    }
+
+    public void setExistNextPage(final boolean existNextPage) {
+        this.existNextPage = existNextPage;
+    }
+
+    public int getPageSize() {
+        if (pageSize <= 0) {
+            pageSize = getDefaultPageSize();
+        }
+        return pageSize;
+    }
+
+    public void setPageSize(final int pageSize) {
+        this.pageSize = pageSize;
+    }
+
+    public int getCurrentPageNumber() {
+        if (currentPageNumber <= 0) {
+            currentPageNumber = getDefaultCurrentPageNumber();
+        }
+        return currentPageNumber;
+    }
+
+    public void setCurrentPageNumber(final int currentPageNumber) {
+        this.currentPageNumber = currentPageNumber;
+    }
+
+    public List<Integer> getPageNumberList() {
+        return pageNumberList;
+    }
+
+    public void setPageNumberList(final List<Integer> pageNumberList) {
+        this.pageNumberList = pageNumberList;
+    }
+
+    protected int getDefaultPageSize() {
+        return Constants.DEFAULT_ADMIN_PAGE_SIZE;
+    }
+
+}

+ 136 - 0
src/main/java/org/codelibs/fess/app/pager/UserPager.java

@@ -0,0 +1,136 @@
+/*
+ * Copyright 2009-2015 the CodeLibs Project and the Others.
+ *
+ * Licensed 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.
+ */
+
+package org.codelibs.fess.app.pager;
+
+import java.io.Serializable;
+import java.util.List;
+
+import org.codelibs.fess.Constants;
+
+public class UserPager implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final int DEFAULT_PAGE_SIZE = 20;
+
+    public static final int DEFAULT_CURRENT_PAGE_NUMBER = 1;
+
+    private int allRecordCount;
+
+    private int allPageCount;
+
+    private boolean existPrePage;
+
+    private boolean existNextPage;
+
+    private List<Integer> pageNumberList;
+
+    private int pageSize;
+
+    private int currentPageNumber;
+
+    public String id;
+
+    public String name;
+
+    public String[] roles;
+    public String[] groups;
+
+    public String versionNo;
+
+    public void clear() {
+        pageSize = getDefaultPageSize();
+        currentPageNumber = getDefaultCurrentPageNumber();
+
+        id = null;
+        roles = null;
+        groups = null;
+        versionNo = null;
+
+    }
+
+    protected int getDefaultCurrentPageNumber() {
+        return DEFAULT_CURRENT_PAGE_NUMBER;
+    }
+
+    public int getAllRecordCount() {
+        return allRecordCount;
+    }
+
+    public void setAllRecordCount(final int allRecordCount) {
+        this.allRecordCount = allRecordCount;
+    }
+
+    public int getAllPageCount() {
+        return allPageCount;
+    }
+
+    public void setAllPageCount(final int allPageCount) {
+        this.allPageCount = allPageCount;
+    }
+
+    public boolean isExistPrePage() {
+        return existPrePage;
+    }
+
+    public void setExistPrePage(final boolean existPrePage) {
+        this.existPrePage = existPrePage;
+    }
+
+    public boolean isExistNextPage() {
+        return existNextPage;
+    }
+
+    public void setExistNextPage(final boolean existNextPage) {
+        this.existNextPage = existNextPage;
+    }
+
+    public int getPageSize() {
+        if (pageSize <= 0) {
+            pageSize = getDefaultPageSize();
+        }
+        return pageSize;
+    }
+
+    public void setPageSize(final int pageSize) {
+        this.pageSize = pageSize;
+    }
+
+    public int getCurrentPageNumber() {
+        if (currentPageNumber <= 0) {
+            currentPageNumber = getDefaultCurrentPageNumber();
+        }
+        return currentPageNumber;
+    }
+
+    public void setCurrentPageNumber(final int currentPageNumber) {
+        this.currentPageNumber = currentPageNumber;
+    }
+
+    public List<Integer> getPageNumberList() {
+        return pageNumberList;
+    }
+
+    public void setPageNumberList(final List<Integer> pageNumberList) {
+        this.pageNumberList = pageNumberList;
+    }
+
+    protected int getDefaultPageSize() {
+        return Constants.DEFAULT_ADMIN_PAGE_SIZE;
+    }
+
+}

+ 125 - 0
src/main/java/org/codelibs/fess/app/service/GroupService.java

@@ -0,0 +1,125 @@
+/*
+ * Copyright 2009-2015 the CodeLibs Project and the Others.
+ *
+ * Licensed 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.
+ */
+
+package org.codelibs.fess.app.service;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Resource;
+
+import org.codelibs.core.beans.util.BeanUtil;
+import org.codelibs.fess.app.pager.GroupPager;
+import org.codelibs.fess.crud.CommonConstants;
+import org.codelibs.fess.crud.CrudMessageException;
+import org.codelibs.fess.es.cbean.GroupCB;
+import org.codelibs.fess.es.exbhv.GroupBhv;
+import org.codelibs.fess.es.exentity.Group;
+import org.dbflute.cbean.result.PagingResultBean;
+
+public class GroupService implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Resource
+    protected GroupBhv groupBhv;
+
+    public List<Group> getGroupList(final GroupPager groupPager) {
+
+        final PagingResultBean<Group> groupList = groupBhv.selectPage(cb -> {
+            cb.paging(groupPager.getPageSize(), groupPager.getCurrentPageNumber());
+            setupListCondition(cb, groupPager);
+        });
+
+        // update pager
+        BeanUtil.copyBeanToBean(groupList, groupPager, option -> option.include(CommonConstants.PAGER_CONVERSION_RULE));
+        groupPager.setPageNumberList(groupList.pageRange(op -> {
+            op.rangeSize(5);
+        }).createPageNumberList());
+
+        return groupList;
+    }
+
+    public Group getGroup(final Map<String, String> keys) {
+        final Group group = groupBhv.selectEntity(cb -> {
+            cb.query().docMeta().setId_Equal(keys.get("id"));
+            setupEntityCondition(cb, keys);
+        }).orElse(null);//TODO
+        if (group == null) {
+            // TODO exception?
+            return null;
+        }
+
+        return group;
+    }
+
+    public void store(final Group group) throws CrudMessageException {
+        setupStoreCondition(group);
+
+        groupBhv.insertOrUpdate(group, op -> {
+            op.setRefresh(true);
+        });
+
+    }
+
+    public void delete(final Group group) throws CrudMessageException {
+        setupDeleteCondition(group);
+
+        groupBhv.delete(group, op -> {
+            op.setRefresh(true);
+        });
+
+    }
+
+    protected void setupListCondition(final GroupCB cb, final GroupPager groupPager) {
+        if (groupPager.id != null) {
+            cb.query().docMeta().setId_Equal(groupPager.id);
+        }
+        // TODO Long, Integer, String supported only.
+
+        // setup condition
+        cb.query().addOrderBy_Name_Asc();
+
+        // search
+
+    }
+
+    protected void setupEntityCondition(final GroupCB cb, final Map<String, String> keys) {
+
+        // setup condition
+
+    }
+
+    protected void setupStoreCondition(final Group group) {
+
+        // setup condition
+
+    }
+
+    protected void setupDeleteCondition(final Group group) {
+
+        // setup condition
+
+    }
+
+    public List<Group> getAvailableGroupList() {
+        return groupBhv.selectList(cb -> {
+            cb.query().matchAll();
+        });
+    }
+
+}

+ 125 - 0
src/main/java/org/codelibs/fess/app/service/RoleService.java

@@ -0,0 +1,125 @@
+/*
+ * Copyright 2009-2015 the CodeLibs Project and the Others.
+ *
+ * Licensed 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.
+ */
+
+package org.codelibs.fess.app.service;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Resource;
+
+import org.codelibs.core.beans.util.BeanUtil;
+import org.codelibs.fess.app.pager.RolePager;
+import org.codelibs.fess.crud.CommonConstants;
+import org.codelibs.fess.crud.CrudMessageException;
+import org.codelibs.fess.es.cbean.RoleCB;
+import org.codelibs.fess.es.exbhv.RoleBhv;
+import org.codelibs.fess.es.exentity.Role;
+import org.dbflute.cbean.result.PagingResultBean;
+
+public class RoleService implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Resource
+    protected RoleBhv roleBhv;
+
+    public List<Role> getRoleList(final RolePager rolePager) {
+
+        final PagingResultBean<Role> roleList = roleBhv.selectPage(cb -> {
+            cb.paging(rolePager.getPageSize(), rolePager.getCurrentPageNumber());
+            setupListCondition(cb, rolePager);
+        });
+
+        // update pager
+        BeanUtil.copyBeanToBean(roleList, rolePager, option -> option.include(CommonConstants.PAGER_CONVERSION_RULE));
+        rolePager.setPageNumberList(roleList.pageRange(op -> {
+            op.rangeSize(5);
+        }).createPageNumberList());
+
+        return roleList;
+    }
+
+    public Role getRole(final Map<String, String> keys) {
+        final Role role = roleBhv.selectEntity(cb -> {
+            cb.query().docMeta().setId_Equal(keys.get("id"));
+            setupEntityCondition(cb, keys);
+        }).orElse(null);//TODO
+        if (role == null) {
+            // TODO exception?
+            return null;
+        }
+
+        return role;
+    }
+
+    public void store(final Role role) throws CrudMessageException {
+        setupStoreCondition(role);
+
+        roleBhv.insertOrUpdate(role, op -> {
+            op.setRefresh(true);
+        });
+
+    }
+
+    public void delete(final Role role) throws CrudMessageException {
+        setupDeleteCondition(role);
+
+        roleBhv.delete(role, op -> {
+            op.setRefresh(true);
+        });
+
+    }
+
+    protected void setupListCondition(final RoleCB cb, final RolePager rolePager) {
+        if (rolePager.id != null) {
+            cb.query().docMeta().setId_Equal(rolePager.id);
+        }
+        // TODO Long, Integer, String supported only.
+
+        // setup condition
+        cb.query().addOrderBy_Name_Asc();
+
+        // search
+
+    }
+
+    protected void setupEntityCondition(final RoleCB cb, final Map<String, String> keys) {
+
+        // setup condition
+
+    }
+
+    protected void setupStoreCondition(final Role role) {
+
+        // setup condition
+
+    }
+
+    protected void setupDeleteCondition(final Role role) {
+
+        // setup condition
+
+    }
+
+    public List<Role> getAvailableRoleList() {
+        return roleBhv.selectList(cb -> {
+            cb.query().matchAll();
+        });
+    }
+
+}

+ 129 - 0
src/main/java/org/codelibs/fess/app/service/UserService.java

@@ -0,0 +1,129 @@
+/*
+ * Copyright 2009-2015 the CodeLibs Project and the Others.
+ *
+ * Licensed 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.
+ */
+
+package org.codelibs.fess.app.service;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Resource;
+
+import org.codelibs.core.beans.util.BeanUtil;
+import org.codelibs.fess.app.pager.UserPager;
+import org.codelibs.fess.crud.CommonConstants;
+import org.codelibs.fess.crud.CrudMessageException;
+import org.codelibs.fess.es.cbean.UserCB;
+import org.codelibs.fess.es.exbhv.UserBhv;
+import org.codelibs.fess.es.exentity.User;
+import org.dbflute.cbean.result.PagingResultBean;
+
+public class UserService implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Resource
+    protected UserBhv userBhv;
+
+    public UserService() {
+        super();
+    }
+
+    public List<User> getUserList(final UserPager userPager) {
+
+        final PagingResultBean<User> userList = userBhv.selectPage(cb -> {
+            cb.paging(userPager.getPageSize(), userPager.getCurrentPageNumber());
+            setupListCondition(cb, userPager);
+        });
+
+        // update pager
+        BeanUtil.copyBeanToBean(userList, userPager, option -> option.include(CommonConstants.PAGER_CONVERSION_RULE));
+        userPager.setPageNumberList(userList.pageRange(op -> {
+            op.rangeSize(5);
+        }).createPageNumberList());
+
+        return userList;
+    }
+
+    public User getUser(final Map<String, String> keys) {
+        final User user = userBhv.selectEntity(cb -> {
+            cb.query().docMeta().setId_Equal(keys.get("id"));
+            setupEntityCondition(cb, keys);
+        }).orElse(null);//TODO
+        if (user == null) {
+            // TODO exception?
+            return null;
+        }
+
+        return user;
+    }
+
+    public void store(final User user) throws CrudMessageException {
+        setupStoreCondition(user);
+
+        userBhv.insertOrUpdate(user, op -> {
+            op.setRefresh(true);
+        });
+
+    }
+
+    public void delete(final User user) throws CrudMessageException {
+        setupDeleteCondition(user);
+
+        userBhv.delete(user, op -> {
+            op.setRefresh(true);
+        });
+
+    }
+
+    protected void setupListCondition(final UserCB cb, final UserPager userPager) {
+        if (userPager.id != null) {
+            cb.query().docMeta().setId_Equal(userPager.id);
+        }
+        // TODO Long, Integer, String supported only.
+
+        // setup condition
+        cb.query().addOrderBy_Name_Asc();
+
+        // search
+
+    }
+
+    protected void setupEntityCondition(final UserCB cb, final Map<String, String> keys) {
+
+        // setup condition
+
+    }
+
+    protected void setupStoreCondition(final User user) {
+
+        // setup condition
+
+    }
+
+    protected void setupDeleteCondition(final User user) {
+
+        // setup condition
+
+    }
+
+    public List<User> getAvailableUserList() {
+        return userBhv.selectList(cb -> {
+            cb.query().matchAll();
+        });
+    }
+
+}

+ 3 - 3
src/main/java/org/codelibs/fess/app/web/RootForm.java

@@ -17,14 +17,14 @@
 package org.codelibs.fess.app.web;
 
 import java.io.Serializable;
-
-import org.codelibs.fess.util.SearchParamMap;
+import java.util.HashMap;
+import java.util.Map;
 
 public class RootForm implements Serializable {
 
     private static final long serialVersionUID = 1L;
 
-    public SearchParamMap fields = new SearchParamMap();
+    public Map<String, String[]> fields = new HashMap<>();
 
     //@Maxbytelength(maxbytelength = 1000)
     public String query;

+ 1 - 1
src/main/java/org/codelibs/fess/app/web/admin/crawl/AdminCrawlAction.java

@@ -68,7 +68,7 @@ public class AdminCrawlAction extends FessAdminAction {
 
     // ===================================================================================
 
-    protected void updateForm(CrawlEditForm form) {
+    protected void updateForm(final CrawlEditForm form) {
         form.diffCrawling = crawlerProperties.getProperty(Constants.DIFF_CRAWLING_PROPERTY, Constants.TRUE);
         form.useAclAsRole = crawlerProperties.getProperty(Constants.USE_ACL_AS_ROLE, Constants.FALSE);
         form.dayForCleanup = crawlerProperties.getProperty(Constants.DAY_FOR_CLEANUP_PROPERTY, "1");

+ 271 - 0
src/main/java/org/codelibs/fess/app/web/admin/group/AdminGroupAction.java

@@ -0,0 +1,271 @@
+/*
+ * Copyright 2009-2015 the CodeLibs Project and the Others.
+ *
+ * Licensed 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.
+ */
+
+package org.codelibs.fess.app.web.admin.group;
+
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.annotation.Resource;
+
+import org.codelibs.fess.Constants;
+import org.codelibs.fess.annotation.Token;
+import org.codelibs.fess.app.pager.GroupPager;
+import org.codelibs.fess.app.service.GroupService;
+import org.codelibs.fess.app.web.base.FessAdminAction;
+import org.codelibs.fess.crud.CommonConstants;
+import org.codelibs.fess.es.exentity.Group;
+import org.codelibs.fess.helper.SystemHelper;
+import org.lastaflute.web.Execute;
+import org.lastaflute.web.callback.ActionRuntime;
+import org.lastaflute.web.response.HtmlResponse;
+import org.lastaflute.web.response.render.RenderData;
+import org.lastaflute.web.validation.VaErrorHook;
+
+/**
+ * @author shinsuke
+ */
+public class AdminGroupAction extends FessAdminAction {
+
+    // ===================================================================================
+    //                                                                           Attribute
+    //                                                                           =========
+    @Resource
+    private GroupService groupService;
+    @Resource
+    private GroupPager groupPager;
+    @Resource
+    private SystemHelper systemHelper;
+
+    // ===================================================================================
+    //                                                                               Hook
+    //                                                                              ======
+    @Override
+    protected void setupHtmlData(final ActionRuntime runtime) {
+        super.setupHtmlData(runtime);
+        runtime.registerData("helpLink", systemHelper.getHelpLink("group"));
+    }
+
+    // ===================================================================================
+    //                                                                      Search Execute
+    //                                                                      ==============
+    @Execute
+    public HtmlResponse index(final GroupSearchForm form) {
+        return asHtml(path_AdminGroup_IndexJsp).renderWith(data -> {
+            searchPaging(data, form);
+        });
+    }
+
+    @Execute
+    public HtmlResponse list(final Integer pageNumber, final GroupSearchForm form) {
+        groupPager.setCurrentPageNumber(pageNumber);
+        return asHtml(path_AdminGroup_IndexJsp).renderWith(data -> {
+            searchPaging(data, form);
+        });
+    }
+
+    @Execute
+    public HtmlResponse search(final GroupSearchForm form) {
+        copyBeanToBean(form.searchParams, groupPager, op -> op.exclude(CommonConstants.PAGER_CONVERSION_RULE));
+        return asHtml(path_AdminGroup_IndexJsp).renderWith(data -> {
+            searchPaging(data, form);
+        });
+    }
+
+    @Execute
+    public HtmlResponse reset(final GroupSearchForm form) {
+        groupPager.clear();
+        return asHtml(path_AdminGroup_IndexJsp).renderWith(data -> {
+            searchPaging(data, form);
+        });
+    }
+
+    @Execute
+    public HtmlResponse back(final GroupSearchForm form) {
+        return asHtml(path_AdminGroup_IndexJsp).renderWith(data -> {
+            searchPaging(data, form);
+        });
+    }
+
+    protected void searchPaging(final RenderData data, final GroupSearchForm form) {
+        data.register("groupItems", groupService.getGroupList(groupPager)); // page navi
+
+        // restore from pager
+        copyBeanToBean(groupPager, form.searchParams, op -> op.exclude(CommonConstants.PAGER_CONVERSION_RULE));
+    }
+
+    // ===================================================================================
+    //                                                                        Edit Execute
+    //                                                                        ============
+    // -----------------------------------------------------
+    //                                            Entry Page
+    //                                            ----------
+    @Token(save = true, validate = false)
+    @Execute
+    public HtmlResponse createpage(final GroupEditForm form) {
+        form.initialize();
+        form.crudMode = CommonConstants.CREATE_MODE;
+        return asHtml(path_AdminGroup_EditJsp);
+    }
+
+    @Token(save = true, validate = false)
+    @Execute
+    public HtmlResponse editpage(final int crudMode, final String id, final GroupEditForm form) {
+        form.crudMode = crudMode;
+        form.id = id;
+        verifyCrudMode(form, CommonConstants.EDIT_MODE);
+        loadGroup(form);
+        return asHtml(path_AdminGroup_EditJsp);
+    }
+
+    @Token(save = true, validate = false)
+    @Execute
+    public HtmlResponse editagain(final GroupEditForm form) {
+        return asHtml(path_AdminGroup_EditJsp);
+    }
+
+    @Token(save = true, validate = false)
+    @Execute
+    public HtmlResponse editfromconfirm(final GroupEditForm form) {
+        form.crudMode = CommonConstants.EDIT_MODE;
+        loadGroup(form);
+        return asHtml(path_AdminGroup_EditJsp);
+    }
+
+    @Token(save = true, validate = false)
+    @Execute
+    public HtmlResponse deletepage(final int crudMode, final String id, final GroupEditForm form) {
+        form.crudMode = crudMode;
+        form.id = id;
+        verifyCrudMode(form, CommonConstants.DELETE_MODE);
+        loadGroup(form);
+        return asHtml(path_AdminGroup_ConfirmJsp);
+    }
+
+    @Token(save = true, validate = false)
+    @Execute
+    public HtmlResponse deletefromconfirm(final GroupEditForm form) {
+        form.crudMode = CommonConstants.DELETE_MODE;
+        loadGroup(form);
+        return asHtml(path_AdminGroup_ConfirmJsp);
+    }
+
+    // -----------------------------------------------------
+    //                                               Confirm
+    //                                               -------
+    @Execute
+    public HtmlResponse confirmpage(final int crudMode, final String id, final GroupEditForm form) {
+        form.crudMode = crudMode;
+        form.id = id;
+        verifyCrudMode(form, CommonConstants.CONFIRM_MODE);
+        loadGroup(form);
+        return asHtml(path_AdminGroup_ConfirmJsp);
+    }
+
+    @Token(save = false, validate = true, keep = true)
+    @Execute
+    public HtmlResponse confirmfromcreate(final GroupEditForm form) {
+        validate(form, messages -> {}, toEditHtml());
+        return asHtml(path_AdminGroup_ConfirmJsp);
+    }
+
+    @Token(save = false, validate = true, keep = true)
+    @Execute
+    public HtmlResponse confirmfromupdate(final GroupEditForm form) {
+        validate(form, messages -> {}, toEditHtml());
+        return asHtml(path_AdminGroup_ConfirmJsp);
+    }
+
+    // -----------------------------------------------------
+    //                                         Actually Crud
+    //                                         -------------
+    @Token(save = false, validate = true)
+    @Execute
+    public HtmlResponse create(final GroupEditForm form) {
+        validate(form, messages -> {}, toEditHtml());
+        groupService.store(createGroup(form));
+        saveInfo(messages -> messages.addSuccessCrudCreateCrudTable(GLOBAL));
+        return redirect(getClass());
+    }
+
+    @Token(save = false, validate = true)
+    @Execute
+    public HtmlResponse update(final GroupEditForm form) {
+        validate(form, messages -> {}, toEditHtml());
+        groupService.store(createGroup(form));
+        saveInfo(messages -> messages.addSuccessCrudUpdateCrudTable(GLOBAL));
+        return redirect(getClass());
+    }
+
+    @Execute
+    public HtmlResponse delete(final GroupEditForm form) {
+        verifyCrudMode(form, CommonConstants.DELETE_MODE);
+        groupService.delete(getGroup(form));
+        saveInfo(messages -> messages.addSuccessCrudDeleteCrudTable(GLOBAL));
+        return redirect(getClass());
+    }
+
+    // ===================================================================================
+    //                                                                        Assist Logic
+    //                                                                        ============
+    protected void loadGroup(final GroupEditForm form) {
+        copyBeanToBean(getGroup(form), form, op -> op.exclude("crudMode"));
+    }
+
+    protected Group getGroup(final GroupEditForm form) {
+        final Group group = groupService.getGroup(createKeyMap(form));
+        if (group == null) {
+            throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, form.id), toEditHtml());
+        }
+        return group;
+    }
+
+    protected Group createGroup(final GroupEditForm form) {
+        Group group;
+        if (form.crudMode == CommonConstants.EDIT_MODE) {
+            group = getGroup(form);
+        } else {
+            group = new Group();
+        }
+        copyBeanToBean(form, group, op -> op.exclude(CommonConstants.COMMON_CONVERSION_RULE));
+        group.setId(Base64.getEncoder().encodeToString(group.getName().getBytes(Constants.CHARSET_UTF_8)));
+        return group;
+    }
+
+    protected Map<String, String> createKeyMap(final GroupEditForm form) {
+        final Map<String, String> keys = new HashMap<String, String>();
+        keys.put("id", form.id);
+        return keys;
+    }
+
+    // ===================================================================================
+    //                                                                        Small Helper
+    //                                                                        ============
+    protected void verifyCrudMode(final GroupEditForm form, final int expectedMode) {
+        if (form.crudMode != expectedMode) {
+            throwValidationError(messages -> {
+                messages.addErrorsCrudInvalidMode(GLOBAL, String.valueOf(expectedMode), String.valueOf(form.crudMode));
+            }, toEditHtml());
+        }
+    }
+
+    protected VaErrorHook toEditHtml() {
+        return () -> {
+            return asHtml(path_AdminGroup_EditJsp);
+        };
+    }
+}

+ 48 - 0
src/main/java/org/codelibs/fess/app/web/admin/group/GroupEditForm.java

@@ -0,0 +1,48 @@
+/*
+ * Copyright 2009-2015 the CodeLibs Project and the Others.
+ *
+ * Licensed 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.
+ */
+
+package org.codelibs.fess.app.web.admin.group;
+
+import java.io.Serializable;
+
+/**
+ * @author shinsuke
+ */
+public class GroupEditForm implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    //@IntegerType
+    public int crudMode;
+
+    //@Required(target = "confirmfromupdate,update,delete")
+    //@Maxbytelength(maxbytelength = 1000)
+    public String id;
+
+    //@Required(target = "confirmfromcreate,create,confirmfromupdate,update,delete")
+    //@Maxbytelength(maxbytelength = 100)
+    public String name;
+
+    //@Required(target = "confirmfromupdate,update,delete")
+    //@IntegerType
+    public String versionNo;
+
+    public void initialize() {
+        id = null;
+        name = null;
+        versionNo = null;
+    }
+}

+ 8 - 14
src/main/java/org/codelibs/fess/util/SearchParamMap.java → src/main/java/org/codelibs/fess/app/web/admin/group/GroupSearchForm.java

@@ -14,24 +14,18 @@
  * governing permissions and limitations under the License.
  */
 
-package org.codelibs.fess.util;
+package org.codelibs.fess.app.web.admin.group;
 
+import java.io.Serializable;
 import java.util.HashMap;
+import java.util.Map;
 
-import org.codelibs.core.lang.StringUtil;
-
-public class SearchParamMap extends HashMap<String, String[]> {
+/**
+ * @author shinsuke
+ */
+public class GroupSearchForm implements Serializable {
 
     private static final long serialVersionUID = 1L;
 
-    public static final String CLASS_NAME = "org.codelibs.fess.util.SearchParamMap";
-
-    @Override
-    public String[] get(final Object key) {
-        if (CLASS_NAME.equals(key)) {
-            return StringUtil.EMPTY_STRINGS;
-        }
-        return super.get(key);
-    }
-
+    public Map<String, String> searchParams = new HashMap<String, String>();
 }

+ 271 - 0
src/main/java/org/codelibs/fess/app/web/admin/role/AdminRoleAction.java

@@ -0,0 +1,271 @@
+/*
+ * Copyright 2009-2015 the CodeLibs Project and the Others.
+ *
+ * Licensed 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.
+ */
+
+package org.codelibs.fess.app.web.admin.role;
+
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.annotation.Resource;
+
+import org.codelibs.fess.Constants;
+import org.codelibs.fess.annotation.Token;
+import org.codelibs.fess.app.pager.RolePager;
+import org.codelibs.fess.app.service.RoleService;
+import org.codelibs.fess.app.web.base.FessAdminAction;
+import org.codelibs.fess.crud.CommonConstants;
+import org.codelibs.fess.es.exentity.Role;
+import org.codelibs.fess.helper.SystemHelper;
+import org.lastaflute.web.Execute;
+import org.lastaflute.web.callback.ActionRuntime;
+import org.lastaflute.web.response.HtmlResponse;
+import org.lastaflute.web.response.render.RenderData;
+import org.lastaflute.web.validation.VaErrorHook;
+
+/**
+ * @author shinsuke
+ */
+public class AdminRoleAction extends FessAdminAction {
+
+    // ===================================================================================
+    //                                                                           Attribute
+    //                                                                           =========
+    @Resource
+    private RoleService roleService;
+    @Resource
+    private RolePager rolePager;
+    @Resource
+    private SystemHelper systemHelper;
+
+    // ===================================================================================
+    //                                                                               Hook
+    //                                                                              ======
+    @Override
+    protected void setupHtmlData(final ActionRuntime runtime) {
+        super.setupHtmlData(runtime);
+        runtime.registerData("helpLink", systemHelper.getHelpLink("role"));
+    }
+
+    // ===================================================================================
+    //                                                                      Search Execute
+    //                                                                      ==============
+    @Execute
+    public HtmlResponse index(final RoleSearchForm form) {
+        return asHtml(path_AdminRole_IndexJsp).renderWith(data -> {
+            searchPaging(data, form);
+        });
+    }
+
+    @Execute
+    public HtmlResponse list(final Integer pageNumber, final RoleSearchForm form) {
+        rolePager.setCurrentPageNumber(pageNumber);
+        return asHtml(path_AdminRole_IndexJsp).renderWith(data -> {
+            searchPaging(data, form);
+        });
+    }
+
+    @Execute
+    public HtmlResponse search(final RoleSearchForm form) {
+        copyBeanToBean(form.searchParams, rolePager, op -> op.exclude(CommonConstants.PAGER_CONVERSION_RULE));
+        return asHtml(path_AdminRole_IndexJsp).renderWith(data -> {
+            searchPaging(data, form);
+        });
+    }
+
+    @Execute
+    public HtmlResponse reset(final RoleSearchForm form) {
+        rolePager.clear();
+        return asHtml(path_AdminRole_IndexJsp).renderWith(data -> {
+            searchPaging(data, form);
+        });
+    }
+
+    @Execute
+    public HtmlResponse back(final RoleSearchForm form) {
+        return asHtml(path_AdminRole_IndexJsp).renderWith(data -> {
+            searchPaging(data, form);
+        });
+    }
+
+    protected void searchPaging(final RenderData data, final RoleSearchForm form) {
+        data.register("roleItems", roleService.getRoleList(rolePager)); // page navi
+
+        // restore from pager
+        copyBeanToBean(rolePager, form.searchParams, op -> op.exclude(CommonConstants.PAGER_CONVERSION_RULE));
+    }
+
+    // ===================================================================================
+    //                                                                        Edit Execute
+    //                                                                        ============
+    // -----------------------------------------------------
+    //                                            Entry Page
+    //                                            ----------
+    @Token(save = true, validate = false)
+    @Execute
+    public HtmlResponse createpage(final RoleEditForm form) {
+        form.initialize();
+        form.crudMode = CommonConstants.CREATE_MODE;
+        return asHtml(path_AdminRole_EditJsp);
+    }
+
+    @Token(save = true, validate = false)
+    @Execute
+    public HtmlResponse editpage(final int crudMode, final String id, final RoleEditForm form) {
+        form.crudMode = crudMode;
+        form.id = id;
+        verifyCrudMode(form, CommonConstants.EDIT_MODE);
+        loadRole(form);
+        return asHtml(path_AdminRole_EditJsp);
+    }
+
+    @Token(save = true, validate = false)
+    @Execute
+    public HtmlResponse editagain(final RoleEditForm form) {
+        return asHtml(path_AdminRole_EditJsp);
+    }
+
+    @Token(save = true, validate = false)
+    @Execute
+    public HtmlResponse editfromconfirm(final RoleEditForm form) {
+        form.crudMode = CommonConstants.EDIT_MODE;
+        loadRole(form);
+        return asHtml(path_AdminRole_EditJsp);
+    }
+
+    @Token(save = true, validate = false)
+    @Execute
+    public HtmlResponse deletepage(final int crudMode, final String id, final RoleEditForm form) {
+        form.crudMode = crudMode;
+        form.id = id;
+        verifyCrudMode(form, CommonConstants.DELETE_MODE);
+        loadRole(form);
+        return asHtml(path_AdminRole_ConfirmJsp);
+    }
+
+    @Token(save = true, validate = false)
+    @Execute
+    public HtmlResponse deletefromconfirm(final RoleEditForm form) {
+        form.crudMode = CommonConstants.DELETE_MODE;
+        loadRole(form);
+        return asHtml(path_AdminRole_ConfirmJsp);
+    }
+
+    // -----------------------------------------------------
+    //                                               Confirm
+    //                                               -------
+    @Execute
+    public HtmlResponse confirmpage(final int crudMode, final String id, final RoleEditForm form) {
+        form.crudMode = crudMode;
+        form.id = id;
+        verifyCrudMode(form, CommonConstants.CONFIRM_MODE);
+        loadRole(form);
+        return asHtml(path_AdminRole_ConfirmJsp);
+    }
+
+    @Token(save = false, validate = true, keep = true)
+    @Execute
+    public HtmlResponse confirmfromcreate(final RoleEditForm form) {
+        validate(form, messages -> {}, toEditHtml());
+        return asHtml(path_AdminRole_ConfirmJsp);
+    }
+
+    @Token(save = false, validate = true, keep = true)
+    @Execute
+    public HtmlResponse confirmfromupdate(final RoleEditForm form) {
+        validate(form, messages -> {}, toEditHtml());
+        return asHtml(path_AdminRole_ConfirmJsp);
+    }
+
+    // -----------------------------------------------------
+    //                                         Actually Crud
+    //                                         -------------
+    @Token(save = false, validate = true)
+    @Execute
+    public HtmlResponse create(final RoleEditForm form) {
+        validate(form, messages -> {}, toEditHtml());
+        roleService.store(createRole(form));
+        saveInfo(messages -> messages.addSuccessCrudCreateCrudTable(GLOBAL));
+        return redirect(getClass());
+    }
+
+    @Token(save = false, validate = true)
+    @Execute
+    public HtmlResponse update(final RoleEditForm form) {
+        validate(form, messages -> {}, toEditHtml());
+        roleService.store(createRole(form));
+        saveInfo(messages -> messages.addSuccessCrudUpdateCrudTable(GLOBAL));
+        return redirect(getClass());
+    }
+
+    @Execute
+    public HtmlResponse delete(final RoleEditForm form) {
+        verifyCrudMode(form, CommonConstants.DELETE_MODE);
+        roleService.delete(getRole(form));
+        saveInfo(messages -> messages.addSuccessCrudDeleteCrudTable(GLOBAL));
+        return redirect(getClass());
+    }
+
+    // ===================================================================================
+    //                                                                        Assist Logic
+    //                                                                        ============
+    protected void loadRole(final RoleEditForm form) {
+        copyBeanToBean(getRole(form), form, op -> op.exclude("crudMode"));
+    }
+
+    protected Role getRole(final RoleEditForm form) {
+        final Role role = roleService.getRole(createKeyMap(form));
+        if (role == null) {
+            throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, form.id), toEditHtml());
+        }
+        return role;
+    }
+
+    protected Role createRole(final RoleEditForm form) {
+        Role role;
+        if (form.crudMode == CommonConstants.EDIT_MODE) {
+            role = getRole(form);
+        } else {
+            role = new Role();
+        }
+        copyBeanToBean(form, role, op -> op.exclude(CommonConstants.COMMON_CONVERSION_RULE));
+        role.setId(Base64.getEncoder().encodeToString(role.getName().getBytes(Constants.CHARSET_UTF_8)));
+        return role;
+    }
+
+    protected Map<String, String> createKeyMap(final RoleEditForm form) {
+        final Map<String, String> keys = new HashMap<String, String>();
+        keys.put("id", form.id);
+        return keys;
+    }
+
+    // ===================================================================================
+    //                                                                        Small Helper
+    //                                                                        ============
+    protected void verifyCrudMode(final RoleEditForm form, final int expectedMode) {
+        if (form.crudMode != expectedMode) {
+            throwValidationError(messages -> {
+                messages.addErrorsCrudInvalidMode(GLOBAL, String.valueOf(expectedMode), String.valueOf(form.crudMode));
+            }, toEditHtml());
+        }
+    }
+
+    protected VaErrorHook toEditHtml() {
+        return () -> {
+            return asHtml(path_AdminRole_EditJsp);
+        };
+    }
+}

+ 48 - 0
src/main/java/org/codelibs/fess/app/web/admin/role/RoleEditForm.java

@@ -0,0 +1,48 @@
+/*
+ * Copyright 2009-2015 the CodeLibs Project and the Others.
+ *
+ * Licensed 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.
+ */
+
+package org.codelibs.fess.app.web.admin.role;
+
+import java.io.Serializable;
+
+/**
+ * @author shinsuke
+ */
+public class RoleEditForm implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    //@IntegerType
+    public int crudMode;
+
+    //@Required(target = "confirmfromupdate,update,delete")
+    //@Maxbytelength(maxbytelength = 1000)
+    public String id;
+
+    //@Required(target = "confirmfromcreate,create,confirmfromupdate,update,delete")
+    //@Maxbytelength(maxbytelength = 100)
+    public String name;
+
+    //@Required(target = "confirmfromupdate,update,delete")
+    //@IntegerType
+    public String versionNo;
+
+    public void initialize() {
+        id = null;
+        name = null;
+        versionNo = null;
+    }
+}

+ 31 - 0
src/main/java/org/codelibs/fess/app/web/admin/role/RoleSearchForm.java

@@ -0,0 +1,31 @@
+/*
+ * Copyright 2009-2015 the CodeLibs Project and the Others.
+ *
+ * Licensed 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.
+ */
+
+package org.codelibs.fess.app.web.admin.role;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author shinsuke
+ */
+public class RoleSearchForm implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    public Map<String, String> searchParams = new HashMap<String, String>();
+}

+ 0 - 1
src/main/java/org/codelibs/fess/app/web/admin/suggestelevateword/AdminSuggestelevatewordAction.java

@@ -45,7 +45,6 @@ import org.codelibs.fess.crud.CommonConstants;
 import org.codelibs.fess.es.exentity.SuggestElevateWord;
 import org.codelibs.fess.helper.SuggestHelper;
 import org.codelibs.fess.helper.SystemHelper;
-import org.codelibs.fess.util.ComponentUtil;
 import org.lastaflute.web.Execute;
 import org.lastaflute.web.callback.ActionRuntime;
 import org.lastaflute.web.response.HtmlResponse;

+ 361 - 0
src/main/java/org/codelibs/fess/app/web/admin/user/AdminUserAction.java

@@ -0,0 +1,361 @@
+/*
+ * Copyright 2009-2015 the CodeLibs Project and the Others.
+ *
+ * Licensed 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.
+ */
+
+package org.codelibs.fess.app.web.admin.user;
+
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.annotation.Resource;
+
+import org.codelibs.core.lang.StringUtil;
+import org.codelibs.fess.Constants;
+import org.codelibs.fess.annotation.Token;
+import org.codelibs.fess.app.pager.UserPager;
+import org.codelibs.fess.app.service.GroupService;
+import org.codelibs.fess.app.service.RoleService;
+import org.codelibs.fess.app.service.UserService;
+import org.codelibs.fess.app.web.base.FessAdminAction;
+import org.codelibs.fess.crud.CommonConstants;
+import org.codelibs.fess.es.exentity.User;
+import org.codelibs.fess.helper.SystemHelper;
+import org.lastaflute.web.Execute;
+import org.lastaflute.web.callback.ActionRuntime;
+import org.lastaflute.web.response.HtmlResponse;
+import org.lastaflute.web.response.render.RenderData;
+import org.lastaflute.web.validation.VaErrorHook;
+
+/**
+ * @author shinsuke
+ */
+public class AdminUserAction extends FessAdminAction {
+
+    private static final String TEMPORARY_PASSWORD = "fess.temporary_password";
+    // ===================================================================================
+    //                                                                           Attribute
+    //                                                                           =========
+    @Resource
+    private UserService userService;
+    @Resource
+    private RoleService roleService;
+    @Resource
+    private GroupService groupService;
+    @Resource
+    private UserPager userPager;
+    @Resource
+    private SystemHelper systemHelper;
+
+    // ===================================================================================
+    //                                                                               Hook
+    //                                                                              ======
+    @Override
+    protected void setupHtmlData(final ActionRuntime runtime) {
+        super.setupHtmlData(runtime);
+        runtime.registerData("helpLink", systemHelper.getHelpLink("user"));
+    }
+
+    // ===================================================================================
+    //                                                                      Search Execute
+    //                                                                      ==============
+    @Execute
+    public HtmlResponse index(final UserSearchForm form) {
+        clearStoredPassword();
+        return asHtml(path_AdminUser_IndexJsp).renderWith(data -> {
+            searchPaging(data, form);
+        });
+    }
+
+    @Execute
+    public HtmlResponse list(final Integer pageNumber, final UserSearchForm form) {
+        clearStoredPassword();
+        userPager.setCurrentPageNumber(pageNumber);
+        return asHtml(path_AdminUser_IndexJsp).renderWith(data -> {
+            searchPaging(data, form);
+        });
+    }
+
+    @Execute
+    public HtmlResponse search(final UserSearchForm form) {
+        clearStoredPassword();
+        copyBeanToBean(form.searchParams, userPager, op -> op.exclude(CommonConstants.PAGER_CONVERSION_RULE));
+        return asHtml(path_AdminUser_IndexJsp).renderWith(data -> {
+            searchPaging(data, form);
+        });
+    }
+
+    @Execute
+    public HtmlResponse reset(final UserSearchForm form) {
+        clearStoredPassword();
+        userPager.clear();
+        return asHtml(path_AdminUser_IndexJsp).renderWith(data -> {
+            searchPaging(data, form);
+        });
+    }
+
+    @Execute
+    public HtmlResponse back(final UserSearchForm form) {
+        clearStoredPassword();
+        return asHtml(path_AdminUser_IndexJsp).renderWith(data -> {
+            searchPaging(data, form);
+        });
+    }
+
+    protected void searchPaging(final RenderData data, final UserSearchForm form) {
+        data.register("userItems", userService.getUserList(userPager)); // page navi
+
+        // restore from pager
+        copyBeanToBean(userPager, form.searchParams, op -> op.exclude(CommonConstants.PAGER_CONVERSION_RULE));
+    }
+
+    private void registerForms(final RenderData data) {
+        data.register("roleItems", roleService.getAvailableRoleList());
+        data.register("groupItems", groupService.getAvailableGroupList());
+    }
+
+    // ===================================================================================
+    //                                                                        Edit Execute
+    //                                                                        ============
+    // -----------------------------------------------------
+    //                                            Entry Page
+    //                                            ----------
+    @Token(save = true, validate = false)
+    @Execute
+    public HtmlResponse createpage(final UserEditForm form) {
+        clearStoredPassword();
+        form.initialize();
+        form.crudMode = CommonConstants.CREATE_MODE;
+        return asHtml(path_AdminUser_EditJsp).renderWith(data -> {
+            registerForms(data);
+        });
+    }
+
+    @Token(save = true, validate = false)
+    @Execute
+    public HtmlResponse editpage(final int crudMode, final String id, final UserEditForm form) {
+        clearStoredPassword();
+        form.crudMode = crudMode;
+        form.id = id;
+        verifyCrudMode(form, CommonConstants.EDIT_MODE);
+        loadUser(form, false);
+        return asHtml(path_AdminUser_EditJsp).renderWith(data -> {
+            registerForms(data);
+        });
+    }
+
+    @Token(save = true, validate = false)
+    @Execute
+    public HtmlResponse editagain(final UserEditForm form) {
+        clearStoredPassword();
+        return asHtml(path_AdminUser_EditJsp).renderWith(data -> {
+            registerForms(data);
+        });
+    }
+
+    @Token(save = true, validate = false)
+    @Execute
+    public HtmlResponse editfromconfirm(final UserEditForm form) {
+        clearStoredPassword();
+        form.crudMode = CommonConstants.EDIT_MODE;
+        loadUser(form, false);
+        return asHtml(path_AdminUser_EditJsp).renderWith(data -> {
+            registerForms(data);
+        });
+    }
+
+    @Token(save = true, validate = false)
+    @Execute
+    public HtmlResponse deletepage(final int crudMode, final String id, final UserEditForm form) {
+        clearStoredPassword();
+        form.crudMode = crudMode;
+        form.id = id;
+        verifyCrudMode(form, CommonConstants.DELETE_MODE);
+        loadUser(form, false);
+        return asHtml(path_AdminUser_ConfirmJsp).renderWith(data -> {
+            registerForms(data);
+        });
+    }
+
+    @Token(save = true, validate = false)
+    @Execute
+    public HtmlResponse deletefromconfirm(final UserEditForm form) {
+        clearStoredPassword();
+        form.crudMode = CommonConstants.DELETE_MODE;
+        loadUser(form, false);
+        return asHtml(path_AdminUser_ConfirmJsp).renderWith(data -> {
+            registerForms(data);
+        });
+    }
+
+    // -----------------------------------------------------
+    //                                               Confirm
+    //                                               -------
+    @Execute
+    public HtmlResponse confirmpage(final int crudMode, final String id, final UserEditForm form) {
+        form.crudMode = crudMode;
+        form.id = id;
+        verifyCrudMode(form, CommonConstants.CONFIRM_MODE);
+        verifyPassword(form);
+        loadUser(form, false);
+        return asHtml(path_AdminUser_ConfirmJsp).renderWith(data -> {
+            registerForms(data);
+        });
+    }
+
+    @Token(save = false, validate = true, keep = true)
+    @Execute
+    public HtmlResponse confirmfromcreate(final UserEditForm form) {
+        verifyPassword(form);
+        storePassword(form);
+        validate(form, messages -> {}, toEditHtml());
+        return asHtml(path_AdminUser_ConfirmJsp).renderWith(data -> {
+            registerForms(data);
+        });
+    }
+
+    @Token(save = false, validate = true, keep = true)
+    @Execute
+    public HtmlResponse confirmfromupdate(final UserEditForm form) {
+        verifyPassword(form);
+        storePassword(form);
+        validate(form, messages -> {}, toEditHtml());
+        return asHtml(path_AdminUser_ConfirmJsp).renderWith(data -> {
+            registerForms(data);
+        });
+    }
+
+    // -----------------------------------------------------
+    //                                         Actually Crud
+    //                                         -------------
+    @Token(save = false, validate = true)
+    @Execute
+    public HtmlResponse create(final UserEditForm form) {
+        validate(form, messages -> {}, toEditHtml());
+        verifyPassword(form);
+        userService.store(createUser(form));
+        saveInfo(messages -> messages.addSuccessCrudCreateCrudTable(GLOBAL));
+        return redirect(getClass());
+    }
+
+    @Token(save = false, validate = true)
+    @Execute
+    public HtmlResponse update(final UserEditForm form) {
+        validate(form, messages -> {}, toEditHtml());
+        verifyPassword(form);
+        userService.store(createUser(form));
+        saveInfo(messages -> messages.addSuccessCrudUpdateCrudTable(GLOBAL));
+        return redirect(getClass());
+    }
+
+    @Execute
+    public HtmlResponse delete(final UserEditForm form) {
+        verifyCrudMode(form, CommonConstants.DELETE_MODE);
+        userService.delete(getUser(form));
+        saveInfo(messages -> messages.addSuccessCrudDeleteCrudTable(GLOBAL));
+        return redirect(getClass());
+    }
+
+    // ===================================================================================
+    //                                                                        Assist Logic
+    //                                                                        ============
+    protected void loadUser(final UserEditForm form, final boolean hasPassword) {
+        copyBeanToBean(getUser(form), form, op -> op.exclude("crudMode"));
+        if (!hasPassword) {
+            form.password = null;
+            form.confirmPassword = null;
+        }
+    }
+
+    protected User getUser(final UserEditForm form) {
+        final User user = userService.getUser(createKeyMap(form));
+        if (user == null) {
+            throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, form.id), toEditHtml());
+        }
+        return user;
+    }
+
+    protected User createUser(final UserEditForm form) {
+        User user;
+        if (form.crudMode == CommonConstants.EDIT_MODE) {
+            user = getUser(form);
+        } else {
+            user = new User();
+        }
+        copyBeanToBean(form, user, op -> op.exclude("password", "confirmPassword"));
+        sessionManager.getAttribute(TEMPORARY_PASSWORD, String.class).ifPresent(password -> {
+            user.setPassword(password);
+        });
+        user.setId(Base64.getEncoder().encodeToString(user.getName().getBytes(Constants.CHARSET_UTF_8)));
+        return user;
+    }
+
+    protected Map<String, String> createKeyMap(final UserEditForm form) {
+        final Map<String, String> keys = new HashMap<String, String>();
+        keys.put("id", form.id);
+        return keys;
+    }
+
+    // ===================================================================================
+    //                                                                        Small Helper
+    //                                                                        ============
+    protected void verifyCrudMode(final UserEditForm form, final int expectedMode) {
+        if (form.crudMode != expectedMode) {
+            throwValidationError(messages -> {
+                messages.addErrorsCrudInvalidMode(GLOBAL, String.valueOf(expectedMode), String.valueOf(form.crudMode));
+            }, toEditHtml());
+        }
+    }
+
+    protected void verifyPassword(final UserEditForm form) {
+        if (form.crudMode == CommonConstants.CREATE_MODE && StringUtil.isBlank(form.password)) {
+            throwValidationError(messages -> {
+                messages.addErrorsBlankPassword(GLOBAL);
+            }, toEditHtml());
+        }
+        if (form.password != null && !form.password.equals(form.confirmPassword)) {
+            throwValidationError(messages -> {
+                messages.addErrorsInvalidConfirmPassword(GLOBAL);
+            }, toEditHtml());
+        }
+    }
+
+    protected void verifyStoredPassword(final UserEditForm form) {
+        if (!sessionManager.getAttribute(TEMPORARY_PASSWORD, String.class).isPresent()) {
+            throwValidationError(messages -> {
+                messages.addErrorsInvalidConfirmPassword(GLOBAL);
+            }, toEditHtml());
+        }
+    }
+
+    private void clearStoredPassword() {
+        sessionManager.removeAttribute(TEMPORARY_PASSWORD);
+    }
+
+    private void storePassword(final UserEditForm form) {
+        final String encodedPassword = fessLoginAssist.encryptPassword(form.password);
+        sessionManager.setAttribute(TEMPORARY_PASSWORD, encodedPassword);
+        form.password = null;
+        form.confirmPassword = null;
+    }
+
+    protected VaErrorHook toEditHtml() {
+        return () -> {
+            return asHtml(path_AdminUser_EditJsp).renderWith(data -> {
+                registerForms(data);
+            });
+        };
+    }
+}

+ 60 - 0
src/main/java/org/codelibs/fess/app/web/admin/user/UserEditForm.java

@@ -0,0 +1,60 @@
+/*
+ * Copyright 2009-2015 the CodeLibs Project and the Others.
+ *
+ * Licensed 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.
+ */
+
+package org.codelibs.fess.app.web.admin.user;
+
+import java.io.Serializable;
+
+/**
+ * @author shinsuke
+ */
+public class UserEditForm implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    //@IntegerType
+    public int crudMode;
+
+    //@Required(target = "confirmfromupdate,update,delete")
+    //@Maxbytelength(maxbytelength = 1000)
+    public String id;
+
+    //@Required(target = "confirmfromcreate,create,confirmfromupdate,update,delete")
+    //@Maxbytelength(maxbytelength = 100)
+    public String name;
+
+    //@Maxbytelength(maxbytelength = 100)
+    public String password;
+
+    //@Maxbytelength(maxbytelength = 100)
+    public String confirmPassword;
+
+    public String[] roles;
+
+    public String[] groups;
+
+    //@Required(target = "confirmfromupdate,update,delete")
+    //@IntegerType
+    public String versionNo;
+
+    public void initialize() {
+        id = null;
+        name = null;
+        roles = null;
+        groups = null;
+        versionNo = null;
+    }
+}

+ 31 - 0
src/main/java/org/codelibs/fess/app/web/admin/user/UserSearchForm.java

@@ -0,0 +1,31 @@
+/*
+ * Copyright 2009-2015 the CodeLibs Project and the Others.
+ *
+ * Licensed 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.
+ */
+
+package org.codelibs.fess.app.web.admin.user;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author shinsuke
+ */
+public class UserSearchForm implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    public Map<String, String> searchParams = new HashMap<String, String>();
+}

+ 0 - 4
src/main/java/org/codelibs/fess/app/web/base/FessAdminAction.java

@@ -17,14 +17,12 @@ package org.codelibs.fess.app.web.base;
 
 import java.util.function.Consumer;
 
-import javax.annotation.Resource;
 import javax.servlet.ServletContext;
 
 import org.codelibs.core.beans.util.BeanUtil;
 import org.codelibs.core.beans.util.CopyOptions;
 import org.codelibs.fess.mylasta.action.FessMessages;
 import org.lastaflute.di.util.LdiFileUtil;
-import org.lastaflute.web.servlet.session.SessionManager;
 import org.lastaflute.web.util.LaServletContextUtil;
 import org.lastaflute.web.validation.VaMessenger;
 
@@ -37,8 +35,6 @@ public abstract class FessAdminAction extends FessBaseAction {
     // ===================================================================================
     //                                                                           Attribute
     //                                                                           =========
-    @Resource
-    private SessionManager sessionManager;
 
     // ===================================================================================
     //                                                                        Small Helper

+ 5 - 1
src/main/java/org/codelibs/fess/app/web/base/FessBaseAction.java

@@ -44,6 +44,7 @@ import org.lastaflute.web.TypicalAction;
 import org.lastaflute.web.callback.ActionRuntime;
 import org.lastaflute.web.login.LoginManager;
 import org.lastaflute.web.response.ActionResponse;
+import org.lastaflute.web.servlet.session.SessionManager;
 import org.lastaflute.web.validation.ActionValidator;
 import org.lastaflute.web.validation.LaValidatable;
 
@@ -66,7 +67,10 @@ public abstract class FessBaseAction extends TypicalAction // has several interf
     //                                                                           Attribute
     //                                                                           =========
     @Resource
-    private FessLoginAssist fessLoginAssist;
+    protected FessLoginAssist fessLoginAssist;
+
+    @Resource
+    protected SessionManager sessionManager;
 
     // ===================================================================================
     //                                                                               Hook

+ 1 - 2
src/main/java/org/codelibs/fess/app/web/base/FessSearchAction.java

@@ -43,7 +43,6 @@ import org.codelibs.fess.helper.SystemHelper;
 import org.codelibs.fess.helper.UserInfoHelper;
 import org.codelibs.fess.helper.ViewHelper;
 import org.codelibs.fess.screenshot.ScreenShotManager;
-import org.codelibs.fess.util.SearchParamMap;
 import org.lastaflute.web.callback.ActionRuntime;
 import org.lastaflute.web.response.ActionResponse;
 import org.lastaflute.web.util.LaRequestUtil;
@@ -135,7 +134,7 @@ public abstract class FessSearchAction extends FessBaseAction {
         }
     }
 
-    protected void buildLabelParams(final SearchParamMap fields) {
+    protected void buildLabelParams(final Map<String, String[]> fields) {
         // label
         final List<Map<String, String>> labelTypeItems = labelTypeHelper.getLabelTypeItemList();
 

+ 9 - 8
src/main/java/org/codelibs/fess/app/web/search/SearchAction.java

@@ -288,14 +288,15 @@ public class SearchAction extends FessSearchAction {
         List<Map<String, Object>> documentItems = null;
         try {
             documentItems =
-                    fessEsClient.search(fieldHelper.docIndex, fieldHelper.docType, searchRequestBuilder -> {
-                        return SearchConditionBuilder.builder(searchRequestBuilder).query(query).offset((int) pageStart).size(pageNum)
-                                .facetInfo(form.facet).geoInfo(form.geo).responseFields(queryHelper.getResponseFields()).build();
-                    }, (searchRequestBuilder, execTime, searchResponse) -> {
-                        QueryResponseList queryResponseList = ComponentUtil.getQueryResponseList();
-                        queryResponseList.init(searchResponse, pageStart, pageNum);
-                        return queryResponseList;
-                    });
+                    fessEsClient.search(fieldHelper.docIndex, fieldHelper.docType,
+                            searchRequestBuilder -> {
+                                return SearchConditionBuilder.builder(searchRequestBuilder).query(query).offset(pageStart).size(pageNum)
+                                        .facetInfo(form.facet).geoInfo(form.geo).responseFields(queryHelper.getResponseFields()).build();
+                            }, (searchRequestBuilder, execTime, searchResponse) -> {
+                                final QueryResponseList queryResponseList = ComponentUtil.getQueryResponseList();
+                                queryResponseList.init(searchResponse, pageStart, pageNum);
+                                return queryResponseList;
+                            });
         } catch (final InvalidQueryException e) {
             if (logger.isDebugEnabled()) {
                 logger.debug(e.getMessage(), e);

+ 4 - 2
src/main/java/org/codelibs/fess/app/web/search/SearchForm.java

@@ -16,10 +16,12 @@
 
 package org.codelibs.fess.app.web.search;
 
+import java.util.HashMap;
+import java.util.Map;
+
 import org.codelibs.fess.app.web.RootForm;
 import org.codelibs.fess.entity.FacetInfo;
 import org.codelibs.fess.entity.GeoInfo;
-import org.codelibs.fess.util.SearchParamMap;
 
 public class SearchForm extends RootForm {
 
@@ -79,6 +81,6 @@ public class SearchForm extends RootForm {
 
     // advance
 
-    public SearchParamMap options = new SearchParamMap();
+    public Map<String, String[]> options = new HashMap<>();
 
 }

+ 14 - 0
src/main/java/org/codelibs/fess/es/bsbhv/AbstractBehavior.java

@@ -25,6 +25,7 @@ import org.dbflute.cbean.ConditionBean;
 import org.dbflute.cbean.coption.CursorSelectOption;
 import org.dbflute.cbean.result.ListResultBean;
 import org.dbflute.exception.IllegalBehaviorStateException;
+import org.dbflute.util.DfTypeUtil;
 import org.elasticsearch.action.bulk.BulkItemResponse;
 import org.elasticsearch.action.bulk.BulkRequestBuilder;
 import org.elasticsearch.action.bulk.BulkResponse;
@@ -348,6 +349,19 @@ public abstract class AbstractBehavior<ENTITY extends Entity, CB extends Conditi
         return results;
     }
 
+    public static String[] toStringArray(final Object value) {
+        if (value instanceof String[]) {
+            return (String[]) value;
+        } else if (value instanceof List) {
+            return ((List<?>) value).stream().map(v -> v.toString()).toArray(n -> new String[n]);
+        }
+        String str = DfTypeUtil.toString(value);
+        if (str == null) {
+            return null;
+        }
+        return new String[] { str };
+    }
+
     public static class BulkList<E> implements List<E> {
 
         private final List<E> parent;

+ 2 - 2
src/main/java/org/codelibs/fess/es/bsbhv/BsUserBhv.java

@@ -55,11 +55,11 @@ public abstract class BsUserBhv extends AbstractBehavior<User, UserCB> {
     protected <RESULT extends User> RESULT createEntity(Map<String, Object> source, Class<? extends RESULT> entityType) {
         try {
             final RESULT result = entityType.newInstance();
-            result.setGroup(DfTypeUtil.toString(source.get("group")));
+            result.setGroups(toStringArray(source.get("groups")));
             result.setId(DfTypeUtil.toString(source.get("id")));
             result.setName(DfTypeUtil.toString(source.get("name")));
             result.setPassword(DfTypeUtil.toString(source.get("password")));
-            result.setRole(DfTypeUtil.toString(source.get("role")));
+            result.setRoles(toStringArray(source.get("roles")));
             return result;
         } catch (InstantiationException | IllegalAccessException e) {
             final String msg = "Cannot create a new instance: " + entityType.getName();

+ 20 - 20
src/main/java/org/codelibs/fess/es/bsentity/BsUser.java

@@ -26,8 +26,8 @@ public class BsUser extends AbstractEntity {
     // ===================================================================================
     //                                                                           Attribute
     //                                                                           =========
-    /** group */
-    protected String group;
+    /** groups */
+    protected String[] groups;
 
     /** name */
     protected String name;
@@ -35,22 +35,22 @@ public class BsUser extends AbstractEntity {
     /** password */
     protected String password;
 
-    /** role */
-    protected String role;
+    /** roles */
+    protected String[] roles;
 
     // [Referrers] *comment only
 
     // ===================================================================================
     //                                                                            Accessor
     //                                                                            ========
-    public String getGroup() {
-        checkSpecifiedProperty("group");
-        return group;
+    public String[] getGroups() {
+        checkSpecifiedProperty("groups");
+        return groups;
     }
 
-    public void setGroup(String value) {
-        registerModifiedProperty("group");
-        this.group = value;
+    public void setGroups(String[] value) {
+        registerModifiedProperty("groups");
+        this.groups = value;
     }
 
     public String getId() {
@@ -83,21 +83,21 @@ public class BsUser extends AbstractEntity {
         this.password = value;
     }
 
-    public String getRole() {
-        checkSpecifiedProperty("role");
-        return role;
+    public String[] getRoles() {
+        checkSpecifiedProperty("roles");
+        return roles;
     }
 
-    public void setRole(String value) {
-        registerModifiedProperty("role");
-        this.role = value;
+    public void setRoles(String[] value) {
+        registerModifiedProperty("roles");
+        this.roles = value;
     }
 
     @Override
     public Map<String, Object> toSource() {
         Map<String, Object> sourceMap = new HashMap<>();
-        if (group != null) {
-            sourceMap.put("group", group);
+        if (groups != null) {
+            sourceMap.put("groups", groups);
         }
         if (asDocMeta().id() != null) {
             sourceMap.put("id", asDocMeta().id());
@@ -108,8 +108,8 @@ public class BsUser extends AbstractEntity {
         if (password != null) {
             sourceMap.put("password", password);
         }
-        if (role != null) {
-            sourceMap.put("role", role);
+        if (roles != null) {
+            sourceMap.put("roles", roles);
         }
         return sourceMap;
     }

+ 12 - 12
src/main/java/org/codelibs/fess/es/bsentity/dbmeta/UserDbm.java

@@ -35,11 +35,11 @@ public class UserDbm extends AbstractDBMeta {
     //                                       ---------------
     protected final Map<String, PropertyGateway> _epgMap = newHashMap();
     {
-        setupEpg(_epgMap, et -> ((User) et).getGroup(), (et, vl) -> ((User) et).setGroup(DfTypeUtil.toString(vl)), "group");
+        setupEpg(_epgMap, et -> ((User) et).getGroups(), (et, vl) -> ((User) et).setGroups((String[]) vl), "groups");
         setupEpg(_epgMap, et -> ((User) et).getId(), (et, vl) -> ((User) et).setId(DfTypeUtil.toString(vl)), "id");
         setupEpg(_epgMap, et -> ((User) et).getName(), (et, vl) -> ((User) et).setName(DfTypeUtil.toString(vl)), "name");
         setupEpg(_epgMap, et -> ((User) et).getPassword(), (et, vl) -> ((User) et).setPassword(DfTypeUtil.toString(vl)), "password");
-        setupEpg(_epgMap, et -> ((User) et).getRole(), (et, vl) -> ((User) et).setRole(DfTypeUtil.toString(vl)), "role");
+        setupEpg(_epgMap, et -> ((User) et).getRoles(), (et, vl) -> ((User) et).setRoles((String[]) vl), "roles");
     }
 
     @Override
@@ -50,19 +50,19 @@ public class UserDbm extends AbstractDBMeta {
     // ===================================================================================
     //                                                                         Column Info
     //                                                                         ===========
-    protected final ColumnInfo _columnGroup = cci("group", "group", null, null, String.class, "group", null, false, false, false, "String",
-            0, 0, null, false, null, null, null, null, null, false);
+    protected final ColumnInfo _columnGroups = cci("groups", "groups", null, null, String[].class, "groups", null, false, false, false,
+            "String", 0, 0, null, false, null, null, null, null, null, false);
     protected final ColumnInfo _columnId = cci("id", "id", null, null, String.class, "id", null, false, false, false, "String", 0, 0, null,
             false, null, null, null, null, null, false);
     protected final ColumnInfo _columnName = cci("name", "name", null, null, String.class, "name", null, false, false, false, "String", 0,
             0, null, false, null, null, null, null, null, false);
     protected final ColumnInfo _columnPassword = cci("password", "password", null, null, String.class, "password", null, false, false,
             false, "String", 0, 0, null, false, null, null, null, null, null, false);
-    protected final ColumnInfo _columnRole = cci("role", "role", null, null, String.class, "role", null, false, false, false, "String", 0,
-            0, null, false, null, null, null, null, null, false);
+    protected final ColumnInfo _columnRoles = cci("roles", "roles", null, null, String[].class, "roles", null, false, false, false,
+            "String", 0, 0, null, false, null, null, null, null, null, false);
 
-    public ColumnInfo columnGroup() {
-        return _columnGroup;
+    public ColumnInfo columnGroups() {
+        return _columnGroups;
     }
 
     public ColumnInfo columnId() {
@@ -77,17 +77,17 @@ public class UserDbm extends AbstractDBMeta {
         return _columnPassword;
     }
 
-    public ColumnInfo columnRole() {
-        return _columnRole;
+    public ColumnInfo columnRoles() {
+        return _columnRoles;
     }
 
     protected List<ColumnInfo> ccil() {
         List<ColumnInfo> ls = newArrayList();
-        ls.add(columnGroup());
+        ls.add(columnGroups());
         ls.add(columnId());
         ls.add(columnName());
         ls.add(columnPassword());
-        ls.add(columnRole());
+        ls.add(columnRoles());
         return ls;
     }
 

+ 4 - 4
src/main/java/org/codelibs/fess/es/cbean/bs/BsUserCB.java

@@ -123,8 +123,8 @@ public class BsUserCB extends AbstractConditionBean {
             columnList.add(name);
         }
 
-        public void columnGroup() {
-            doColumn("group");
+        public void columnGroups() {
+            doColumn("groups");
         }
 
         public void columnId() {
@@ -139,8 +139,8 @@ public class BsUserCB extends AbstractConditionBean {
             doColumn("password");
         }
 
-        public void columnRole() {
-            doColumn("role");
+        public void columnRoles() {
+            doColumn("roles");
         }
     }
 }

+ 96 - 96
src/main/java/org/codelibs/fess/es/cbean/cf/bs/BsUserCF.java

@@ -106,127 +106,127 @@ public abstract class BsUserCF extends AbstractConditionFilter {
         }
     }
 
-    public void setGroup_NotEqual(String group) {
-        setGroup_NotEqual(group, null, null);
+    public void setGroups_NotEqual(String groups) {
+        setGroups_NotEqual(groups, null, null);
     }
 
-    public void setGroup_NotEqual(String group, ConditionOptionCall<NotFilterBuilder> notOpLambda,
+    public void setGroups_NotEqual(String groups, ConditionOptionCall<NotFilterBuilder> notOpLambda,
             ConditionOptionCall<TermFilterBuilder> eqOpLambda) {
         not(subCf -> {
-            subCf.setGroup_Equal(group, eqOpLambda);
+            subCf.setGroups_Equal(groups, eqOpLambda);
         }, notOpLambda);
     }
 
-    public void setGroup_Equal(String group) {
-        setGroup_Term(group, null);
+    public void setGroups_Equal(String groups) {
+        setGroups_Term(groups, null);
     }
 
-    public void setGroup_Equal(String group, ConditionOptionCall<TermFilterBuilder> opLambda) {
-        setGroup_Term(group, opLambda);
+    public void setGroups_Equal(String groups, ConditionOptionCall<TermFilterBuilder> opLambda) {
+        setGroups_Term(groups, opLambda);
     }
 
-    public void setGroup_Term(String group) {
-        setGroup_Term(group, null);
+    public void setGroups_Term(String groups) {
+        setGroups_Term(groups, null);
     }
 
-    public void setGroup_Term(String group, ConditionOptionCall<TermFilterBuilder> opLambda) {
-        TermFilterBuilder builder = regTermF("group", group);
+    public void setGroups_Term(String groups, ConditionOptionCall<TermFilterBuilder> opLambda) {
+        TermFilterBuilder builder = regTermF("groups", groups);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setGroup_Terms(Collection<String> groupList) {
-        setGroup_Terms(groupList, null);
+    public void setGroups_Terms(Collection<String> groupsList) {
+        setGroups_Terms(groupsList, null);
     }
 
-    public void setGroup_Terms(Collection<String> groupList, ConditionOptionCall<TermsFilterBuilder> opLambda) {
-        TermsFilterBuilder builder = regTermsF("group", groupList);
+    public void setGroups_Terms(Collection<String> groupsList, ConditionOptionCall<TermsFilterBuilder> opLambda) {
+        TermsFilterBuilder builder = regTermsF("groups", groupsList);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setGroup_InScope(Collection<String> groupList) {
-        setGroup_Terms(groupList, null);
+    public void setGroups_InScope(Collection<String> groupsList) {
+        setGroups_Terms(groupsList, null);
     }
 
-    public void setGroup_InScope(Collection<String> groupList, ConditionOptionCall<TermsFilterBuilder> opLambda) {
-        setGroup_Terms(groupList, opLambda);
+    public void setGroups_InScope(Collection<String> groupsList, ConditionOptionCall<TermsFilterBuilder> opLambda) {
+        setGroups_Terms(groupsList, opLambda);
     }
 
-    public void setGroup_Prefix(String group) {
-        setGroup_Prefix(group, null);
+    public void setGroups_Prefix(String groups) {
+        setGroups_Prefix(groups, null);
     }
 
-    public void setGroup_Prefix(String group, ConditionOptionCall<PrefixFilterBuilder> opLambda) {
-        PrefixFilterBuilder builder = regPrefixF("group", group);
+    public void setGroups_Prefix(String groups, ConditionOptionCall<PrefixFilterBuilder> opLambda) {
+        PrefixFilterBuilder builder = regPrefixF("groups", groups);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setGroup_Exists() {
-        setGroup_Exists(null);
+    public void setGroups_Exists() {
+        setGroups_Exists(null);
     }
 
-    public void setGroup_Exists(ConditionOptionCall<ExistsFilterBuilder> opLambda) {
-        ExistsFilterBuilder builder = regExistsF("group");
+    public void setGroups_Exists(ConditionOptionCall<ExistsFilterBuilder> opLambda) {
+        ExistsFilterBuilder builder = regExistsF("groups");
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setGroup_Missing() {
-        setGroup_Missing(null);
+    public void setGroups_Missing() {
+        setGroups_Missing(null);
     }
 
-    public void setGroup_Missing(ConditionOptionCall<MissingFilterBuilder> opLambda) {
-        MissingFilterBuilder builder = regMissingF("group");
+    public void setGroups_Missing(ConditionOptionCall<MissingFilterBuilder> opLambda) {
+        MissingFilterBuilder builder = regMissingF("groups");
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setGroup_GreaterThan(String group) {
-        setGroup_GreaterThan(group, null);
+    public void setGroups_GreaterThan(String groups) {
+        setGroups_GreaterThan(groups, null);
     }
 
-    public void setGroup_GreaterThan(String group, ConditionOptionCall<RangeFilterBuilder> opLambda) {
-        RangeFilterBuilder builder = regRangeF("group", ConditionKey.CK_GREATER_THAN, group);
+    public void setGroups_GreaterThan(String groups, ConditionOptionCall<RangeFilterBuilder> opLambda) {
+        RangeFilterBuilder builder = regRangeF("groups", ConditionKey.CK_GREATER_THAN, groups);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setGroup_LessThan(String group) {
-        setGroup_LessThan(group, null);
+    public void setGroups_LessThan(String groups) {
+        setGroups_LessThan(groups, null);
     }
 
-    public void setGroup_LessThan(String group, ConditionOptionCall<RangeFilterBuilder> opLambda) {
-        RangeFilterBuilder builder = regRangeF("group", ConditionKey.CK_LESS_THAN, group);
+    public void setGroups_LessThan(String groups, ConditionOptionCall<RangeFilterBuilder> opLambda) {
+        RangeFilterBuilder builder = regRangeF("groups", ConditionKey.CK_LESS_THAN, groups);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setGroup_GreaterEqual(String group) {
-        setGroup_GreaterEqual(group, null);
+    public void setGroups_GreaterEqual(String groups) {
+        setGroups_GreaterEqual(groups, null);
     }
 
-    public void setGroup_GreaterEqual(String group, ConditionOptionCall<RangeFilterBuilder> opLambda) {
-        RangeFilterBuilder builder = regRangeF("group", ConditionKey.CK_GREATER_EQUAL, group);
+    public void setGroups_GreaterEqual(String groups, ConditionOptionCall<RangeFilterBuilder> opLambda) {
+        RangeFilterBuilder builder = regRangeF("groups", ConditionKey.CK_GREATER_EQUAL, groups);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setGroup_LessEqual(String group) {
-        setGroup_LessEqual(group, null);
+    public void setGroups_LessEqual(String groups) {
+        setGroups_LessEqual(groups, null);
     }
 
-    public void setGroup_LessEqual(String group, ConditionOptionCall<RangeFilterBuilder> opLambda) {
-        RangeFilterBuilder builder = regRangeF("group", ConditionKey.CK_LESS_EQUAL, group);
+    public void setGroups_LessEqual(String groups, ConditionOptionCall<RangeFilterBuilder> opLambda) {
+        RangeFilterBuilder builder = regRangeF("groups", ConditionKey.CK_LESS_EQUAL, groups);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
@@ -610,127 +610,127 @@ public abstract class BsUserCF extends AbstractConditionFilter {
         }
     }
 
-    public void setRole_NotEqual(String role) {
-        setRole_NotEqual(role, null, null);
+    public void setRoles_NotEqual(String roles) {
+        setRoles_NotEqual(roles, null, null);
     }
 
-    public void setRole_NotEqual(String role, ConditionOptionCall<NotFilterBuilder> notOpLambda,
+    public void setRoles_NotEqual(String roles, ConditionOptionCall<NotFilterBuilder> notOpLambda,
             ConditionOptionCall<TermFilterBuilder> eqOpLambda) {
         not(subCf -> {
-            subCf.setRole_Equal(role, eqOpLambda);
+            subCf.setRoles_Equal(roles, eqOpLambda);
         }, notOpLambda);
     }
 
-    public void setRole_Equal(String role) {
-        setRole_Term(role, null);
+    public void setRoles_Equal(String roles) {
+        setRoles_Term(roles, null);
     }
 
-    public void setRole_Equal(String role, ConditionOptionCall<TermFilterBuilder> opLambda) {
-        setRole_Term(role, opLambda);
+    public void setRoles_Equal(String roles, ConditionOptionCall<TermFilterBuilder> opLambda) {
+        setRoles_Term(roles, opLambda);
     }
 
-    public void setRole_Term(String role) {
-        setRole_Term(role, null);
+    public void setRoles_Term(String roles) {
+        setRoles_Term(roles, null);
     }
 
-    public void setRole_Term(String role, ConditionOptionCall<TermFilterBuilder> opLambda) {
-        TermFilterBuilder builder = regTermF("role", role);
+    public void setRoles_Term(String roles, ConditionOptionCall<TermFilterBuilder> opLambda) {
+        TermFilterBuilder builder = regTermF("roles", roles);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setRole_Terms(Collection<String> roleList) {
-        setRole_Terms(roleList, null);
+    public void setRoles_Terms(Collection<String> rolesList) {
+        setRoles_Terms(rolesList, null);
     }
 
-    public void setRole_Terms(Collection<String> roleList, ConditionOptionCall<TermsFilterBuilder> opLambda) {
-        TermsFilterBuilder builder = regTermsF("role", roleList);
+    public void setRoles_Terms(Collection<String> rolesList, ConditionOptionCall<TermsFilterBuilder> opLambda) {
+        TermsFilterBuilder builder = regTermsF("roles", rolesList);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setRole_InScope(Collection<String> roleList) {
-        setRole_Terms(roleList, null);
+    public void setRoles_InScope(Collection<String> rolesList) {
+        setRoles_Terms(rolesList, null);
     }
 
-    public void setRole_InScope(Collection<String> roleList, ConditionOptionCall<TermsFilterBuilder> opLambda) {
-        setRole_Terms(roleList, opLambda);
+    public void setRoles_InScope(Collection<String> rolesList, ConditionOptionCall<TermsFilterBuilder> opLambda) {
+        setRoles_Terms(rolesList, opLambda);
     }
 
-    public void setRole_Prefix(String role) {
-        setRole_Prefix(role, null);
+    public void setRoles_Prefix(String roles) {
+        setRoles_Prefix(roles, null);
     }
 
-    public void setRole_Prefix(String role, ConditionOptionCall<PrefixFilterBuilder> opLambda) {
-        PrefixFilterBuilder builder = regPrefixF("role", role);
+    public void setRoles_Prefix(String roles, ConditionOptionCall<PrefixFilterBuilder> opLambda) {
+        PrefixFilterBuilder builder = regPrefixF("roles", roles);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setRole_Exists() {
-        setRole_Exists(null);
+    public void setRoles_Exists() {
+        setRoles_Exists(null);
     }
 
-    public void setRole_Exists(ConditionOptionCall<ExistsFilterBuilder> opLambda) {
-        ExistsFilterBuilder builder = regExistsF("role");
+    public void setRoles_Exists(ConditionOptionCall<ExistsFilterBuilder> opLambda) {
+        ExistsFilterBuilder builder = regExistsF("roles");
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setRole_Missing() {
-        setRole_Missing(null);
+    public void setRoles_Missing() {
+        setRoles_Missing(null);
     }
 
-    public void setRole_Missing(ConditionOptionCall<MissingFilterBuilder> opLambda) {
-        MissingFilterBuilder builder = regMissingF("role");
+    public void setRoles_Missing(ConditionOptionCall<MissingFilterBuilder> opLambda) {
+        MissingFilterBuilder builder = regMissingF("roles");
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setRole_GreaterThan(String role) {
-        setRole_GreaterThan(role, null);
+    public void setRoles_GreaterThan(String roles) {
+        setRoles_GreaterThan(roles, null);
     }
 
-    public void setRole_GreaterThan(String role, ConditionOptionCall<RangeFilterBuilder> opLambda) {
-        RangeFilterBuilder builder = regRangeF("role", ConditionKey.CK_GREATER_THAN, role);
+    public void setRoles_GreaterThan(String roles, ConditionOptionCall<RangeFilterBuilder> opLambda) {
+        RangeFilterBuilder builder = regRangeF("roles", ConditionKey.CK_GREATER_THAN, roles);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setRole_LessThan(String role) {
-        setRole_LessThan(role, null);
+    public void setRoles_LessThan(String roles) {
+        setRoles_LessThan(roles, null);
     }
 
-    public void setRole_LessThan(String role, ConditionOptionCall<RangeFilterBuilder> opLambda) {
-        RangeFilterBuilder builder = regRangeF("role", ConditionKey.CK_LESS_THAN, role);
+    public void setRoles_LessThan(String roles, ConditionOptionCall<RangeFilterBuilder> opLambda) {
+        RangeFilterBuilder builder = regRangeF("roles", ConditionKey.CK_LESS_THAN, roles);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setRole_GreaterEqual(String role) {
-        setRole_GreaterEqual(role, null);
+    public void setRoles_GreaterEqual(String roles) {
+        setRoles_GreaterEqual(roles, null);
     }
 
-    public void setRole_GreaterEqual(String role, ConditionOptionCall<RangeFilterBuilder> opLambda) {
-        RangeFilterBuilder builder = regRangeF("role", ConditionKey.CK_GREATER_EQUAL, role);
+    public void setRoles_GreaterEqual(String roles, ConditionOptionCall<RangeFilterBuilder> opLambda) {
+        RangeFilterBuilder builder = regRangeF("roles", ConditionKey.CK_GREATER_EQUAL, roles);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setRole_LessEqual(String role) {
-        setRole_LessEqual(role, null);
+    public void setRoles_LessEqual(String roles) {
+        setRoles_LessEqual(roles, null);
     }
 
-    public void setRole_LessEqual(String role, ConditionOptionCall<RangeFilterBuilder> opLambda) {
-        RangeFilterBuilder builder = regRangeF("role", ConditionKey.CK_LESS_EQUAL, role);
+    public void setRoles_LessEqual(String roles, ConditionOptionCall<RangeFilterBuilder> opLambda) {
+        RangeFilterBuilder builder = regRangeF("roles", ConditionKey.CK_LESS_EQUAL, roles);
         if (opLambda != null) {
             opLambda.callback(builder);
         }

+ 112 - 112
src/main/java/org/codelibs/fess/es/cbean/cq/bs/BsUserCQ.java

@@ -62,150 +62,150 @@ public abstract class BsUserCQ extends AbstractConditionQuery {
         }
     }
 
-    public void setGroup_Equal(String group) {
-        setGroup_Term(group, null);
+    public void setGroups_Equal(String groups) {
+        setGroups_Term(groups, null);
     }
 
-    public void setGroup_Equal(String group, ConditionOptionCall<TermQueryBuilder> opLambda) {
-        setGroup_Term(group, opLambda);
+    public void setGroups_Equal(String groups, ConditionOptionCall<TermQueryBuilder> opLambda) {
+        setGroups_Term(groups, opLambda);
     }
 
-    public void setGroup_Term(String group) {
-        setGroup_Term(group, null);
+    public void setGroups_Term(String groups) {
+        setGroups_Term(groups, null);
     }
 
-    public void setGroup_Term(String group, ConditionOptionCall<TermQueryBuilder> opLambda) {
-        TermQueryBuilder builder = regTermQ("group", group);
+    public void setGroups_Term(String groups, ConditionOptionCall<TermQueryBuilder> opLambda) {
+        TermQueryBuilder builder = regTermQ("groups", groups);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setGroup_Terms(Collection<String> groupList) {
-        setGroup_Terms(groupList, null);
+    public void setGroups_Terms(Collection<String> groupsList) {
+        setGroups_Terms(groupsList, null);
     }
 
-    public void setGroup_Terms(Collection<String> groupList, ConditionOptionCall<TermsQueryBuilder> opLambda) {
-        TermsQueryBuilder builder = regTermsQ("group", groupList);
+    public void setGroups_Terms(Collection<String> groupsList, ConditionOptionCall<TermsQueryBuilder> opLambda) {
+        TermsQueryBuilder builder = regTermsQ("groups", groupsList);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setGroup_InScope(Collection<String> groupList) {
-        setGroup_Terms(groupList, null);
+    public void setGroups_InScope(Collection<String> groupsList) {
+        setGroups_Terms(groupsList, null);
     }
 
-    public void setGroup_InScope(Collection<String> groupList, ConditionOptionCall<TermsQueryBuilder> opLambda) {
-        setGroup_Terms(groupList, opLambda);
+    public void setGroups_InScope(Collection<String> groupsList, ConditionOptionCall<TermsQueryBuilder> opLambda) {
+        setGroups_Terms(groupsList, opLambda);
     }
 
-    public void setGroup_Match(String group) {
-        setGroup_Match(group, null);
+    public void setGroups_Match(String groups) {
+        setGroups_Match(groups, null);
     }
 
-    public void setGroup_Match(String group, ConditionOptionCall<MatchQueryBuilder> opLambda) {
-        MatchQueryBuilder builder = regMatchQ("group", group);
+    public void setGroups_Match(String groups, ConditionOptionCall<MatchQueryBuilder> opLambda) {
+        MatchQueryBuilder builder = regMatchQ("groups", groups);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setGroup_MatchPhrase(String group) {
-        setGroup_MatchPhrase(group, null);
+    public void setGroups_MatchPhrase(String groups) {
+        setGroups_MatchPhrase(groups, null);
     }
 
-    public void setGroup_MatchPhrase(String group, ConditionOptionCall<MatchQueryBuilder> opLambda) {
-        MatchQueryBuilder builder = regMatchPhraseQ("group", group);
+    public void setGroups_MatchPhrase(String groups, ConditionOptionCall<MatchQueryBuilder> opLambda) {
+        MatchQueryBuilder builder = regMatchPhraseQ("groups", groups);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setGroup_MatchPhrasePrefix(String group) {
-        setGroup_MatchPhrasePrefix(group, null);
+    public void setGroups_MatchPhrasePrefix(String groups) {
+        setGroups_MatchPhrasePrefix(groups, null);
     }
 
-    public void setGroup_MatchPhrasePrefix(String group, ConditionOptionCall<MatchQueryBuilder> opLambda) {
-        MatchQueryBuilder builder = regMatchPhrasePrefixQ("group", group);
+    public void setGroups_MatchPhrasePrefix(String groups, ConditionOptionCall<MatchQueryBuilder> opLambda) {
+        MatchQueryBuilder builder = regMatchPhrasePrefixQ("groups", groups);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setGroup_Fuzzy(String group) {
-        setGroup_Fuzzy(group, null);
+    public void setGroups_Fuzzy(String groups) {
+        setGroups_Fuzzy(groups, null);
     }
 
-    public void setGroup_Fuzzy(String group, ConditionOptionCall<FuzzyQueryBuilder> opLambda) {
-        FuzzyQueryBuilder builder = regFuzzyQ("group", group);
+    public void setGroups_Fuzzy(String groups, ConditionOptionCall<FuzzyQueryBuilder> opLambda) {
+        FuzzyQueryBuilder builder = regFuzzyQ("groups", groups);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setGroup_Prefix(String group) {
-        setGroup_Prefix(group, null);
+    public void setGroups_Prefix(String groups) {
+        setGroups_Prefix(groups, null);
     }
 
-    public void setGroup_Prefix(String group, ConditionOptionCall<PrefixQueryBuilder> opLambda) {
-        PrefixQueryBuilder builder = regPrefixQ("group", group);
+    public void setGroups_Prefix(String groups, ConditionOptionCall<PrefixQueryBuilder> opLambda) {
+        PrefixQueryBuilder builder = regPrefixQ("groups", groups);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setGroup_GreaterThan(String group) {
-        setGroup_GreaterThan(group, null);
+    public void setGroups_GreaterThan(String groups) {
+        setGroups_GreaterThan(groups, null);
     }
 
-    public void setGroup_GreaterThan(String group, ConditionOptionCall<RangeQueryBuilder> opLambda) {
-        RangeQueryBuilder builder = regRangeQ("group", ConditionKey.CK_GREATER_THAN, group);
+    public void setGroups_GreaterThan(String groups, ConditionOptionCall<RangeQueryBuilder> opLambda) {
+        RangeQueryBuilder builder = regRangeQ("groups", ConditionKey.CK_GREATER_THAN, groups);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setGroup_LessThan(String group) {
-        setGroup_LessThan(group, null);
+    public void setGroups_LessThan(String groups) {
+        setGroups_LessThan(groups, null);
     }
 
-    public void setGroup_LessThan(String group, ConditionOptionCall<RangeQueryBuilder> opLambda) {
-        RangeQueryBuilder builder = regRangeQ("group", ConditionKey.CK_LESS_THAN, group);
+    public void setGroups_LessThan(String groups, ConditionOptionCall<RangeQueryBuilder> opLambda) {
+        RangeQueryBuilder builder = regRangeQ("groups", ConditionKey.CK_LESS_THAN, groups);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setGroup_GreaterEqual(String group) {
-        setGroup_GreaterEqual(group, null);
+    public void setGroups_GreaterEqual(String groups) {
+        setGroups_GreaterEqual(groups, null);
     }
 
-    public void setGroup_GreaterEqual(String group, ConditionOptionCall<RangeQueryBuilder> opLambda) {
-        RangeQueryBuilder builder = regRangeQ("group", ConditionKey.CK_GREATER_EQUAL, group);
+    public void setGroups_GreaterEqual(String groups, ConditionOptionCall<RangeQueryBuilder> opLambda) {
+        RangeQueryBuilder builder = regRangeQ("groups", ConditionKey.CK_GREATER_EQUAL, groups);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setGroup_LessEqual(String group) {
-        setGroup_LessEqual(group, null);
+    public void setGroups_LessEqual(String groups) {
+        setGroups_LessEqual(groups, null);
     }
 
-    public void setGroup_LessEqual(String group, ConditionOptionCall<RangeQueryBuilder> opLambda) {
-        RangeQueryBuilder builder = regRangeQ("group", ConditionKey.CK_LESS_EQUAL, group);
+    public void setGroups_LessEqual(String groups, ConditionOptionCall<RangeQueryBuilder> opLambda) {
+        RangeQueryBuilder builder = regRangeQ("groups", ConditionKey.CK_LESS_EQUAL, groups);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public BsUserCQ addOrderBy_Group_Asc() {
-        regOBA("group");
+    public BsUserCQ addOrderBy_Groups_Asc() {
+        regOBA("groups");
         return this;
     }
 
-    public BsUserCQ addOrderBy_Group_Desc() {
-        regOBD("group");
+    public BsUserCQ addOrderBy_Groups_Desc() {
+        regOBD("groups");
         return this;
     }
 
@@ -650,150 +650,150 @@ public abstract class BsUserCQ extends AbstractConditionQuery {
         return this;
     }
 
-    public void setRole_Equal(String role) {
-        setRole_Term(role, null);
+    public void setRoles_Equal(String roles) {
+        setRoles_Term(roles, null);
     }
 
-    public void setRole_Equal(String role, ConditionOptionCall<TermQueryBuilder> opLambda) {
-        setRole_Term(role, opLambda);
+    public void setRoles_Equal(String roles, ConditionOptionCall<TermQueryBuilder> opLambda) {
+        setRoles_Term(roles, opLambda);
     }
 
-    public void setRole_Term(String role) {
-        setRole_Term(role, null);
+    public void setRoles_Term(String roles) {
+        setRoles_Term(roles, null);
     }
 
-    public void setRole_Term(String role, ConditionOptionCall<TermQueryBuilder> opLambda) {
-        TermQueryBuilder builder = regTermQ("role", role);
+    public void setRoles_Term(String roles, ConditionOptionCall<TermQueryBuilder> opLambda) {
+        TermQueryBuilder builder = regTermQ("roles", roles);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setRole_Terms(Collection<String> roleList) {
-        setRole_Terms(roleList, null);
+    public void setRoles_Terms(Collection<String> rolesList) {
+        setRoles_Terms(rolesList, null);
     }
 
-    public void setRole_Terms(Collection<String> roleList, ConditionOptionCall<TermsQueryBuilder> opLambda) {
-        TermsQueryBuilder builder = regTermsQ("role", roleList);
+    public void setRoles_Terms(Collection<String> rolesList, ConditionOptionCall<TermsQueryBuilder> opLambda) {
+        TermsQueryBuilder builder = regTermsQ("roles", rolesList);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setRole_InScope(Collection<String> roleList) {
-        setRole_Terms(roleList, null);
+    public void setRoles_InScope(Collection<String> rolesList) {
+        setRoles_Terms(rolesList, null);
     }
 
-    public void setRole_InScope(Collection<String> roleList, ConditionOptionCall<TermsQueryBuilder> opLambda) {
-        setRole_Terms(roleList, opLambda);
+    public void setRoles_InScope(Collection<String> rolesList, ConditionOptionCall<TermsQueryBuilder> opLambda) {
+        setRoles_Terms(rolesList, opLambda);
     }
 
-    public void setRole_Match(String role) {
-        setRole_Match(role, null);
+    public void setRoles_Match(String roles) {
+        setRoles_Match(roles, null);
     }
 
-    public void setRole_Match(String role, ConditionOptionCall<MatchQueryBuilder> opLambda) {
-        MatchQueryBuilder builder = regMatchQ("role", role);
+    public void setRoles_Match(String roles, ConditionOptionCall<MatchQueryBuilder> opLambda) {
+        MatchQueryBuilder builder = regMatchQ("roles", roles);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setRole_MatchPhrase(String role) {
-        setRole_MatchPhrase(role, null);
+    public void setRoles_MatchPhrase(String roles) {
+        setRoles_MatchPhrase(roles, null);
     }
 
-    public void setRole_MatchPhrase(String role, ConditionOptionCall<MatchQueryBuilder> opLambda) {
-        MatchQueryBuilder builder = regMatchPhraseQ("role", role);
+    public void setRoles_MatchPhrase(String roles, ConditionOptionCall<MatchQueryBuilder> opLambda) {
+        MatchQueryBuilder builder = regMatchPhraseQ("roles", roles);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setRole_MatchPhrasePrefix(String role) {
-        setRole_MatchPhrasePrefix(role, null);
+    public void setRoles_MatchPhrasePrefix(String roles) {
+        setRoles_MatchPhrasePrefix(roles, null);
     }
 
-    public void setRole_MatchPhrasePrefix(String role, ConditionOptionCall<MatchQueryBuilder> opLambda) {
-        MatchQueryBuilder builder = regMatchPhrasePrefixQ("role", role);
+    public void setRoles_MatchPhrasePrefix(String roles, ConditionOptionCall<MatchQueryBuilder> opLambda) {
+        MatchQueryBuilder builder = regMatchPhrasePrefixQ("roles", roles);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setRole_Fuzzy(String role) {
-        setRole_Fuzzy(role, null);
+    public void setRoles_Fuzzy(String roles) {
+        setRoles_Fuzzy(roles, null);
     }
 
-    public void setRole_Fuzzy(String role, ConditionOptionCall<FuzzyQueryBuilder> opLambda) {
-        FuzzyQueryBuilder builder = regFuzzyQ("role", role);
+    public void setRoles_Fuzzy(String roles, ConditionOptionCall<FuzzyQueryBuilder> opLambda) {
+        FuzzyQueryBuilder builder = regFuzzyQ("roles", roles);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setRole_Prefix(String role) {
-        setRole_Prefix(role, null);
+    public void setRoles_Prefix(String roles) {
+        setRoles_Prefix(roles, null);
     }
 
-    public void setRole_Prefix(String role, ConditionOptionCall<PrefixQueryBuilder> opLambda) {
-        PrefixQueryBuilder builder = regPrefixQ("role", role);
+    public void setRoles_Prefix(String roles, ConditionOptionCall<PrefixQueryBuilder> opLambda) {
+        PrefixQueryBuilder builder = regPrefixQ("roles", roles);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setRole_GreaterThan(String role) {
-        setRole_GreaterThan(role, null);
+    public void setRoles_GreaterThan(String roles) {
+        setRoles_GreaterThan(roles, null);
     }
 
-    public void setRole_GreaterThan(String role, ConditionOptionCall<RangeQueryBuilder> opLambda) {
-        RangeQueryBuilder builder = regRangeQ("role", ConditionKey.CK_GREATER_THAN, role);
+    public void setRoles_GreaterThan(String roles, ConditionOptionCall<RangeQueryBuilder> opLambda) {
+        RangeQueryBuilder builder = regRangeQ("roles", ConditionKey.CK_GREATER_THAN, roles);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setRole_LessThan(String role) {
-        setRole_LessThan(role, null);
+    public void setRoles_LessThan(String roles) {
+        setRoles_LessThan(roles, null);
     }
 
-    public void setRole_LessThan(String role, ConditionOptionCall<RangeQueryBuilder> opLambda) {
-        RangeQueryBuilder builder = regRangeQ("role", ConditionKey.CK_LESS_THAN, role);
+    public void setRoles_LessThan(String roles, ConditionOptionCall<RangeQueryBuilder> opLambda) {
+        RangeQueryBuilder builder = regRangeQ("roles", ConditionKey.CK_LESS_THAN, roles);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setRole_GreaterEqual(String role) {
-        setRole_GreaterEqual(role, null);
+    public void setRoles_GreaterEqual(String roles) {
+        setRoles_GreaterEqual(roles, null);
     }
 
-    public void setRole_GreaterEqual(String role, ConditionOptionCall<RangeQueryBuilder> opLambda) {
-        RangeQueryBuilder builder = regRangeQ("role", ConditionKey.CK_GREATER_EQUAL, role);
+    public void setRoles_GreaterEqual(String roles, ConditionOptionCall<RangeQueryBuilder> opLambda) {
+        RangeQueryBuilder builder = regRangeQ("roles", ConditionKey.CK_GREATER_EQUAL, roles);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public void setRole_LessEqual(String role) {
-        setRole_LessEqual(role, null);
+    public void setRoles_LessEqual(String roles) {
+        setRoles_LessEqual(roles, null);
     }
 
-    public void setRole_LessEqual(String role, ConditionOptionCall<RangeQueryBuilder> opLambda) {
-        RangeQueryBuilder builder = regRangeQ("role", ConditionKey.CK_LESS_EQUAL, role);
+    public void setRoles_LessEqual(String roles, ConditionOptionCall<RangeQueryBuilder> opLambda) {
+        RangeQueryBuilder builder = regRangeQ("roles", ConditionKey.CK_LESS_EQUAL, roles);
         if (opLambda != null) {
             opLambda.callback(builder);
         }
     }
 
-    public BsUserCQ addOrderBy_Role_Asc() {
-        regOBA("role");
+    public BsUserCQ addOrderBy_Roles_Asc() {
+        regOBA("roles");
         return this;
     }
 
-    public BsUserCQ addOrderBy_Role_Desc() {
-        regOBD("role");
+    public BsUserCQ addOrderBy_Roles_Desc() {
+        regOBD("roles");
         return this;
     }
 

+ 1 - 2
src/main/java/org/codelibs/fess/helper/QueryHelper.java

@@ -40,7 +40,6 @@ import org.codelibs.fess.entity.GeoInfo;
 import org.codelibs.fess.entity.SearchQuery;
 import org.codelibs.fess.entity.SearchQuery.SortField;
 import org.codelibs.fess.util.QueryUtil;
-import org.codelibs.fess.util.SearchParamMap;
 import org.lastaflute.web.ruts.message.ActionMessages;
 import org.lastaflute.web.util.LaRequestUtil;
 
@@ -965,7 +964,7 @@ public class QueryHelper implements Serializable {
         return "count".equals(sort) || "index".equals(sort);
     }
 
-    public String buildOptionQuery(final SearchParamMap optionMap) {
+    public String buildOptionQuery(final Map<String, String[]> optionMap) {
         if (optionMap == null) {
             return StringUtil.EMPTY;
         }

+ 36 - 0
src/main/java/org/codelibs/fess/mylasta/action/FessHtmlPath.java

@@ -131,6 +131,18 @@ public interface FessHtmlPath {
     /** The path of the HTML: /admin/fileconfig/index.jsp */
     HtmlNext path_AdminFileconfig_IndexJsp = new HtmlNext("/admin/fileconfig/index.jsp");
 
+    /** The path of the HTML: /admin/group/confirm.jsp */
+    HtmlNext path_AdminGroup_ConfirmJsp = new HtmlNext("/admin/group/confirm.jsp");
+
+    /** The path of the HTML: /admin/group/edit.jsp */
+    HtmlNext path_AdminGroup_EditJsp = new HtmlNext("/admin/group/edit.jsp");
+
+    /** The path of the HTML: /admin/group/error.jsp */
+    HtmlNext path_AdminGroup_ErrorJsp = new HtmlNext("/admin/group/error.jsp");
+
+    /** The path of the HTML: /admin/group/index.jsp */
+    HtmlNext path_AdminGroup_IndexJsp = new HtmlNext("/admin/group/index.jsp");
+
     /** The path of the HTML: /admin/joblog/confirm.jsp */
     HtmlNext path_AdminJoblog_ConfirmJsp = new HtmlNext("/admin/joblog/confirm.jsp");
 
@@ -203,6 +215,18 @@ public interface FessHtmlPath {
     /** The path of the HTML: /admin/requestheader/index.jsp */
     HtmlNext path_AdminRequestheader_IndexJsp = new HtmlNext("/admin/requestheader/index.jsp");
 
+    /** The path of the HTML: /admin/role/confirm.jsp */
+    HtmlNext path_AdminRole_ConfirmJsp = new HtmlNext("/admin/role/confirm.jsp");
+
+    /** The path of the HTML: /admin/role/edit.jsp */
+    HtmlNext path_AdminRole_EditJsp = new HtmlNext("/admin/role/edit.jsp");
+
+    /** The path of the HTML: /admin/role/error.jsp */
+    HtmlNext path_AdminRole_ErrorJsp = new HtmlNext("/admin/role/error.jsp");
+
+    /** The path of the HTML: /admin/role/index.jsp */
+    HtmlNext path_AdminRole_IndexJsp = new HtmlNext("/admin/role/index.jsp");
+
     /** The path of the HTML: /admin/roletype/confirm.jsp */
     HtmlNext path_AdminRoletype_ConfirmJsp = new HtmlNext("/admin/roletype/confirm.jsp");
 
@@ -275,6 +299,18 @@ public interface FessHtmlPath {
     /** The path of the HTML: /admin/systeminfo/index.jsp */
     HtmlNext path_AdminSysteminfo_IndexJsp = new HtmlNext("/admin/systeminfo/index.jsp");
 
+    /** The path of the HTML: /admin/user/confirm.jsp */
+    HtmlNext path_AdminUser_ConfirmJsp = new HtmlNext("/admin/user/confirm.jsp");
+
+    /** The path of the HTML: /admin/user/edit.jsp */
+    HtmlNext path_AdminUser_EditJsp = new HtmlNext("/admin/user/edit.jsp");
+
+    /** The path of the HTML: /admin/user/error.jsp */
+    HtmlNext path_AdminUser_ErrorJsp = new HtmlNext("/admin/user/error.jsp");
+
+    /** The path of the HTML: /admin/user/index.jsp */
+    HtmlNext path_AdminUser_IndexJsp = new HtmlNext("/admin/user/index.jsp");
+
     /** The path of the HTML: /admin/webauthentication/confirm.jsp */
     HtmlNext path_AdminWebauthentication_ConfirmJsp = new HtmlNext("/admin/webauthentication/confirm.jsp");
 

+ 438 - 12
src/main/java/org/codelibs/fess/mylasta/action/FessLabels.java

@@ -566,6 +566,18 @@ public class FessLabels extends ActionMessages {
     /** The key of the message: &#x00bb; Role */
     public static final String LABELS_MENU_role_type = "{labels.menu.role_type}";
 
+    /** The key of the message: User */
+    public static final String LABELS_menu_user = "{labels.menu_user}";
+
+    /** The key of the message: User */
+    public static final String LABELS_MENU_USER = "{labels.menu.user}";
+
+    /** The key of the message: Role */
+    public static final String LABELS_MENU_ROLE = "{labels.menu.role}";
+
+    /** The key of the message: Group */
+    public static final String LABELS_MENU_GROUP = "{labels.menu.group}";
+
     /** The key of the message: Suggest */
     public static final String LABELS_menu_suggest = "{labels.menu_suggest}";
 
@@ -605,9 +617,6 @@ public class FessLabels extends ActionMessages {
     /** The key of the message: &#x00bb; Statistics */
     public static final String LABELS_MENU_STATS = "{labels.menu.stats}";
 
-    /** The key of the message: &#x00bb; Users */
-    public static final String LABELS_MENU_USER = "{labels.menu.user}";
-
     /** The key of the message: &#x00bb; Popular URL */
     public static final String LABELS_MENU_FAVORITE_LOG = "{labels.menu.favoriteLog}";
 
@@ -767,9 +776,6 @@ public class FessLabels extends ActionMessages {
     /** The key of the message: Home */
     public static final String LABELS_HOME = "{labels.home}";
 
-    /** The key of the message: User Name */
-    public static final String LABELS_user_name = "{labels.user_name}";
-
     /** The key of the message: Login */
     public static final String LABELS_LOGIN = "{labels.login}";
 
@@ -3271,6 +3277,213 @@ public class FessLabels extends ActionMessages {
     /** The key of the message: Bad Word File */
     public static final String LABELS_suggest_bad_word_file = "{labels.suggest_bad_word_file}";
 
+    /** The key of the message: User */
+    public static final String LABELS_user_configuration = "{labels.user_configuration}";
+
+    /** The key of the message: Create New */
+    public static final String LABELS_user_link_create_new = "{labels.user_link_create_new}";
+
+    /** The key of the message: List */
+    public static final String LABELS_user_link_list = "{labels.user_link_list}";
+
+    /** The key of the message: Create New */
+    public static final String LABELS_user_link_create = "{labels.user_link_create}";
+
+    /** The key of the message: Edit */
+    public static final String LABELS_user_link_update = "{labels.user_link_update}";
+
+    /** The key of the message: Details */
+    public static final String LABELS_user_link_confirm = "{labels.user_link_confirm}";
+
+    /** The key of the message: Details */
+    public static final String LABELS_user_link_details = "{labels.user_link_details}";
+
+    /** The key of the message: Edit */
+    public static final String LABELS_user_link_edit = "{labels.user_link_edit}";
+
+    /** The key of the message: Delete */
+    public static final String LABELS_user_link_delete = "{labels.user_link_delete}";
+
+    /** The key of the message: Prev */
+    public static final String LABELS_user_link_prev_page = "{labels.user_link_prev_page}";
+
+    /** The key of the message: Next */
+    public static final String LABELS_user_link_next_page = "{labels.user_link_next_page}";
+
+    /** The key of the message: Name */
+    public static final String LABELS_user_list_name = "{labels.user_list_name}";
+
+    /** The key of the message: Name */
+    public static final String LABELS_user_name = "{labels.user_name}";
+
+    /** The key of the message: Password */
+    public static final String LABELS_user_password = "{labels.user_password}";
+
+    /** The key of the message: Confirm */
+    public static final String LABELS_user_confirm_password = "{labels.user_confirm_password}";
+
+    /** The key of the message: Role */
+    public static final String LABELS_user_role = "{labels.user_role}";
+
+    /** The key of the message: Group */
+    public static final String LABELS_user_group = "{labels.user_group}";
+
+    /** The key of the message: User */
+    public static final String LABELS_user_title_details = "{labels.user_title_details}";
+
+    /** The key of the message: Create */
+    public static final String LABELS_user_button_create = "{labels.user_button_create}";
+
+    /** The key of the message: Back */
+    public static final String LABELS_user_button_back = "{labels.user_button_back}";
+
+    /** The key of the message: Confirm */
+    public static final String LABELS_user_button_confirm = "{labels.user_button_confirm}";
+
+    /** The key of the message: Confirm User */
+    public static final String LABELS_user_title_confirm = "{labels.user_title_confirm}";
+
+    /** The key of the message: Update */
+    public static final String LABELS_user_button_update = "{labels.user_button_update}";
+
+    /** The key of the message: Delete */
+    public static final String LABELS_user_button_delete = "{labels.user_button_delete}";
+
+    /** The key of the message: Edit */
+    public static final String LABELS_user_button_edit = "{labels.user_button_edit}";
+
+    /** The key of the message: Role */
+    public static final String LABELS_role_configuration = "{labels.role_configuration}";
+
+    /** The key of the message: Create New */
+    public static final String LABELS_role_link_create_new = "{labels.role_link_create_new}";
+
+    /** The key of the message: List */
+    public static final String LABELS_role_link_list = "{labels.role_link_list}";
+
+    /** The key of the message: Create New */
+    public static final String LABELS_role_link_create = "{labels.role_link_create}";
+
+    /** The key of the message: Edit */
+    public static final String LABELS_role_link_update = "{labels.role_link_update}";
+
+    /** The key of the message: Details */
+    public static final String LABELS_role_link_confirm = "{labels.role_link_confirm}";
+
+    /** The key of the message: Details */
+    public static final String LABELS_role_link_details = "{labels.role_link_details}";
+
+    /** The key of the message: Edit */
+    public static final String LABELS_role_link_edit = "{labels.role_link_edit}";
+
+    /** The key of the message: Delete */
+    public static final String LABELS_role_link_delete = "{labels.role_link_delete}";
+
+    /** The key of the message: Prev */
+    public static final String LABELS_role_link_prev_page = "{labels.role_link_prev_page}";
+
+    /** The key of the message: Next */
+    public static final String LABELS_role_link_next_page = "{labels.role_link_next_page}";
+
+    /** The key of the message: Name */
+    public static final String LABELS_role_list_name = "{labels.role_list_name}";
+
+    /** The key of the message: Name */
+    public static final String LABELS_role_name = "{labels.role_name}";
+
+    /** The key of the message: Role */
+    public static final String LABELS_role_title_details = "{labels.role_title_details}";
+
+    /** The key of the message: Create */
+    public static final String LABELS_role_button_create = "{labels.role_button_create}";
+
+    /** The key of the message: Back */
+    public static final String LABELS_role_button_back = "{labels.role_button_back}";
+
+    /** The key of the message: Confirm */
+    public static final String LABELS_role_button_confirm = "{labels.role_button_confirm}";
+
+    /** The key of the message: Confirm Role */
+    public static final String LABELS_role_title_confirm = "{labels.role_title_confirm}";
+
+    /** The key of the message: Update */
+    public static final String LABELS_role_button_update = "{labels.role_button_update}";
+
+    /** The key of the message: Delete */
+    public static final String LABELS_role_button_delete = "{labels.role_button_delete}";
+
+    /** The key of the message: Edit */
+    public static final String LABELS_role_button_edit = "{labels.role_button_edit}";
+
+    /** The key of the message: group */
+    public static final String LABELS_group_configuration = "{labels.group_configuration}";
+
+    /** The key of the message: Create New */
+    public static final String LABELS_group_link_create_new = "{labels.group_link_create_new}";
+
+    /** The key of the message: List */
+    public static final String LABELS_group_link_list = "{labels.group_link_list}";
+
+    /** The key of the message: Create New */
+    public static final String LABELS_group_link_create = "{labels.group_link_create}";
+
+    /** The key of the message: Edit */
+    public static final String LABELS_group_link_update = "{labels.group_link_update}";
+
+    /** The key of the message: Details */
+    public static final String LABELS_group_link_confirm = "{labels.group_link_confirm}";
+
+    /** The key of the message: Details */
+    public static final String LABELS_group_link_details = "{labels.group_link_details}";
+
+    /** The key of the message: Edit */
+    public static final String LABELS_group_link_edit = "{labels.group_link_edit}";
+
+    /** The key of the message: Delete */
+    public static final String LABELS_group_link_delete = "{labels.group_link_delete}";
+
+    /** The key of the message: Prev */
+    public static final String LABELS_group_link_prev_page = "{labels.group_link_prev_page}";
+
+    /** The key of the message: Next */
+    public static final String LABELS_group_link_next_page = "{labels.group_link_next_page}";
+
+    /** The key of the message: Name */
+    public static final String LABELS_group_list_name = "{labels.group_list_name}";
+
+    /** The key of the message: Name */
+    public static final String LABELS_group_name = "{labels.group_name}";
+
+    /** The key of the message: Group */
+    public static final String LABELS_group_title_details = "{labels.group_title_details}";
+
+    /** The key of the message: Create */
+    public static final String LABELS_group_button_create = "{labels.group_button_create}";
+
+    /** The key of the message: Back */
+    public static final String LABELS_group_button_back = "{labels.group_button_back}";
+
+    /** The key of the message: Confirm */
+    public static final String LABELS_group_button_confirm = "{labels.group_button_confirm}";
+
+    /** The key of the message: Confirm Group */
+    public static final String LABELS_group_title_confirm = "{labels.group_title_confirm}";
+
+    /** The key of the message: Update */
+    public static final String LABELS_group_button_update = "{labels.group_button_update}";
+
+    /** The key of the message: Delete */
+    public static final String LABELS_group_button_delete = "{labels.group_button_delete}";
+
+    /** The key of the message: Edit */
+    public static final String LABELS_group_button_edit = "{labels.group_button_edit}";
+
+    /** The key of the message: Roles */
+    public static final String LABELS_ROLES = "{labels.roles}";
+
+    /** The key of the message: Groups */
+    public static final String LABELS_GROUPS = "{labels.groups}";
+
     /** The key of the message: Create */
     public static final String LABELS_crud_button_create = "{labels.crud_button_create}";
 
@@ -3894,6 +4107,18 @@ public class FessLabels extends ActionMessages {
         /** The key of the label: &#x00bb; Role */
         String LABELS_MENU_role_type = "{labels.menu.role_type}";
 
+        /** The key of the label: User */
+        String LABELS_menu_user = "{labels.menu_user}";
+
+        /** The key of the label: User */
+        String LABELS_MENU_USER = "{labels.menu.user}";
+
+        /** The key of the label: Role */
+        String LABELS_MENU_ROLE = "{labels.menu.role}";
+
+        /** The key of the label: Group */
+        String LABELS_MENU_GROUP = "{labels.menu.group}";
+
         /** The key of the label: Suggest */
         String LABELS_menu_suggest = "{labels.menu_suggest}";
 
@@ -3933,9 +4158,6 @@ public class FessLabels extends ActionMessages {
         /** The key of the label: &#x00bb; Statistics */
         String LABELS_MENU_STATS = "{labels.menu.stats}";
 
-        /** The key of the label: &#x00bb; Users */
-        String LABELS_MENU_USER = "{labels.menu.user}";
-
         /** The key of the label: &#x00bb; Popular URL */
         String LABELS_MENU_FAVORITE_LOG = "{labels.menu.favoriteLog}";
 
@@ -4095,9 +4317,6 @@ public class FessLabels extends ActionMessages {
         /** The key of the label: Home */
         String LABELS_HOME = "{labels.home}";
 
-        /** The key of the label: User Name */
-        String LABELS_user_name = "{labels.user_name}";
-
         /** The key of the label: Login */
         String LABELS_LOGIN = "{labels.login}";
 
@@ -6597,6 +6816,213 @@ public class FessLabels extends ActionMessages {
         /** The key of the label: Bad Word File */
         String LABELS_suggest_bad_word_file = "{labels.suggest_bad_word_file}";
 
+        /** The key of the label: User */
+        String LABELS_user_configuration = "{labels.user_configuration}";
+
+        /** The key of the label: Create New */
+        String LABELS_user_link_create_new = "{labels.user_link_create_new}";
+
+        /** The key of the label: List */
+        String LABELS_user_link_list = "{labels.user_link_list}";
+
+        /** The key of the label: Create New */
+        String LABELS_user_link_create = "{labels.user_link_create}";
+
+        /** The key of the label: Edit */
+        String LABELS_user_link_update = "{labels.user_link_update}";
+
+        /** The key of the label: Details */
+        String LABELS_user_link_confirm = "{labels.user_link_confirm}";
+
+        /** The key of the label: Details */
+        String LABELS_user_link_details = "{labels.user_link_details}";
+
+        /** The key of the label: Edit */
+        String LABELS_user_link_edit = "{labels.user_link_edit}";
+
+        /** The key of the label: Delete */
+        String LABELS_user_link_delete = "{labels.user_link_delete}";
+
+        /** The key of the label: Prev */
+        String LABELS_user_link_prev_page = "{labels.user_link_prev_page}";
+
+        /** The key of the label: Next */
+        String LABELS_user_link_next_page = "{labels.user_link_next_page}";
+
+        /** The key of the label: Name */
+        String LABELS_user_list_name = "{labels.user_list_name}";
+
+        /** The key of the label: Name */
+        String LABELS_user_name = "{labels.user_name}";
+
+        /** The key of the label: Password */
+        String LABELS_user_password = "{labels.user_password}";
+
+        /** The key of the label: Confirm */
+        String LABELS_user_confirm_password = "{labels.user_confirm_password}";
+
+        /** The key of the label: Role */
+        String LABELS_user_role = "{labels.user_role}";
+
+        /** The key of the label: Group */
+        String LABELS_user_group = "{labels.user_group}";
+
+        /** The key of the label: User */
+        String LABELS_user_title_details = "{labels.user_title_details}";
+
+        /** The key of the label: Create */
+        String LABELS_user_button_create = "{labels.user_button_create}";
+
+        /** The key of the label: Back */
+        String LABELS_user_button_back = "{labels.user_button_back}";
+
+        /** The key of the label: Confirm */
+        String LABELS_user_button_confirm = "{labels.user_button_confirm}";
+
+        /** The key of the label: Confirm User */
+        String LABELS_user_title_confirm = "{labels.user_title_confirm}";
+
+        /** The key of the label: Update */
+        String LABELS_user_button_update = "{labels.user_button_update}";
+
+        /** The key of the label: Delete */
+        String LABELS_user_button_delete = "{labels.user_button_delete}";
+
+        /** The key of the label: Edit */
+        String LABELS_user_button_edit = "{labels.user_button_edit}";
+
+        /** The key of the label: Role */
+        String LABELS_role_configuration = "{labels.role_configuration}";
+
+        /** The key of the label: Create New */
+        String LABELS_role_link_create_new = "{labels.role_link_create_new}";
+
+        /** The key of the label: List */
+        String LABELS_role_link_list = "{labels.role_link_list}";
+
+        /** The key of the label: Create New */
+        String LABELS_role_link_create = "{labels.role_link_create}";
+
+        /** The key of the label: Edit */
+        String LABELS_role_link_update = "{labels.role_link_update}";
+
+        /** The key of the label: Details */
+        String LABELS_role_link_confirm = "{labels.role_link_confirm}";
+
+        /** The key of the label: Details */
+        String LABELS_role_link_details = "{labels.role_link_details}";
+
+        /** The key of the label: Edit */
+        String LABELS_role_link_edit = "{labels.role_link_edit}";
+
+        /** The key of the label: Delete */
+        String LABELS_role_link_delete = "{labels.role_link_delete}";
+
+        /** The key of the label: Prev */
+        String LABELS_role_link_prev_page = "{labels.role_link_prev_page}";
+
+        /** The key of the label: Next */
+        String LABELS_role_link_next_page = "{labels.role_link_next_page}";
+
+        /** The key of the label: Name */
+        String LABELS_role_list_name = "{labels.role_list_name}";
+
+        /** The key of the label: Name */
+        String LABELS_role_name = "{labels.role_name}";
+
+        /** The key of the label: Role */
+        String LABELS_role_title_details = "{labels.role_title_details}";
+
+        /** The key of the label: Create */
+        String LABELS_role_button_create = "{labels.role_button_create}";
+
+        /** The key of the label: Back */
+        String LABELS_role_button_back = "{labels.role_button_back}";
+
+        /** The key of the label: Confirm */
+        String LABELS_role_button_confirm = "{labels.role_button_confirm}";
+
+        /** The key of the label: Confirm Role */
+        String LABELS_role_title_confirm = "{labels.role_title_confirm}";
+
+        /** The key of the label: Update */
+        String LABELS_role_button_update = "{labels.role_button_update}";
+
+        /** The key of the label: Delete */
+        String LABELS_role_button_delete = "{labels.role_button_delete}";
+
+        /** The key of the label: Edit */
+        String LABELS_role_button_edit = "{labels.role_button_edit}";
+
+        /** The key of the label: group */
+        String LABELS_group_configuration = "{labels.group_configuration}";
+
+        /** The key of the label: Create New */
+        String LABELS_group_link_create_new = "{labels.group_link_create_new}";
+
+        /** The key of the label: List */
+        String LABELS_group_link_list = "{labels.group_link_list}";
+
+        /** The key of the label: Create New */
+        String LABELS_group_link_create = "{labels.group_link_create}";
+
+        /** The key of the label: Edit */
+        String LABELS_group_link_update = "{labels.group_link_update}";
+
+        /** The key of the label: Details */
+        String LABELS_group_link_confirm = "{labels.group_link_confirm}";
+
+        /** The key of the label: Details */
+        String LABELS_group_link_details = "{labels.group_link_details}";
+
+        /** The key of the label: Edit */
+        String LABELS_group_link_edit = "{labels.group_link_edit}";
+
+        /** The key of the label: Delete */
+        String LABELS_group_link_delete = "{labels.group_link_delete}";
+
+        /** The key of the label: Prev */
+        String LABELS_group_link_prev_page = "{labels.group_link_prev_page}";
+
+        /** The key of the label: Next */
+        String LABELS_group_link_next_page = "{labels.group_link_next_page}";
+
+        /** The key of the label: Name */
+        String LABELS_group_list_name = "{labels.group_list_name}";
+
+        /** The key of the label: Name */
+        String LABELS_group_name = "{labels.group_name}";
+
+        /** The key of the label: Group */
+        String LABELS_group_title_details = "{labels.group_title_details}";
+
+        /** The key of the label: Create */
+        String LABELS_group_button_create = "{labels.group_button_create}";
+
+        /** The key of the label: Back */
+        String LABELS_group_button_back = "{labels.group_button_back}";
+
+        /** The key of the label: Confirm */
+        String LABELS_group_button_confirm = "{labels.group_button_confirm}";
+
+        /** The key of the label: Confirm Group */
+        String LABELS_group_title_confirm = "{labels.group_title_confirm}";
+
+        /** The key of the label: Update */
+        String LABELS_group_button_update = "{labels.group_button_update}";
+
+        /** The key of the label: Delete */
+        String LABELS_group_button_delete = "{labels.group_button_delete}";
+
+        /** The key of the label: Edit */
+        String LABELS_group_button_edit = "{labels.group_button_edit}";
+
+        /** The key of the label: Roles */
+        String LABELS_ROLES = "{labels.roles}";
+
+        /** The key of the label: Groups */
+        String LABELS_GROUPS = "{labels.groups}";
+
         /** The key of the label: Create */
         String LABELS_crud_button_create = "{labels.crud_button_create}";
 

+ 51 - 0
src/main/java/org/codelibs/fess/mylasta/action/FessMessages.java

@@ -354,6 +354,15 @@ public class FessMessages extends FessLabels {
     /** The key of the message: Failed to upload the UserDict file. */
     public static final String ERRORS_failed_to_upload_userdict_file = "{errors.failed_to_upload_userdict_file}";
 
+    /** The key of the message: Password is required. */
+    public static final String ERRORS_blank_password = "{errors.blank_password}";
+
+    /** The key of the message: Confirm Password does not match. */
+    public static final String ERRORS_invalid_confirm_password = "{errors.invalid_confirm_password}";
+
+    /** The key of the message: Invalid password. */
+    public static final String ERRORS_password_does_not_exist_in_session = "{errors.password_does_not_exist_in_session}";
+
     /** The key of the message: The given query is invalid. */
     public static final String ERRORS_invalid_query_unknown = "{errors.invalid_query_unknown}";
 
@@ -2207,6 +2216,48 @@ public class FessMessages extends FessLabels {
         return this;
     }
 
+    /**
+     * Add the created action message for the key 'errors.blank_password' with parameters.
+     * <pre>
+     * message: Password is required.
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     * @return this. (NotNull)
+     */
+    public FessMessages addErrorsBlankPassword(String property) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_blank_password));
+        return this;
+    }
+
+    /**
+     * Add the created action message for the key 'errors.invalid_confirm_password' with parameters.
+     * <pre>
+     * message: Confirm Password does not match.
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     * @return this. (NotNull)
+     */
+    public FessMessages addErrorsInvalidConfirmPassword(String property) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_invalid_confirm_password));
+        return this;
+    }
+
+    /**
+     * Add the created action message for the key 'errors.password_does_not_exist_in_session' with parameters.
+     * <pre>
+     * message: Invalid password.
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     * @return this. (NotNull)
+     */
+    public FessMessages addErrorsPasswordDoesNotExistInSession(String property) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_password_does_not_exist_in_session));
+        return this;
+    }
+
     /**
      * Add the created action message for the key 'errors.invalid_query_unknown' with parameters.
      * <pre>

+ 1 - 8
src/main/java/org/codelibs/fess/mylasta/direction/FessFwAssistantDirector.java

@@ -37,8 +37,6 @@ import org.lastaflute.core.security.OneWayCryptographer;
 import org.lastaflute.db.dbflute.classification.ListedClassificationProvider;
 import org.lastaflute.db.direction.FwDbDirection;
 import org.lastaflute.web.direction.FwWebDirection;
-import org.lastaflute.web.ruts.multipart.MultipartRequestHandler;
-import org.lastaflute.web.ruts.multipart.MultipartResourceProvider;
 
 /**
  * @author jflute
@@ -127,12 +125,7 @@ public class FessFwAssistantDirector extends CachedFwAssistantDirector {
         direction.directAdjustment(createActionAdjustmentProvider());
         direction.directMessage(nameList -> nameList.add("fess_message"), "fess_label");
         direction.directApiCall(createApiFailureHook());
-        direction.directMultipart(new MultipartResourceProvider() {
-            @Override
-            public MultipartRequestHandler createHandler() {
-                return new FessMultipartRequestHandler();
-            }
-        });
+        direction.directMultipart(() -> new FessMultipartRequestHandler());
     }
 
     protected FessUserLocaleProcessProvider createUserLocaleProcessProvider() {

+ 1 - 1
src/main/java/org/codelibs/fess/util/QueryResponseList.java

@@ -158,7 +158,7 @@ public class QueryResponseList implements List<Map<String, Object>> {
         allPageCount = (int) ((allRecordCount - 1) / pageSize) + 1;
         existPrevPage = start > 0;
         existNextPage = start < (long) (allPageCount - 1) * (long) pageSize;
-        currentPageNumber = (int) (start / pageSize) + 1;
+        currentPageNumber = start / pageSize + 1;
         currentStartRecordNumber = allRecordCount != 0 ? (currentPageNumber - 1) * pageSize + 1 : 0;
         currentEndRecordNumber = currentPageNumber * pageSize;
         currentEndRecordNumber = allRecordCount < currentEndRecordNumber ? allRecordCount : currentEndRecordNumber;

+ 4 - 0
src/main/resources/fess_es.xml

@@ -37,4 +37,8 @@
     <component name="searchFieldLogBhv" class="org.codelibs.fess.es.exbhv.SearchFieldLogBhv"/>
     <component name="searchLogBhv" class="org.codelibs.fess.es.exbhv.SearchLogBhv"/>
     <component name="userInfoBhv" class="org.codelibs.fess.es.exbhv.UserInfoBhv"/>
+
+    <component name="userBhv" class="org.codelibs.fess.es.exbhv.UserBhv"/>
+    <component name="roleBhv" class="org.codelibs.fess.es.exbhv.RoleBhv"/>
+    <component name="groupBhv" class="org.codelibs.fess.es.exbhv.GroupBhv"/>
 </components>

+ 2 - 0
src/main/resources/fess_indices/.fess_user/role.bulk

@@ -0,0 +1,2 @@
+{"index":{"_index":".fess_user","_type":"role","_id":"YWRtaW4="}}
+{"name":"admin","id":"YWRtaW4="}

+ 2 - 0
src/main/resources/fess_indices/.fess_user/user.bulk

@@ -0,0 +1,2 @@
+{"index":{"_index":".fess_user","_type":"user","_id":"YWRtaW4="}}
+{"password":"8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918","roles":["YWRtaW4="],"name":"admin","id":"YWRtaW4="}

+ 2 - 2
src/main/resources/fess_indices/.fess_user/user.json

@@ -22,11 +22,11 @@
         "type": "string",
         "index": "not_analyzed"
       },
-      "group": {
+      "groups": {
         "type": "string",
         "index": "not_analyzed"
       },
-      "role": {
+      "roles": {
         "type": "string",
         "index": "not_analyzed"
       }

+ 76 - 1
src/main/resources/fess_label.properties

@@ -189,6 +189,10 @@ labels.menu.file_authentication=&#x00bb; File Authentication
 labels.menu.request_header=&#x00bb; Request Header
 labels.menu.overlapping_host=&#x00bb; Overlapping Host
 labels.menu.role_type=&#x00bb; Role
+labels.menu_user=User
+labels.menu.user=User
+labels.menu.role=Role
+labels.menu.group=Group
 labels.menu_suggest=Suggest
 labels.menu.suggest_elevate_word=&#x00bb; Additional Word
 labels.menu.suggest_bad_word=&#x00bb; Bad Word
@@ -202,7 +206,6 @@ labels.menu.search_list=&#x00bb; Search
 labels.menu_user_log=User Info
 labels.menu.search_log=&#x00bb; Search Log
 labels.menu.stats=&#x00bb; Statistics
-labels.menu.user=&#x00bb; Users
 labels.menu.favoriteLog=&#x00bb; Popular URL
 labels.menu.logout=Logout
 labels.header.logo_alt=Fess
@@ -1119,6 +1122,78 @@ labels.suggest_bad_word_suggest_word=Bad Word
 labels.suggest_bad_word_target_role=Role
 labels.suggest_bad_word_target_label=Label
 labels.suggest_bad_word_file=Bad Word File
+labels.user_configuration=User
+labels.user_link_create_new=Create New
+labels.user_link_list=List
+labels.user_link_create=Create New
+labels.user_link_update=Edit
+labels.user_link_delete=Delete
+labels.user_link_confirm=Details
+labels.user_link_details=Details
+labels.user_link_edit=Edit
+labels.user_link_delete=Delete
+labels.user_link_prev_page=Prev
+labels.user_link_next_page=Next
+labels.user_list_name=Name
+labels.user_name=Name
+labels.user_password=Password
+labels.user_confirm_password=Confirm
+labels.user_role=Role
+labels.user_group=Group
+labels.user_title_details=User
+labels.user_button_create=Create
+labels.user_button_back=Back
+labels.user_button_confirm=Confirm
+labels.user_title_confirm=Confirm User
+labels.user_button_update=Update
+labels.user_button_delete=Delete
+labels.user_button_edit=Edit
+labels.role_configuration=Role
+labels.role_link_create_new=Create New
+labels.role_link_list=List
+labels.role_link_create=Create New
+labels.role_link_update=Edit
+labels.role_link_delete=Delete
+labels.role_link_confirm=Details
+labels.role_link_details=Details
+labels.role_link_edit=Edit
+labels.role_link_delete=Delete
+labels.role_link_prev_page=Prev
+labels.role_link_next_page=Next
+labels.role_list_name=Name
+labels.role_name=Name
+labels.role_title_details=Role
+labels.role_button_create=Create
+labels.role_button_back=Back
+labels.role_button_confirm=Confirm
+labels.role_title_confirm=Confirm Role
+labels.role_button_update=Update
+labels.role_button_delete=Delete
+labels.role_button_edit=Edit
+labels.group_configuration=group
+labels.group_link_create_new=Create New
+labels.group_link_list=List
+labels.group_link_create=Create New
+labels.group_link_update=Edit
+labels.group_link_delete=Delete
+labels.group_link_confirm=Details
+labels.group_link_details=Details
+labels.group_link_edit=Edit
+labels.group_link_delete=Delete
+labels.group_link_prev_page=Prev
+labels.group_link_next_page=Next
+labels.group_list_name=Name
+labels.group_name=Name
+labels.group_title_details=Group
+labels.group_button_create=Create
+labels.group_button_back=Back
+labels.group_button_confirm=Confirm
+labels.group_title_confirm=Confirm Group
+labels.group_button_update=Update
+labels.group_button_delete=Delete
+labels.group_button_edit=Edit
+labels.roles=Roles
+labels.groups=Groups
 labels.crud_button_create=Create
 labels.crud_button_update=Update
 labels.crud_button_delete=Delete

+ 76 - 1
src/main/resources/fess_label_en.properties

@@ -189,6 +189,10 @@ labels.menu.file_authentication=File Authentication
 labels.menu.request_header=Request Header
 labels.menu.overlapping_host=Overlapping Host
 labels.menu.role_type=Role
+labels.menu_user=User
+labels.menu.user=User
+labels.menu.role=Role
+labels.menu.group=Group
 labels.menu_suggest=Suggest
 labels.menu.suggest_elevate_word=Additional Word
 labels.menu.suggest_bad_word=Bad Word
@@ -202,7 +206,6 @@ labels.menu.search_list=Search
 labels.menu_user_log=User Info
 labels.menu.search_log=Search Log
 labels.menu.stats=Statistics
-labels.menu.user=Users
 labels.menu.favoriteLog=Popular URL
 labels.menu.logout=Logout
 labels.header.logo_alt=Fess
@@ -1119,6 +1122,78 @@ labels.suggest_bad_word_suggest_word=Bad Word
 labels.suggest_bad_word_target_role=Role
 labels.suggest_bad_word_target_label=Label
 labels.suggest_bad_word_file=Bad Word File
+labels.user_configuration=User
+labels.user_link_create_new=Create New
+labels.user_link_list=List
+labels.user_link_create=Create New
+labels.user_link_update=Edit
+labels.user_link_delete=Delete
+labels.user_link_confirm=Details
+labels.user_link_details=Details
+labels.user_link_edit=Edit
+labels.user_link_delete=Delete
+labels.user_link_prev_page=Prev
+labels.user_link_next_page=Next
+labels.user_list_name=Name
+labels.user_name=Name
+labels.user_password=Password
+labels.user_confirm_password=Confirm
+labels.user_role=Role
+labels.user_group=Group
+labels.user_title_details=User
+labels.user_button_create=Create
+labels.user_button_back=Back
+labels.user_button_confirm=Confirm
+labels.user_title_confirm=Confirm User
+labels.user_button_update=Update
+labels.user_button_delete=Delete
+labels.user_button_edit=Edit
+labels.role_configuration=Role
+labels.role_link_create_new=Create New
+labels.role_link_list=List
+labels.role_link_create=Create New
+labels.role_link_update=Edit
+labels.role_link_delete=Delete
+labels.role_link_confirm=Details
+labels.role_link_details=Details
+labels.role_link_edit=Edit
+labels.role_link_delete=Delete
+labels.role_link_prev_page=Prev
+labels.role_link_next_page=Next
+labels.role_list_name=Name
+labels.role_name=Name
+labels.role_title_details=Role
+labels.role_button_create=Create
+labels.role_button_back=Back
+labels.role_button_confirm=Confirm
+labels.role_title_confirm=Confirm Role
+labels.role_button_update=Update
+labels.role_button_delete=Delete
+labels.role_button_edit=Edit
+labels.group_configuration=Group
+labels.group_link_create_new=Create New
+labels.group_link_list=List
+labels.group_link_create=Create New
+labels.group_link_update=Edit
+labels.group_link_delete=Delete
+labels.group_link_confirm=Details
+labels.group_link_details=Details
+labels.group_link_edit=Edit
+labels.group_link_delete=Delete
+labels.group_link_prev_page=Prev
+labels.group_link_next_page=Next
+labels.group_list_name=Name
+labels.group_name=Name
+labels.group_title_details=Group
+labels.group_button_create=Create
+labels.group_button_back=Back
+labels.group_button_confirm=Confirm
+labels.group_title_confirm=Confirm Group
+labels.group_button_update=Update
+labels.group_button_delete=Delete
+labels.group_button_edit=Edit
+labels.roles=Roles
+labels.groups=Groups
 labels.crud_button_create=Create
 labels.crud_button_update=Update
 labels.crud_button_delete=Delete

+ 76 - 1
src/main/resources/fess_label_ja.properties

@@ -189,6 +189,10 @@ labels.menu.file_authentication=\u30d5\u30a1\u30a4\u30eb\u30b7\u30b9\u30c6\u30e0
 labels.menu.request_header=\u30ea\u30af\u30a8\u30b9\u30c8\u30d8\u30c3\u30c0\u30fc
 labels.menu.overlapping_host=\u91cd\u8907\u30db\u30b9\u30c8
 labels.menu.role_type=\u30ed\u30fc\u30eb
+labels.menu_user=\u30e6\u30fc\u30b6\u30fc
+labels.menu.user=\u30e6\u30fc\u30b6\u30fc
+labels.menu.role=\u30ed\u30fc\u30eb
+labels.menu.group=\u30b0\u30eb\u30fc\u30d7
 labels.menu_suggest=\u30b5\u30b8\u30a7\u30b9\u30c8
 labels.menu.suggest_elevate_word=\u8ffd\u52a0\u5019\u88dc
 labels.menu.suggest_bad_word=NG\u30ef\u30fc\u30c9
@@ -202,7 +206,6 @@ labels.menu.search_list=\u691c\u7d22
 labels.menu_user_log=\u5229\u7528\u8005\u60c5\u5831
 labels.menu.search_log=\u691c\u7d22\u30ed\u30b0
 labels.menu.stats=\u7d71\u8a08
-labels.menu.user=\u5229\u7528\u8005
 labels.menu.favoriteLog=\u4eba\u6c17URL
 labels.menu.logout=\u30ed\u30b0\u30a2\u30a6\u30c8
 labels.header.logo_alt=Fess
@@ -1119,6 +1122,78 @@ labels.suggest_bad_word_suggest_word=\u30b5\u30b8\u30a7\u30b9\u30c8\u5019\u88dc
 labels.suggest_bad_word_target_role=\u30ed\u30fc\u30eb\u540d
 labels.suggest_bad_word_target_label=\u30e9\u30d9\u30eb\u540d
 labels.suggest_bad_word_file=NG\u30ef\u30fc\u30c9\u30d5\u30a1\u30a4\u30eb
+labels.user_configuration=\u30e6\u30fc\u30b6\u30fc
+labels.user_link_create_new=\u65b0\u898f\u4f5c\u6210
+labels.user_link_list=\u4e00\u89a7
+labels.user_link_create=\u65b0\u898f\u4f5c\u6210
+labels.user_link_update=\u7de8\u96c6
+labels.user_link_delete=\u524a\u9664
+labels.user_link_confirm=\u8a73\u7d30
+labels.user_link_details=\u8a73\u7d30
+labels.user_link_edit=\u7de8\u96c6
+labels.user_link_delete=\u524a\u9664
+labels.user_link_prev_page=\u524d\u3078
+labels.user_link_next_page=\u6b21\u3078
+labels.user_list_name=\u540d\u524d
+labels.user_name=\u540d\u524d
+labels.user_password=\u30d1\u30b9\u30ef\u30fc\u30c9
+labels.user_confirm_password=\u78ba\u8a8d
+labels.user_role=\u30ed\u30fc\u30eb
+labels.user_group=\u30b0\u30eb\u30fc\u30d7
+labels.user_title_details=\u30e6\u30fc\u30b6\u30fc
+labels.user_button_create=\u4f5c\u6210
+labels.user_button_back=\u623b\u308b
+labels.user_button_confirm=\u78ba\u8a8d
+labels.user_title_confirm=\u30e6\u30fc\u30b6\u30fc\u8a2d\u5b9a\u306e\u78ba\u8a8d
+labels.user_button_update=\u66f4\u65b0
+labels.user_button_delete=\u524a\u9664
+labels.user_button_edit=\u7de8\u96c6
+labels.role_configuration=\u30ed\u30fc\u30eb
+labels.role_link_create_new=\u65b0\u898f\u4f5c\u6210
+labels.role_link_list=\u4e00\u89a7
+labels.role_link_create=\u65b0\u898f\u4f5c\u6210
+labels.role_link_update=\u7de8\u96c6
+labels.role_link_delete=\u524a\u9664
+labels.role_link_confirm=\u8a73\u7d30
+labels.role_link_details=\u8a73\u7d30
+labels.role_link_edit=\u7de8\u96c6
+labels.role_link_delete=\u524a\u9664
+labels.role_link_prev_page=\u524d\u3078
+labels.role_link_next_page=\u6b21\u3078
+labels.role_list_name=\u540d\u524d
+labels.role_name=\u540d\u524d
+labels.role_title_details=\u30ed\u30fc\u30eb
+labels.role_button_create=\u4f5c\u6210
+labels.role_button_back=\u623b\u308b
+labels.role_button_confirm=\u78ba\u8a8d
+labels.role_title_confirm=\u30ed\u30fc\u30eb\u8a2d\u5b9a\u306e\u78ba\u8a8d
+labels.role_button_update=\u66f4\u65b0
+labels.role_button_delete=\u524a\u9664
+labels.role_button_edit=\u7de8\u96c6
+labels.group_configuration=\u30b0\u30eb\u30fc\u30d7
+labels.group_link_create_new=\u65b0\u898f\u4f5c\u6210
+labels.group_link_list=\u4e00\u89a7
+labels.group_link_create=\u65b0\u898f\u4f5c\u6210
+labels.group_link_update=\u7de8\u96c6
+labels.group_link_delete=\u524a\u9664
+labels.group_link_confirm=\u8a73\u7d30
+labels.group_link_details=\u8a73\u7d30
+labels.group_link_edit=\u7de8\u96c6
+labels.group_link_delete=\u524a\u9664
+labels.group_link_prev_page=\u524d\u3078
+labels.group_link_next_page=\u6b21\u3078
+labels.group_list_name=\u540d\u524d
+labels.group_name=\u540d\u524d
+labels.group_title_details=\u30b0\u30eb\u30fc\u30d7
+labels.group_button_create=\u4f5c\u6210
+labels.group_button_back=\u623b\u308b
+labels.group_button_confirm=\u78ba\u8a8d
+labels.group_title_confirm=\u30b0\u30eb\u30fc\u30d7\u8a2d\u5b9a\u306e\u78ba\u8a8d
+labels.group_button_update=\u66f4\u65b0
+labels.group_button_delete=\u524a\u9664
+labels.group_button_edit=\u7de8\u96c6
+labels.roles=\u30ed\u30fc\u30eb
+labels.groups=\u30b0\u30eb\u30fc\u30d7
 labels.crud_button_create=\u4f5c\u6210
 labels.crud_button_update=\u66f4\u65b0
 labels.crud_button_delete=\u524a\u9664

+ 3 - 0
src/main/resources/fess_message.properties

@@ -150,6 +150,9 @@ errors.failed_to_upload_synonym_file=Failed to upload the Synonym file.
 errors.userdict_file_is_not_found=Synonym file is not found
 errors.failed_to_download_userdict_file=Failed to download the UserDict file.
 errors.failed_to_upload_userdict_file=Failed to upload the UserDict file.
+errors.blank_password=Password is required.
+errors.invalid_confirm_password=Confirm Password does not match.
+errors.password_does_not_exist_in_session=Invalid password.
 
 errors.invalid_query_unknown=The given query is invalid.
 errors.invalid_query_quoted=An invalid quote character is used.

+ 3 - 0
src/main/resources/fess_message_en.properties

@@ -79,6 +79,9 @@ errors.failed_to_upload_synonym_file=Failed to upload the Synonym file.
 errors.userdict_file_is_not_found=Synonym file is not found
 errors.failed_to_download_userdict_file=Failed to download the UserDict file.
 errors.failed_to_upload_userdict_file=Failed to upload the UserDict file.
+errors.blank_password=Password is required.
+errors.invalid_confirm_password=Confirm Password does not match.
+errors.password_does_not_exist_in_session=Invalid password.
 
 errors.invalid_query_unknown=The given query is invalid.
 errors.invalid_query_quoted=An invalid quote character is used.

+ 144 - 0
src/main/webapp/WEB-INF/view/admin/group/confirm.jsp

@@ -0,0 +1,144 @@
+<%@page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%><!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<title>Fess | <la:message key="labels.group_configuration" /></title>
+<jsp:include page="/WEB-INF/view/common/admin2/head.jsp"></jsp:include>
+</head>
+<body class="skin-blue sidebar-mini">
+	<div class="wrapper">
+		<jsp:include page="/WEB-INF/view/common/admin2/header.jsp"></jsp:include>
+		<jsp:include page="/WEB-INF/view/common/admin2/sidebar.jsp">
+			<jsp:param name="menuCategoryType" value="user" />
+			<jsp:param name="menuType" value="group" />
+		</jsp:include>
+
+		<div class="content-wrapper">
+
+			<%-- Content Header --%>
+			<section class="content-header">
+				<h1>
+					<la:message key="labels.group_title_details" />
+				</h1>
+				<ol class="breadcrumb">
+					<li><la:link href="index">
+							<la:message key="labels.group_link_list" />
+						</la:link></li>
+					<c:if test="${crudMode == 1}">
+						<li class="active"><a href="#"><la:message key="labels.group_link_create" /></a></li>
+					</c:if>
+					<c:if test="${crudMode == 2}">
+						<li class="active"><a href="#"><la:message key="labels.group_link_update" /></a></li>
+					</c:if>
+					<c:if test="${crudMode == 3}">
+						<li class="active"><a href="#"><la:message key="labels.group_link_delete" /></a></li>
+					</c:if>
+					<c:if test="${crudMode == 4}">
+						<li class="active"><a href="#"><la:message key="labels.group_link_confirm" /></a></li>
+					</c:if>
+				</ol>
+			</section>
+
+			<section class="content">
+
+				<%-- Form --%>
+				<la:form>
+					<la:hidden property="crudMode" />
+					<c:if test="${crudMode==2 || crudMode==3 || crudMode==4}">
+						<la:hidden property="id" />
+						<la:hidden property="versionNo" />
+					</c:if>
+					<div class="row">
+						<div class="col-md-12">
+							<div class="box">
+								<%-- Box Header --%>
+								<div class="box-header with-border">
+									<h3 class="box-title">
+										<c:if test="${crudMode == 1}">
+											<la:message key="labels.group_link_create" />
+										</c:if>
+										<c:if test="${crudMode == 2}">
+											<la:message key="labels.group_link_update" />
+										</c:if>
+										<c:if test="${crudMode == 3}">
+											<la:message key="labels.group_link_delete" />
+										</c:if>
+										<c:if test="${crudMode == 4}">
+											<la:message key="labels.group_link_confirm" />
+										</c:if>
+									</h3>
+									<div class="box-tools pull-right">
+										<span class="label label-default"><la:link href="index">
+												<la:message key="labels.group_link_list" />
+											</la:link></span>
+									</div>
+								</div>
+								<%-- Box Body --%>
+								<div class="box-body">
+									<%-- Message --%>
+									<div>
+										<la:info id="msg" message="true">
+											<div class="alert-message info">
+												${msg}
+											</div>
+										</la:info>
+										<la:errors />
+									</div>
+
+									<%-- Form Fields --%>
+									<table class="table table-bordered">
+										<tbody>
+											<tr>
+												<th class="col-xs-2"><la:message key="labels.group_name" /></th>
+												<td>${f:h(name)}<la:hidden property="name" /></td>
+											</tr>
+										</tbody>
+									</table>
+
+								</div>
+								<%-- Box Footer --%>
+								<div class="box-footer">
+									<c:if test="${crudMode == 1}">
+										<input type="submit" class="btn" name="editagain" value="<la:message key="labels.group_button_back"/>" />
+										<input type="submit" class="btn btn-primary" name="create"
+											value="<la:message key="labels.group_button_create"/>"
+										/>
+									</c:if>
+									<c:if test="${crudMode == 2}">
+										<input type="submit" class="btn" name="editagain" value="<la:message key="labels.group_button_back"/>" />
+										<input type="submit" class="btn btn-primary" name="update"
+											value="<la:message key="labels.group_button_update"/>"
+										/>
+									</c:if>
+									<c:if test="${crudMode == 3}">
+										<input type="submit" class="btn" name="back" value="<la:message key="labels.group_button_back"/>" />
+										<input type="submit" class="btn btn-primary" name="delete"
+											value="<la:message key="labels.group_button_delete"/>"
+										/>
+									</c:if>
+									<c:if test="${crudMode == 4}">
+										<input type="submit" class="btn" name="back" value="<la:message key="labels.group_button_back"/>" />
+										<%--
+										<input type="submit" class="btn" name="editfromconfirm"
+											value="<la:message key="labels.group_button_edit"/>"
+										/>
+										 --%>
+										<input type="submit" class="btn" name="deletefromconfirm"
+											value="<la:message key="labels.group_button_delete"/>"
+										/>
+									</c:if>
+								</div>
+							</div>
+						</div>
+					</div>
+				</la:form>
+
+			</section>
+		</div>
+
+		<jsp:include page="/WEB-INF/view/common/admin2/footer.jsp"></jsp:include>
+	</div>
+	<jsp:include page="/WEB-INF/view/common/admin2/foot.jsp"></jsp:include>
+</body>
+</html>
+

+ 117 - 0
src/main/webapp/WEB-INF/view/admin/group/edit.jsp

@@ -0,0 +1,117 @@
+<%@page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%><!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<title>Fess | <la:message key="labels.group_configuration" /></title>
+<jsp:include page="/WEB-INF/view/common/admin2/head.jsp"></jsp:include>
+</head>
+<body class="skin-blue sidebar-mini">
+	<div class="wrapper">
+		<jsp:include page="/WEB-INF/view/common/admin2/header.jsp"></jsp:include>
+		<jsp:include page="/WEB-INF/view/common/admin2/sidebar.jsp">
+			<jsp:param name="menuCategoryType" value="user" />
+			<jsp:param name="menuType" value="group" />
+		</jsp:include>
+
+		<div class="content-wrapper">
+
+			<%-- Content Header --%>
+			<section class="content-header">
+				<h1>
+					<la:message key="labels.group_title_details" />
+				</h1>
+				<ol class="breadcrumb">
+					<li><la:link href="index">
+							<la:message key="labels.group_link_list" />
+						</la:link></li>
+					<c:if test="${crudMode == 1}">
+						<li class="active"><a href="#"><la:message key="labels.group_link_create" /></a></li>
+					</c:if>
+					<c:if test="${crudMode == 2}">
+						<li class="active"><a href="#"><la:message key="labels.group_link_update" /></a></li>
+					</c:if>
+					<c:if test="${crudMode == 3}">
+						<li class="active"><a href="#"><la:message key="labels.group_link_delete" /></a></li>
+					</c:if>
+					<c:if test="${crudMode == 4}">
+						<li class="active"><a href="#"><la:message key="labels.group_link_confirm" /></a></li>
+					</c:if>
+				</ol>
+			</section>
+
+			<section class="content">
+
+				<%-- Form --%>
+				<la:form>
+					<la:hidden property="crudMode" />
+					<c:if test="${crudMode==2}">
+						<la:hidden property="id" />
+						<la:hidden property="versionNo" />
+					</c:if>
+					<div class="row">
+						<div class="col-md-12">
+							<div class="box">
+								<%-- Box Header --%>
+								<div class="box-header with-border">
+									<h3 class="box-title">
+										<c:if test="${crudMode == 1}">
+											<la:message key="labels.group_link_create" />
+										</c:if>
+										<c:if test="${crudMode == 2}">
+											<la:message key="labels.group_link_update" />
+										</c:if>
+									</h3>
+									<div class="box-tools pull-right">
+										<span class="label label-default"><la:link href="index">
+												<la:message key="labels.group_link_list" />
+											</la:link></span>
+									</div>
+								</div>
+								<%-- Box Body --%>
+								<div class="box-body">
+									<%-- Message --%>
+									<div>
+										<la:info id="msg" message="true">
+											<div class="alert-message info">
+												${msg}
+											</div>
+										</la:info>
+										<la:errors />
+									</div>
+
+									<%-- Form Fields --%>
+									<div class="form-group">
+										<label for="name"><la:message key="labels.group_name" /></label>
+										<la:text property="name" styleClass="form-control" />
+									</div>
+
+								</div>
+								<%-- Box Footer --%>
+								<div class="box-footer">
+									<c:if test="${crudMode == 1}">
+										<input type="submit" class="btn" name="back" value="<la:message key="labels.group_button_back"/>" />
+										<input type="submit" class="btn btn-primary" name="confirmfromcreate"
+											value="<la:message key="labels.group_button_create"/>"
+										/>
+									</c:if>
+									<c:if test="${crudMode == 2}">
+										<input type="submit" class="btn" name="back" value="<la:message key="labels.group_button_back"/>" />
+										<input type="submit" class="btn btn-primary" name="confirmfromupdate"
+											value="<la:message key="labels.group_button_confirm"/>"
+										/>
+									</c:if>
+								</div>
+							</div>
+						</div>
+					</div>
+				</la:form>
+
+			</section>
+		</div>
+
+		<jsp:include page="/WEB-INF/view/common/admin2/footer.jsp"></jsp:include>
+	</div>
+	<jsp:include page="/WEB-INF/view/common/admin2/foot.jsp"></jsp:include>
+</body>
+</html>
+

+ 50 - 0
src/main/webapp/WEB-INF/view/admin/group/error.jsp

@@ -0,0 +1,50 @@
+<%@page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%><!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<title>Fess | <la:message key="labels.group_configuration" /></title>
+<jsp:include page="/WEB-INF/view/common/admin2/head.jsp"></jsp:include>
+</head>
+<body class="skin-blue sidebar-mini">
+	<div class="wrapper">
+		<jsp:include page="/WEB-INF/view/common/admin2/header.jsp"></jsp:include>
+		<jsp:include page="/WEB-INF/view/common/admin2/sidebar.jsp">
+			<jsp:param name="menuCategoryType" value="user" />
+			<jsp:param name="menuType" value="group" />
+		</jsp:include>
+
+		<div class="content-wrapper">
+			<section class="content-header">
+				<h1>
+					<la:message key="labels.wizard_start_title" />
+				</h1>
+				<ol class="breadcrumb">
+					<li class="active"><la:link href="/admin/group/">
+							<la:message key="labels.group_link_list" />
+						</la:link></li>
+				</ol>
+			</section>
+
+			<section class="content">
+
+				<div class="callout callout-danger lead">
+					<h4>Error</h4>
+					<p>
+						<la:errors />
+					</p>
+					<p>
+						<la:link href="index">
+							<la:message key="labels.group_button_back" />
+						</la:link>
+					</p>
+				</div>
+
+			</section>
+		</div>
+
+		<jsp:include page="/WEB-INF/view/common/admin2/footer.jsp"></jsp:include>
+
+	</div>
+	<jsp:include page="/WEB-INF/view/common/admin2/foot.jsp"></jsp:include>
+</body>
+</html>

+ 126 - 0
src/main/webapp/WEB-INF/view/admin/group/index.jsp

@@ -0,0 +1,126 @@
+<%@page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%><!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<title>Fess | <la:message key="labels.group_configuration" /></title>
+<jsp:include page="/WEB-INF/view/common/admin2/head.jsp"></jsp:include>
+</head>
+<body class="skin-blue sidebar-mini">
+	<div class="wrapper">
+		<jsp:include page="/WEB-INF/view/common/admin2/header.jsp"></jsp:include>
+		<jsp:include page="/WEB-INF/view/common/admin2/sidebar.jsp">
+			<jsp:param name="menuCategoryType" value="user" />
+			<jsp:param name="menuType" value="group" />
+		</jsp:include>
+
+		<div class="content-wrapper">
+
+			<%-- Content Header --%>
+			<section class="content-header">
+				<h1>
+					<la:message key="labels.group_configuration" />
+				</h1>
+				<ol class="breadcrumb">
+					<li class="active"><la:link href="index">
+							<la:message key="labels.group_link_list" />
+						</la:link></li>
+				</ol>
+			</section>
+
+			<section class="content">
+
+				<div class="row">
+					<div class="col-md-12">
+						<div class="box">
+							<%-- Box Header --%>
+							<div class="box-header with-border">
+								<h3 class="box-title">
+									<la:message key="labels.group_link_list" />
+								</h3>
+								<div class="box-tools pull-right">
+									<span class="label label-default"><la:link href="createpage">
+											<la:message key="labels.group_link_create_new" />
+										</la:link></span>
+								</div>
+							</div>
+							<%-- Box Body --%>
+							<div class="box-body">
+								<%-- Message --%>
+								<div>
+									<la:info id="msg" message="true">
+										<div class="alert-message info">
+											${msg}
+										</div>
+									</la:info>
+									<la:errors />
+								</div>
+
+								<%-- List --%>
+								<c:if test="${groupPager.allRecordCount == 0}">
+									<p class="alert-message warning">
+										<la:message key="labels.list_could_not_find_crud_table" />
+									</p>
+								</c:if>
+								<c:if test="${groupPager.allRecordCount > 0}">
+									<table class="table table-bordered table-striped">
+										<thead>
+											<tr>
+												<th><la:message key="labels.group_list_name" /></th>
+											</tr>
+										</thead>
+										<tbody>
+											<c:forEach var="data" varStatus="s" items="${groupItems}">
+												<tr class="${s.index % 2 == 0 ? 'row1' : 'row2'}" data-href="${contextPath}/admin/group/confirmpage/4/${f:u(data.id)}">
+													<td>${f:h(data.name)}</td>
+												</tr>
+											</c:forEach>
+										</tbody>
+									</table>
+								</c:if>
+
+							</div>
+							<%-- Box Footer --%>
+							<div class="box-footer">
+								<%-- Paging Info --%>
+								<span><la:message key="labels.pagination_page_guide_msg" arg0="${f:h(groupPager.currentPageNumber)}"
+										arg1="${f:h(groupPager.allPageCount)}" arg2="${f:h(groupPager.allRecordCount)}"
+									/></span>
+
+								<%-- Paging Navigation --%>
+								<ul class="pagination pagination-sm no-margin pull-right">
+									<c:if test="${groupPager.existPrePage}">
+										<li class="prev"><la:link href="list/${groupPager.currentPageNumber - 1}">
+												<la:message key="labels.group_link_prev_page" />
+											</la:link></li>
+									</c:if>
+									<c:if test="${!groupPager.existPrePage}">
+										<li class="prev disabled"><a href="#"><la:message key="labels.group_link_prev_page" /></a></li>
+									</c:if>
+									<c:forEach var="p" varStatus="s" items="${groupPager.pageNumberList}">
+										<li <c:if test="${p == groupPager.currentPageNumber}">class="active"</c:if>><la:link href="list/${p}">${p}</la:link>
+										</li>
+									</c:forEach>
+									<c:if test="${groupPager.existNextPage}">
+										<li class="next"><la:link href="list/${groupPager.currentPageNumber + 1}">
+												<la:message key="labels.group_link_next_page" />
+											</la:link></li>
+									</c:if>
+									<c:if test="${!groupPager.existNextPage}">
+										<li class="next disabled"><a href="#"><la:message key="labels.group_link_next_page" /></a></li>
+									</c:if>
+								</ul>
+
+							</div>
+						</div>
+					</div>
+				</div>
+
+			</section>
+		</div>
+
+		<jsp:include page="/WEB-INF/view/common/admin2/footer.jsp"></jsp:include>
+	</div>
+	<jsp:include page="/WEB-INF/view/common/admin2/foot.jsp"></jsp:include>
+</body>
+</html>
+

+ 144 - 0
src/main/webapp/WEB-INF/view/admin/role/confirm.jsp

@@ -0,0 +1,144 @@
+<%@page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%><!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<title>Fess | <la:message key="labels.role_configuration" /></title>
+<jsp:include page="/WEB-INF/view/common/admin2/head.jsp"></jsp:include>
+</head>
+<body class="skin-blue sidebar-mini">
+	<div class="wrapper">
+		<jsp:include page="/WEB-INF/view/common/admin2/header.jsp"></jsp:include>
+		<jsp:include page="/WEB-INF/view/common/admin2/sidebar.jsp">
+			<jsp:param name="menuCategoryType" value="user" />
+			<jsp:param name="menuType" value="role" />
+		</jsp:include>
+
+		<div class="content-wrapper">
+
+			<%-- Content Header --%>
+			<section class="content-header">
+				<h1>
+					<la:message key="labels.role_title_details" />
+				</h1>
+				<ol class="breadcrumb">
+					<li><la:link href="index">
+							<la:message key="labels.role_link_list" />
+						</la:link></li>
+					<c:if test="${crudMode == 1}">
+						<li class="active"><a href="#"><la:message key="labels.role_link_create" /></a></li>
+					</c:if>
+					<c:if test="${crudMode == 2}">
+						<li class="active"><a href="#"><la:message key="labels.role_link_update" /></a></li>
+					</c:if>
+					<c:if test="${crudMode == 3}">
+						<li class="active"><a href="#"><la:message key="labels.role_link_delete" /></a></li>
+					</c:if>
+					<c:if test="${crudMode == 4}">
+						<li class="active"><a href="#"><la:message key="labels.role_link_confirm" /></a></li>
+					</c:if>
+				</ol>
+			</section>
+
+			<section class="content">
+
+				<%-- Form --%>
+				<la:form>
+					<la:hidden property="crudMode" />
+					<c:if test="${crudMode==2 || crudMode==3 || crudMode==4}">
+						<la:hidden property="id" />
+						<la:hidden property="versionNo" />
+					</c:if>
+					<div class="row">
+						<div class="col-md-12">
+							<div class="box">
+								<%-- Box Header --%>
+								<div class="box-header with-border">
+									<h3 class="box-title">
+										<c:if test="${crudMode == 1}">
+											<la:message key="labels.role_link_create" />
+										</c:if>
+										<c:if test="${crudMode == 2}">
+											<la:message key="labels.role_link_update" />
+										</c:if>
+										<c:if test="${crudMode == 3}">
+											<la:message key="labels.role_link_delete" />
+										</c:if>
+										<c:if test="${crudMode == 4}">
+											<la:message key="labels.role_link_confirm" />
+										</c:if>
+									</h3>
+									<div class="box-tools pull-right">
+										<span class="label label-default"><la:link href="index">
+												<la:message key="labels.role_link_list" />
+											</la:link></span>
+									</div>
+								</div>
+								<%-- Box Body --%>
+								<div class="box-body">
+									<%-- Message --%>
+									<div>
+										<la:info id="msg" message="true">
+											<div class="alert-message info">
+												${msg}
+											</div>
+										</la:info>
+										<la:errors />
+									</div>
+
+									<%-- Form Fields --%>
+									<table class="table table-bordered">
+										<tbody>
+											<tr>
+												<th class="col-xs-2"><la:message key="labels.role_name" /></th>
+												<td>${f:h(name)}<la:hidden property="name" /></td>
+											</tr>
+										</tbody>
+									</table>
+
+								</div>
+								<%-- Box Footer --%>
+								<div class="box-footer">
+									<c:if test="${crudMode == 1}">
+										<input type="submit" class="btn" name="editagain" value="<la:message key="labels.role_button_back"/>" />
+										<input type="submit" class="btn btn-primary" name="create"
+											value="<la:message key="labels.role_button_create"/>"
+										/>
+									</c:if>
+									<c:if test="${crudMode == 2}">
+										<input type="submit" class="btn" name="editagain" value="<la:message key="labels.role_button_back"/>" />
+										<input type="submit" class="btn btn-primary" name="update"
+											value="<la:message key="labels.role_button_update"/>"
+										/>
+									</c:if>
+									<c:if test="${crudMode == 3}">
+										<input type="submit" class="btn" name="back" value="<la:message key="labels.role_button_back"/>" />
+										<input type="submit" class="btn btn-primary" name="delete"
+											value="<la:message key="labels.role_button_delete"/>"
+										/>
+									</c:if>
+									<c:if test="${crudMode == 4}">
+										<input type="submit" class="btn" name="back" value="<la:message key="labels.role_button_back"/>" />
+										<%--
+										<input type="submit" class="btn" name="editfromconfirm"
+											value="<la:message key="labels.role_button_edit"/>"
+										/>
+										 --%>
+										<input type="submit" class="btn" name="deletefromconfirm"
+											value="<la:message key="labels.role_button_delete"/>"
+										/>
+									</c:if>
+								</div>
+							</div>
+						</div>
+					</div>
+				</la:form>
+
+			</section>
+		</div>
+
+		<jsp:include page="/WEB-INF/view/common/admin2/footer.jsp"></jsp:include>
+	</div>
+	<jsp:include page="/WEB-INF/view/common/admin2/foot.jsp"></jsp:include>
+</body>
+</html>
+

+ 117 - 0
src/main/webapp/WEB-INF/view/admin/role/edit.jsp

@@ -0,0 +1,117 @@
+<%@page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%><!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<title>Fess | <la:message key="labels.role_configuration" /></title>
+<jsp:include page="/WEB-INF/view/common/admin2/head.jsp"></jsp:include>
+</head>
+<body class="skin-blue sidebar-mini">
+	<div class="wrapper">
+		<jsp:include page="/WEB-INF/view/common/admin2/header.jsp"></jsp:include>
+		<jsp:include page="/WEB-INF/view/common/admin2/sidebar.jsp">
+			<jsp:param name="menuCategoryType" value="user" />
+			<jsp:param name="menuType" value="role" />
+		</jsp:include>
+
+		<div class="content-wrapper">
+
+			<%-- Content Header --%>
+			<section class="content-header">
+				<h1>
+					<la:message key="labels.role_title_details" />
+				</h1>
+				<ol class="breadcrumb">
+					<li><la:link href="index">
+							<la:message key="labels.role_link_list" />
+						</la:link></li>
+					<c:if test="${crudMode == 1}">
+						<li class="active"><a href="#"><la:message key="labels.role_link_create" /></a></li>
+					</c:if>
+					<c:if test="${crudMode == 2}">
+						<li class="active"><a href="#"><la:message key="labels.role_link_update" /></a></li>
+					</c:if>
+					<c:if test="${crudMode == 3}">
+						<li class="active"><a href="#"><la:message key="labels.role_link_delete" /></a></li>
+					</c:if>
+					<c:if test="${crudMode == 4}">
+						<li class="active"><a href="#"><la:message key="labels.role_link_confirm" /></a></li>
+					</c:if>
+				</ol>
+			</section>
+
+			<section class="content">
+
+				<%-- Form --%>
+				<la:form>
+					<la:hidden property="crudMode" />
+					<c:if test="${crudMode==2}">
+						<la:hidden property="id" />
+						<la:hidden property="versionNo" />
+					</c:if>
+					<div class="row">
+						<div class="col-md-12">
+							<div class="box">
+								<%-- Box Header --%>
+								<div class="box-header with-border">
+									<h3 class="box-title">
+										<c:if test="${crudMode == 1}">
+											<la:message key="labels.role_link_create" />
+										</c:if>
+										<c:if test="${crudMode == 2}">
+											<la:message key="labels.role_link_update" />
+										</c:if>
+									</h3>
+									<div class="box-tools pull-right">
+										<span class="label label-default"><la:link href="index">
+												<la:message key="labels.role_link_list" />
+											</la:link></span>
+									</div>
+								</div>
+								<%-- Box Body --%>
+								<div class="box-body">
+									<%-- Message --%>
+									<div>
+										<la:info id="msg" message="true">
+											<div class="alert-message info">
+												${msg}
+											</div>
+										</la:info>
+										<la:errors />
+									</div>
+
+									<%-- Form Fields --%>
+									<div class="form-group">
+										<label for="name"><la:message key="labels.role_name" /></label>
+										<la:text property="name" styleClass="form-control" />
+									</div>
+
+								</div>
+								<%-- Box Footer --%>
+								<div class="box-footer">
+									<c:if test="${crudMode == 1}">
+										<input type="submit" class="btn" name="back" value="<la:message key="labels.role_button_back"/>" />
+										<input type="submit" class="btn btn-primary" name="confirmfromcreate"
+											value="<la:message key="labels.role_button_create"/>"
+										/>
+									</c:if>
+									<c:if test="${crudMode == 2}">
+										<input type="submit" class="btn" name="back" value="<la:message key="labels.role_button_back"/>" />
+										<input type="submit" class="btn btn-primary" name="confirmfromupdate"
+											value="<la:message key="labels.role_button_confirm"/>"
+										/>
+									</c:if>
+								</div>
+							</div>
+						</div>
+					</div>
+				</la:form>
+
+			</section>
+		</div>
+
+		<jsp:include page="/WEB-INF/view/common/admin2/footer.jsp"></jsp:include>
+	</div>
+	<jsp:include page="/WEB-INF/view/common/admin2/foot.jsp"></jsp:include>
+</body>
+</html>
+

+ 50 - 0
src/main/webapp/WEB-INF/view/admin/role/error.jsp

@@ -0,0 +1,50 @@
+<%@page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%><!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<title>Fess | <la:message key="labels.role_configuration" /></title>
+<jsp:include page="/WEB-INF/view/common/admin2/head.jsp"></jsp:include>
+</head>
+<body class="skin-blue sidebar-mini">
+	<div class="wrapper">
+		<jsp:include page="/WEB-INF/view/common/admin2/header.jsp"></jsp:include>
+		<jsp:include page="/WEB-INF/view/common/admin2/sidebar.jsp">
+			<jsp:param name="menuCategoryType" value="user" />
+			<jsp:param name="menuType" value="role" />
+		</jsp:include>
+
+		<div class="content-wrapper">
+			<section class="content-header">
+				<h1>
+					<la:message key="labels.wizard_start_title" />
+				</h1>
+				<ol class="breadcrumb">
+					<li class="active"><la:link href="/admin/role/">
+							<la:message key="labels.role_link_list" />
+						</la:link></li>
+				</ol>
+			</section>
+
+			<section class="content">
+
+				<div class="callout callout-danger lead">
+					<h4>Error</h4>
+					<p>
+						<la:errors />
+					</p>
+					<p>
+						<la:link href="index">
+							<la:message key="labels.role_button_back" />
+						</la:link>
+					</p>
+				</div>
+
+			</section>
+		</div>
+
+		<jsp:include page="/WEB-INF/view/common/admin2/footer.jsp"></jsp:include>
+
+	</div>
+	<jsp:include page="/WEB-INF/view/common/admin2/foot.jsp"></jsp:include>
+</body>
+</html>

+ 126 - 0
src/main/webapp/WEB-INF/view/admin/role/index.jsp

@@ -0,0 +1,126 @@
+<%@page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%><!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<title>Fess | <la:message key="labels.role_configuration" /></title>
+<jsp:include page="/WEB-INF/view/common/admin2/head.jsp"></jsp:include>
+</head>
+<body class="skin-blue sidebar-mini">
+	<div class="wrapper">
+		<jsp:include page="/WEB-INF/view/common/admin2/header.jsp"></jsp:include>
+		<jsp:include page="/WEB-INF/view/common/admin2/sidebar.jsp">
+			<jsp:param name="menuCategoryType" value="user" />
+			<jsp:param name="menuType" value="role" />
+		</jsp:include>
+
+		<div class="content-wrapper">
+
+			<%-- Content Header --%>
+			<section class="content-header">
+				<h1>
+					<la:message key="labels.role_configuration" />
+				</h1>
+				<ol class="breadcrumb">
+					<li class="active"><la:link href="index">
+							<la:message key="labels.role_link_list" />
+						</la:link></li>
+				</ol>
+			</section>
+
+			<section class="content">
+
+				<div class="row">
+					<div class="col-md-12">
+						<div class="box">
+							<%-- Box Header --%>
+							<div class="box-header with-border">
+								<h3 class="box-title">
+									<la:message key="labels.role_link_list" />
+								</h3>
+								<div class="box-tools pull-right">
+									<span class="label label-default"><la:link href="createpage">
+											<la:message key="labels.role_link_create_new" />
+										</la:link></span>
+								</div>
+							</div>
+							<%-- Box Body --%>
+							<div class="box-body">
+								<%-- Message --%>
+								<div>
+									<la:info id="msg" message="true">
+										<div class="alert-message info">
+											${msg}
+										</div>
+									</la:info>
+									<la:errors />
+								</div>
+
+								<%-- List --%>
+								<c:if test="${rolePager.allRecordCount == 0}">
+									<p class="alert-message warning">
+										<la:message key="labels.list_could_not_find_crud_table" />
+									</p>
+								</c:if>
+								<c:if test="${rolePager.allRecordCount > 0}">
+									<table class="table table-bordered table-striped">
+										<thead>
+											<tr>
+												<th><la:message key="labels.role_list_name" /></th>
+											</tr>
+										</thead>
+										<tbody>
+											<c:forEach var="data" varStatus="s" items="${roleItems}">
+												<tr class="${s.index % 2 == 0 ? 'row1' : 'row2'}" data-href="${contextPath}/admin/role/confirmpage/4/${f:u(data.id)}">
+													<td>${f:h(data.name)}</td>
+												</tr>
+											</c:forEach>
+										</tbody>
+									</table>
+								</c:if>
+
+							</div>
+							<%-- Box Footer --%>
+							<div class="box-footer">
+								<%-- Paging Info --%>
+								<span><la:message key="labels.pagination_page_guide_msg" arg0="${f:h(rolePager.currentPageNumber)}"
+										arg1="${f:h(rolePager.allPageCount)}" arg2="${f:h(rolePager.allRecordCount)}"
+									/></span>
+
+								<%-- Paging Navigation --%>
+								<ul class="pagination pagination-sm no-margin pull-right">
+									<c:if test="${rolePager.existPrePage}">
+										<li class="prev"><la:link href="list/${rolePager.currentPageNumber - 1}">
+												<la:message key="labels.role_link_prev_page" />
+											</la:link></li>
+									</c:if>
+									<c:if test="${!rolePager.existPrePage}">
+										<li class="prev disabled"><a href="#"><la:message key="labels.role_link_prev_page" /></a></li>
+									</c:if>
+									<c:forEach var="p" varStatus="s" items="${rolePager.pageNumberList}">
+										<li <c:if test="${p == rolePager.currentPageNumber}">class="active"</c:if>><la:link href="list/${p}">${p}</la:link>
+										</li>
+									</c:forEach>
+									<c:if test="${rolePager.existNextPage}">
+										<li class="next"><la:link href="list/${rolePager.currentPageNumber + 1}">
+												<la:message key="labels.role_link_next_page" />
+											</la:link></li>
+									</c:if>
+									<c:if test="${!rolePager.existNextPage}">
+										<li class="next disabled"><a href="#"><la:message key="labels.role_link_next_page" /></a></li>
+									</c:if>
+								</ul>
+
+							</div>
+						</div>
+					</div>
+				</div>
+
+			</section>
+		</div>
+
+		<jsp:include page="/WEB-INF/view/common/admin2/footer.jsp"></jsp:include>
+	</div>
+	<jsp:include page="/WEB-INF/view/common/admin2/foot.jsp"></jsp:include>
+</body>
+</html>
+

+ 176 - 0
src/main/webapp/WEB-INF/view/admin/user/confirm.jsp

@@ -0,0 +1,176 @@
+<%@page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%><!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<title>Fess | <la:message key="labels.user_configuration" /></title>
+<jsp:include page="/WEB-INF/view/common/admin2/head.jsp"></jsp:include>
+</head>
+<body class="skin-blue sidebar-mini">
+	<div class="wrapper">
+		<jsp:include page="/WEB-INF/view/common/admin2/header.jsp"></jsp:include>
+		<jsp:include page="/WEB-INF/view/common/admin2/sidebar.jsp">
+			<jsp:param name="menuCategoryType" value="user" />
+			<jsp:param name="menuType" value="user" />
+		</jsp:include>
+
+		<div class="content-wrapper">
+
+			<%-- Content Header --%>
+			<section class="content-header">
+				<h1>
+					<la:message key="labels.user_title_details" />
+				</h1>
+				<ol class="breadcrumb">
+					<li><la:link href="index">
+							<la:message key="labels.user_link_list" />
+						</la:link></li>
+					<c:if test="${crudMode == 1}">
+						<li class="active"><a href="#"><la:message key="labels.user_link_create" /></a></li>
+					</c:if>
+					<c:if test="${crudMode == 2}">
+						<li class="active"><a href="#"><la:message key="labels.user_link_update" /></a></li>
+					</c:if>
+					<c:if test="${crudMode == 3}">
+						<li class="active"><a href="#"><la:message key="labels.user_link_delete" /></a></li>
+					</c:if>
+					<c:if test="${crudMode == 4}">
+						<li class="active"><a href="#"><la:message key="labels.user_link_confirm" /></a></li>
+					</c:if>
+				</ol>
+			</section>
+
+			<section class="content">
+
+				<%-- Form --%>
+				<la:form>
+					<la:hidden property="crudMode" />
+					<c:if test="${crudMode==2 || crudMode==3 || crudMode==4}">
+						<la:hidden property="id" />
+						<la:hidden property="versionNo" />
+					</c:if>
+					<div class="row">
+						<div class="col-md-12">
+							<div class="box">
+								<%-- Box Header --%>
+								<div class="box-header with-border">
+									<h3 class="box-title">
+										<c:if test="${crudMode == 1}">
+											<la:message key="labels.user_link_create" />
+										</c:if>
+										<c:if test="${crudMode == 2}">
+											<la:message key="labels.user_link_update" />
+										</c:if>
+										<c:if test="${crudMode == 3}">
+											<la:message key="labels.user_link_delete" />
+										</c:if>
+										<c:if test="${crudMode == 4}">
+											<la:message key="labels.user_link_confirm" />
+										</c:if>
+									</h3>
+									<div class="box-tools pull-right">
+										<span class="label label-default"><la:link href="index">
+												<la:message key="labels.user_link_list" />
+											</la:link></span>
+									</div>
+								</div>
+								<%-- Box Body --%>
+								<div class="box-body">
+									<%-- Message --%>
+									<div>
+										<la:info id="msg" message="true">
+											<div class="alert-message info">
+												${msg}
+											</div>
+										</la:info>
+										<la:errors />
+									</div>
+
+									<%-- Form Fields --%>
+									<table class="table table-bordered">
+										<tbody>
+											<tr>
+												<th class="col-xs-2"><la:message key="labels.user_name" /></th>
+												<td>${f:h(name)}<la:hidden property="name" /></td>
+											</tr>
+											<tr>
+													<th><la:message key="labels.roles" /></th>
+													<td>
+															<c:forEach var="rt" varStatus="s" items="${roleItems}">
+																	<c:forEach var="rtid" varStatus="s" items="${roles}">
+																			<c:if test="${rtid==rt.id}">
+																					${f:h(rt.name)}<br/>
+																			</c:if>
+																	</c:forEach>
+															</c:forEach>
+															<la:select property="roles" multiple="true" style="display:none;">
+																	<c:forEach var="rt" varStatus="s" items="${roleItems}">
+																			<la:option value="${rt.id}">${f:h(rt.name)}</la:option>
+																	</c:forEach>
+															</la:select>
+													</td>
+											</tr>
+											<tr>
+													<th><la:message key="labels.groups" /></th>
+													<td>
+															<c:forEach var="rt" varStatus="s" items="${groupItems}">
+																	<c:forEach var="rtid" varStatus="s" items="${groups}">
+																			<c:if test="${rtid==rt.id}">
+																					${f:h(rt.name)}<br/>
+																			</c:if>
+																	</c:forEach>
+															</c:forEach>
+															<la:select property="groups" multiple="true" style="display:none;">
+																	<c:forEach var="rt" varStatus="s" items="${groupItems}">
+																			<la:option value="${rt.id}">${f:h(rt.name)}</la:option>
+																	</c:forEach>
+															</la:select>
+													</td>
+											</tr>
+										</tbody>
+									</table>
+
+								</div>
+								<%-- Box Footer --%>
+								<div class="box-footer">
+									<c:if test="${crudMode == 1}">
+										<input type="submit" class="btn" name="editagain" value="<la:message key="labels.user_button_back"/>" />
+										<input type="submit" class="btn btn-primary" name="create"
+											value="<la:message key="labels.user_button_create"/>"
+										/>
+									</c:if>
+									<c:if test="${crudMode == 2}">
+										<input type="submit" class="btn" name="editagain" value="<la:message key="labels.user_button_back"/>" />
+										<input type="submit" class="btn btn-primary" name="update"
+											value="<la:message key="labels.user_button_update"/>"
+										/>
+									</c:if>
+									<c:if test="${crudMode == 3}">
+										<input type="submit" class="btn" name="back" value="<la:message key="labels.user_button_back"/>" />
+										<input type="submit" class="btn btn-primary" name="delete"
+											value="<la:message key="labels.user_button_delete"/>"
+										/>
+									</c:if>
+									<c:if test="${crudMode == 4}">
+										<input type="submit" class="btn" name="back" value="<la:message key="labels.user_button_back"/>" />
+										<input type="submit" class="btn" name="editfromconfirm"
+											value="<la:message key="labels.user_button_edit"/>"
+										/>
+										<input type="submit" class="btn" name="deletefromconfirm"
+											value="<la:message key="labels.user_button_delete"/>"
+										/>
+									</c:if>
+								</div>
+							</div>
+						</div>
+					</div>
+				</la:form>
+
+			</section>
+		</div>
+
+		<jsp:include page="/WEB-INF/view/common/admin2/footer.jsp"></jsp:include>
+	</div>
+	<jsp:include page="/WEB-INF/view/common/admin2/foot.jsp"></jsp:include>
+</body>
+</html>
+

+ 147 - 0
src/main/webapp/WEB-INF/view/admin/user/edit.jsp

@@ -0,0 +1,147 @@
+<%@page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%><!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<title>Fess | <la:message key="labels.user_configuration" /></title>
+<jsp:include page="/WEB-INF/view/common/admin2/head.jsp"></jsp:include>
+</head>
+<body class="skin-blue sidebar-mini">
+	<div class="wrapper">
+		<jsp:include page="/WEB-INF/view/common/admin2/header.jsp"></jsp:include>
+		<jsp:include page="/WEB-INF/view/common/admin2/sidebar.jsp">
+			<jsp:param name="menuCategoryType" value="user" />
+			<jsp:param name="menuType" value="user" />
+		</jsp:include>
+
+		<div class="content-wrapper">
+
+			<%-- Content Header --%>
+			<section class="content-header">
+				<h1>
+					<la:message key="labels.user_title_details" />
+				</h1>
+				<ol class="breadcrumb">
+					<li><la:link href="index">
+							<la:message key="labels.user_link_list" />
+						</la:link></li>
+					<c:if test="${crudMode == 1}">
+						<li class="active"><a href="#"><la:message key="labels.user_link_create" /></a></li>
+					</c:if>
+					<c:if test="${crudMode == 2}">
+						<li class="active"><a href="#"><la:message key="labels.user_link_update" /></a></li>
+					</c:if>
+					<c:if test="${crudMode == 3}">
+						<li class="active"><a href="#"><la:message key="labels.user_link_delete" /></a></li>
+					</c:if>
+					<c:if test="${crudMode == 4}">
+						<li class="active"><a href="#"><la:message key="labels.user_link_confirm" /></a></li>
+					</c:if>
+				</ol>
+			</section>
+
+			<section class="content">
+
+				<%-- Form --%>
+				<la:form>
+					<la:hidden property="crudMode" />
+					<c:if test="${crudMode==2}">
+						<la:hidden property="id" />
+						<la:hidden property="versionNo" />
+					</c:if>
+					<div class="row">
+						<div class="col-md-12">
+							<div class="box">
+								<%-- Box Header --%>
+								<div class="box-header with-border">
+									<h3 class="box-title">
+										<c:if test="${crudMode == 1}">
+											<la:message key="labels.user_link_create" />
+										</c:if>
+										<c:if test="${crudMode == 2}">
+											<la:message key="labels.user_link_update" />
+										</c:if>
+									</h3>
+									<div class="box-tools pull-right">
+										<span class="label label-default"><la:link href="index">
+												<la:message key="labels.user_link_list" />
+											</la:link></span>
+									</div>
+								</div>
+								<%-- Box Body --%>
+								<div class="box-body">
+									<%-- Message --%>
+									<div>
+										<la:info id="msg" message="true">
+											<div class="alert-message info">
+												${msg}
+											</div>
+										</la:info>
+										<la:errors />
+									</div>
+
+									<%-- Form Fields --%>
+									<div class="form-group">
+										<label for="name"><la:message key="labels.user_name" /></label>
+										<c:if test="${crudMode==1}">
+											<la:text property="name" styleClass="form-control" />
+										</c:if>
+										<c:if test="${crudMode==2}">
+											${f:h(name)}
+											<la:hidden property="name" styleClass="form-control" />
+										</c:if>
+									</div>
+									<div class="form-group">
+										<label for="password"><la:message key="labels.user_password" /></label>
+										<la:password property="password" styleClass="form-control" />
+									</div>
+									<div class="form-group">
+										<label for="confirm_password"><la:message key="labels.user_confirm_password" /></label>
+										<la:password property="confirmPassword" styleClass="form-control" />
+									</div>
+									<div class="form-group">
+										<label for="roles"><la:message key="labels.roles" /></label>
+										<la:select property="roles" multiple="true" styleClass="form-control">
+											<c:forEach var="l" varStatus="s" items="${roleItems}">
+												<la:option value="${l.id}">${f:h(l.name)}</la:option>
+											</c:forEach>
+										</la:select>
+									</div>
+									<div class="form-group">
+										<label for="groups"><la:message key="labels.groups" /></label>
+										<la:select property="groups" multiple="true" styleClass="form-control">
+											<c:forEach var="l" varStatus="s" items="${groupItems}">
+												<la:option value="${l.id}">${f:h(l.name)}</la:option>
+											</c:forEach>
+										</la:select>
+									</div>
+
+								</div>
+								<%-- Box Footer --%>
+								<div class="box-footer">
+									<c:if test="${crudMode == 1}">
+										<input type="submit" class="btn" name="back" value="<la:message key="labels.user_button_back"/>" />
+										<input type="submit" class="btn btn-primary" name="confirmfromcreate"
+											value="<la:message key="labels.user_button_create"/>"
+										/>
+									</c:if>
+									<c:if test="${crudMode == 2}">
+										<input type="submit" class="btn" name="back" value="<la:message key="labels.user_button_back"/>" />
+										<input type="submit" class="btn btn-primary" name="confirmfromupdate"
+											value="<la:message key="labels.user_button_confirm"/>"
+										/>
+									</c:if>
+								</div>
+							</div>
+						</div>
+					</div>
+				</la:form>
+
+			</section>
+		</div>
+
+		<jsp:include page="/WEB-INF/view/common/admin2/footer.jsp"></jsp:include>
+	</div>
+	<jsp:include page="/WEB-INF/view/common/admin2/foot.jsp"></jsp:include>
+</body>
+</html>
+

+ 50 - 0
src/main/webapp/WEB-INF/view/admin/user/error.jsp

@@ -0,0 +1,50 @@
+<%@page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%><!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<title>Fess | <la:message key="labels.user_configuration" /></title>
+<jsp:include page="/WEB-INF/view/common/admin2/head.jsp"></jsp:include>
+</head>
+<body class="skin-blue sidebar-mini">
+	<div class="wrapper">
+		<jsp:include page="/WEB-INF/view/common/admin2/header.jsp"></jsp:include>
+		<jsp:include page="/WEB-INF/view/common/admin2/sidebar.jsp">
+			<jsp:param name="menuCategoryType" value="user" />
+			<jsp:param name="menuType" value="user" />
+		</jsp:include>
+
+		<div class="content-wrapper">
+			<section class="content-header">
+				<h1>
+					<la:message key="labels.wizard_start_title" />
+				</h1>
+				<ol class="breadcrumb">
+					<li class="active"><la:link href="/admin/user/">
+							<la:message key="labels.user_link_list" />
+						</la:link></li>
+				</ol>
+			</section>
+
+			<section class="content">
+
+				<div class="callout callout-danger lead">
+					<h4>Error</h4>
+					<p>
+						<la:errors />
+					</p>
+					<p>
+						<la:link href="index">
+							<la:message key="labels.user_button_back" />
+						</la:link>
+					</p>
+				</div>
+
+			</section>
+		</div>
+
+		<jsp:include page="/WEB-INF/view/common/admin2/footer.jsp"></jsp:include>
+
+	</div>
+	<jsp:include page="/WEB-INF/view/common/admin2/foot.jsp"></jsp:include>
+</body>
+</html>

+ 126 - 0
src/main/webapp/WEB-INF/view/admin/user/index.jsp

@@ -0,0 +1,126 @@
+<%@page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%><!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<title>Fess | <la:message key="labels.user_configuration" /></title>
+<jsp:include page="/WEB-INF/view/common/admin2/head.jsp"></jsp:include>
+</head>
+<body class="skin-blue sidebar-mini">
+	<div class="wrapper">
+		<jsp:include page="/WEB-INF/view/common/admin2/header.jsp"></jsp:include>
+		<jsp:include page="/WEB-INF/view/common/admin2/sidebar.jsp">
+			<jsp:param name="menuCategoryType" value="user" />
+			<jsp:param name="menuType" value="user" />
+		</jsp:include>
+
+		<div class="content-wrapper">
+
+			<%-- Content Header --%>
+			<section class="content-header">
+				<h1>
+					<la:message key="labels.user_configuration" />
+				</h1>
+				<ol class="breadcrumb">
+					<li class="active"><la:link href="index">
+							<la:message key="labels.user_link_list" />
+						</la:link></li>
+				</ol>
+			</section>
+
+			<section class="content">
+
+				<div class="row">
+					<div class="col-md-12">
+						<div class="box">
+							<%-- Box Header --%>
+							<div class="box-header with-border">
+								<h3 class="box-title">
+									<la:message key="labels.user_link_list" />
+								</h3>
+								<div class="box-tools pull-right">
+									<span class="label label-default"><la:link href="createpage">
+											<la:message key="labels.user_link_create_new" />
+										</la:link></span>
+								</div>
+							</div>
+							<%-- Box Body --%>
+							<div class="box-body">
+								<%-- Message --%>
+								<div>
+									<la:info id="msg" message="true">
+										<div class="alert-message info">
+											${msg}
+										</div>
+									</la:info>
+									<la:errors />
+								</div>
+
+								<%-- List --%>
+								<c:if test="${userPager.allRecordCount == 0}">
+									<p class="alert-message warning">
+										<la:message key="labels.list_could_not_find_crud_table" />
+									</p>
+								</c:if>
+								<c:if test="${userPager.allRecordCount > 0}">
+									<table class="table table-bordered table-striped">
+										<thead>
+											<tr>
+												<th><la:message key="labels.user_list_name" /></th>
+											</tr>
+										</thead>
+										<tbody>
+											<c:forEach var="data" varStatus="s" items="${userItems}">
+												<tr class="${s.index % 2 == 0 ? 'row1' : 'row2'}" data-href="${contextPath}/admin/user/confirmpage/4/${f:u(data.id)}">
+													<td>${f:h(data.name)}</td>
+												</tr>
+											</c:forEach>
+										</tbody>
+									</table>
+								</c:if>
+
+							</div>
+							<%-- Box Footer --%>
+							<div class="box-footer">
+								<%-- Paging Info --%>
+								<span><la:message key="labels.pagination_page_guide_msg" arg0="${f:h(userPager.currentPageNumber)}"
+										arg1="${f:h(userPager.allPageCount)}" arg2="${f:h(userPager.allRecordCount)}"
+									/></span>
+
+								<%-- Paging Navigation --%>
+								<ul class="pagination pagination-sm no-margin pull-right">
+									<c:if test="${userPager.existPrePage}">
+										<li class="prev"><la:link href="list/${userPager.currentPageNumber - 1}">
+												<la:message key="labels.user_link_prev_page" />
+											</la:link></li>
+									</c:if>
+									<c:if test="${!userPager.existPrePage}">
+										<li class="prev disabled"><a href="#"><la:message key="labels.user_link_prev_page" /></a></li>
+									</c:if>
+									<c:forEach var="p" varStatus="s" items="${userPager.pageNumberList}">
+										<li <c:if test="${p == userPager.currentPageNumber}">class="active"</c:if>><la:link href="list/${p}">${p}</la:link>
+										</li>
+									</c:forEach>
+									<c:if test="${userPager.existNextPage}">
+										<li class="next"><la:link href="list/${userPager.currentPageNumber + 1}">
+												<la:message key="labels.user_link_next_page" />
+											</la:link></li>
+									</c:if>
+									<c:if test="${!userPager.existNextPage}">
+										<li class="next disabled"><a href="#"><la:message key="labels.user_link_next_page" /></a></li>
+									</c:if>
+								</ul>
+
+							</div>
+						</div>
+					</div>
+				</div>
+
+			</section>
+		</div>
+
+		<jsp:include page="/WEB-INF/view/common/admin2/footer.jsp"></jsp:include>
+	</div>
+	<jsp:include page="/WEB-INF/view/common/admin2/foot.jsp"></jsp:include>
+</body>
+</html>
+

+ 36 - 4
src/main/webapp/WEB-INF/view/common/admin2/sidebar.jsp

@@ -59,7 +59,8 @@
 							<span><la:message key="labels.menu.dict" /></span>
 						</todo:link></li>
 
-				</ul></li>
+				</ul>
+			</li>
 			<li class="treeview <c:if test="${param.menuCategoryType=='crawl'}">active</c:if>"><a href="#"><i
 					class='fa fa-cogs'
 				></i> <span><la:message key="labels.menu_crawl" /></span> <i class="fa fa-angle-left pull-right"></i></a>
@@ -137,7 +138,36 @@
 							<span><la:message key="labels.menu.role_type" /></span>
 						</la:link></li>
 
-				</ul></li>
+				</ul>
+			</li>
+			<li class="treeview <c:if test="${param.menuCategoryType=='user'}">active</c:if>"><a href="#"><i
+					class='fa fa-list'
+				></i> <span><la:message key="labels.menu_user" /></span> <i class="fa fa-angle-left pull-right"></i></a>
+				<ul class="treeview-menu">
+
+					<li <c:if test="${param.menuType=='user'}">class="active"</c:if>><la:link
+							href="/admin/user/index"
+						>
+							<i class='fa fa-angle-right'></i>
+							<span><la:message key="labels.menu.user" /></span>
+						</la:link></li>
+
+					<li <c:if test="${param.menuType=='role'}">class="active"</c:if>><la:link
+							href="/admin/role/index"
+						>
+							<i class='fa fa-angle-right'></i>
+							<span><la:message key="labels.menu.role" /></span>
+						</la:link></li>
+
+					<li <c:if test="${param.menuType=='group'}">class="active"</c:if>><la:link
+							href="/admin/group/index"
+						>
+							<i class='fa fa-angle-right'></i>
+							<span><la:message key="labels.menu.group" /></span>
+						</la:link></li>
+
+				</ul>
+			</li>
 			<li class="treeview <c:if test="${param.menuCategoryType=='suggest'}">active</c:if>"><a href="#"><i
 					class='fa fa-list'
 				></i> <span><la:message key="labels.menu_suggest" /></span> <i class="fa fa-angle-left pull-right"></i></a>
@@ -157,7 +187,8 @@
 							<span><la:message key="labels.menu.suggest_bad_word" /></span>
 						</todo:link></li>
 
-				</ul></li>
+				</ul>
+			</li>
 			<li class="treeview <c:if test="${param.menuCategoryType=='log'}">active</c:if>"><a href="#"><i
 					class='fa fa-files-o'
 				></i> <span><la:message key="labels.menu_system_log" /></span> <i class="fa fa-angle-left pull-right"></i></a>
@@ -195,7 +226,8 @@
 							<span><la:message key="labels.menu.search_list" /></span>
 						</todo:link></li>
 
-				</ul></li>
+				</ul>
+			</li>
 		</ul>
 		<!-- /.sidebar-menu -->
 	</section>