diff --git a/pom.xml b/pom.xml
index babd96f8..e34c8b11 100644
--- a/pom.xml
+++ b/pom.xml
@@ -12,7 +12,7 @@
org.springframework.boot
spring-boot-starter-parent
- 3.2.5
+ 3.3.0
@@ -349,7 +349,19 @@
io.swagger.core.v3
swagger-annotations
- 2.2.3
+ 2.2.22
+
+
+
+ co.elastic.clients
+ elasticsearch-java
+ 8.13.4
+
+
+
+ org.springframework.data
+ spring-data-elasticsearch
+ 5.3.0
diff --git a/src/main/java/biblivre/administration/accesscards/AccessCardBO.java b/src/main/java/biblivre/administration/accesscards/AccessCardBO.java
index cb6f77c4..e5519d4b 100644
--- a/src/main/java/biblivre/administration/accesscards/AccessCardBO.java
+++ b/src/main/java/biblivre/administration/accesscards/AccessCardBO.java
@@ -44,6 +44,14 @@ public boolean save(AccessCardDTO dto) {
return false;
}
+ public DTOCollection search(PagedAccessCardSearchDTO pagedAccessCardSearchDTO) {
+ return this.accessCardDAO.search(
+ pagedAccessCardSearchDTO.code(),
+ pagedAccessCardSearchDTO.status(),
+ pagedAccessCardSearchDTO.limit(),
+ pagedAccessCardSearchDTO.offset());
+ }
+
public DTOCollection search(
String code, AccessCardStatus status, int limit, int offset) {
return this.accessCardDAO.search(code, status, limit, offset);
diff --git a/src/main/java/biblivre/administration/accesscards/Handler.java b/src/main/java/biblivre/administration/accesscards/Handler.java
index 735bbe71..e095d48e 100644
--- a/src/main/java/biblivre/administration/accesscards/Handler.java
+++ b/src/main/java/biblivre/administration/accesscards/Handler.java
@@ -24,55 +24,32 @@
import biblivre.core.ExtendedRequest;
import biblivre.core.ExtendedResponse;
import biblivre.core.enums.ActionResult;
-import biblivre.core.utils.Constants;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONException;
-import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Handler extends AbstractHandler {
private AccessCardBO accessCardBO;
+ @Autowired private PagedAccessCardSearchWebHelper pagedAccessCardSearchWebHelper;
public void search(ExtendedRequest request, ExtendedResponse response) {
- DTOCollection list = this.searchHelper(request, response, this);
+ var pagedAccessCardSearchDTO =
+ pagedAccessCardSearchWebHelper.getPagedAccessCardSearchDTO(request);
- try {
- put("search", list.toJSONObject());
- } catch (JSONException e) {
- this.setMessage(ActionResult.WARNING, ERROR_INVALID_JSON);
- }
- }
-
- public DTOCollection searchHelper(
- ExtendedRequest request, ExtendedResponse response, AbstractHandler handler) {
- String searchParameters = request.getString("search_parameters");
+ DTOCollection accessCards = accessCardBO.search(pagedAccessCardSearchDTO);
- String query;
- AccessCardStatus status;
- try {
- JSONObject json = new JSONObject(searchParameters);
- query = json.optString("query");
- status = AccessCardStatus.fromString(json.optString("status"));
- } catch (JSONException je) {
- this.setMessage(ActionResult.WARNING, "error.invalid_parameters");
- return DTOCollection.empty();
- }
-
- Integer limit =
- request.getInteger(
- "limit", configurationBO.getInt(Constants.CONFIG_SEARCH_RESULTS_PER_PAGE));
- int offset = (request.getInteger("page", 1) - 1) * limit;
-
- DTOCollection list = accessCardBO.search(query, status, limit, offset);
-
- if (list.size() == 0) {
+ if (accessCards.isEmpty()) {
this.setMessage(ActionResult.WARNING, "administration.accesscards.error.no_card_found");
}
- return list;
+ try {
+ put("search", accessCards.toJSONObject());
+ } catch (JSONException e) {
+ this.setMessage(ActionResult.WARNING, ERROR_INVALID_JSON);
+ }
}
public void paginate(ExtendedRequest request, ExtendedResponse response) {
diff --git a/src/main/java/biblivre/administration/accesscards/PagedAccessCardSearchDTO.java b/src/main/java/biblivre/administration/accesscards/PagedAccessCardSearchDTO.java
new file mode 100644
index 00000000..ea375223
--- /dev/null
+++ b/src/main/java/biblivre/administration/accesscards/PagedAccessCardSearchDTO.java
@@ -0,0 +1,4 @@
+package biblivre.administration.accesscards;
+
+public record PagedAccessCardSearchDTO(
+ String code, AccessCardStatus status, int limit, int offset) {}
diff --git a/src/main/java/biblivre/administration/accesscards/PagedAccessCardSearchWebHelper.java b/src/main/java/biblivre/administration/accesscards/PagedAccessCardSearchWebHelper.java
new file mode 100644
index 00000000..4bd6da9e
--- /dev/null
+++ b/src/main/java/biblivre/administration/accesscards/PagedAccessCardSearchWebHelper.java
@@ -0,0 +1,28 @@
+package biblivre.administration.accesscards;
+
+import biblivre.core.ExtendedRequest;
+import biblivre.core.configurations.ConfigurationBO;
+import biblivre.core.utils.Constants;
+import org.json.JSONObject;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class PagedAccessCardSearchWebHelper {
+ @Autowired private ConfigurationBO configurationBO;
+
+ public PagedAccessCardSearchDTO getPagedAccessCardSearchDTO(ExtendedRequest request) {
+ String searchParameters = request.getString("search_parameters");
+
+ JSONObject json = new JSONObject(searchParameters);
+ String query = json.optString("query");
+ var status = AccessCardStatus.fromString(json.optString("status"));
+
+ Integer limit =
+ request.getInteger(
+ "limit", configurationBO.getInt(Constants.CONFIG_SEARCH_RESULTS_PER_PAGE));
+ int offset = (request.getInteger("page", 1) - 1) * limit;
+
+ return new PagedAccessCardSearchDTO(query, status, limit, offset);
+ }
+}
diff --git a/src/main/java/biblivre/administration/indexing/Handler.java b/src/main/java/biblivre/administration/indexing/Handler.java
index 4201b6f8..fdef3412 100644
--- a/src/main/java/biblivre/administration/indexing/Handler.java
+++ b/src/main/java/biblivre/administration/indexing/Handler.java
@@ -20,10 +20,12 @@
package biblivre.administration.indexing;
import biblivre.cataloging.enums.RecordType;
+import biblivre.circulation.user.UserDAO;
import biblivre.core.AbstractHandler;
import biblivre.core.ExtendedRequest;
import biblivre.core.ExtendedResponse;
import biblivre.core.enums.ActionResult;
+import biblivre.search.SearchException;
import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -32,8 +34,9 @@
public class Handler extends AbstractHandler {
private IndexingBO indexingBO;
- public void reindex(ExtendedRequest request, ExtendedResponse response) {
+ @Autowired private UserDAO userDAO;
+ public void reindex(ExtendedRequest request, ExtendedResponse response) throws SearchException {
String strRecordType = request.getString("record_type", "biblio");
RecordType recordType = RecordType.fromString(strRecordType);
@@ -48,6 +51,7 @@ public void reindex(ExtendedRequest request, ExtendedResponse response) {
long end;
start = new Date().getTime();
+ userDAO.reindexAll();
indexingBO.reindex(recordType);
end = new Date().getTime();
diff --git a/src/main/java/biblivre/administration/permissions/Handler.java b/src/main/java/biblivre/administration/permissions/Handler.java
index f548bf64..b03e1bab 100644
--- a/src/main/java/biblivre/administration/permissions/Handler.java
+++ b/src/main/java/biblivre/administration/permissions/Handler.java
@@ -19,6 +19,7 @@
******************************************************************************/
package biblivre.administration.permissions;
+import biblivre.circulation.user.PagedUserSearchWebHelper;
import biblivre.circulation.user.UserBO;
import biblivre.circulation.user.UserDTO;
import biblivre.core.AbstractHandler;
@@ -29,6 +30,7 @@
import biblivre.core.utils.TextUtils;
import biblivre.login.LoginBO;
import biblivre.login.LoginDTO;
+import biblivre.search.SearchException;
import java.util.Arrays;
import java.util.Collection;
import org.apache.commons.lang3.StringUtils;
@@ -44,25 +46,31 @@ public class Handler extends AbstractHandler {
biblivre.circulation.user.Handler userHandler;
+ @Autowired private PagedUserSearchWebHelper pagedUserSearchWebHelper;
+
public void search(ExtendedRequest request, ExtendedResponse response) {
- DTOCollection userList = userHandler.searchHelper(request, response, this);
+ var pagedSearchDTO = pagedUserSearchWebHelper.getPagedUserSearchDTO(request);
- if (userList == null || userList.size() == 0) {
- this.setMessage(ActionResult.WARNING, "circulation.error.no_users_found");
- return;
- }
+ try {
+ DTOCollection userList = userBO.search(pagedSearchDTO);
- DTOCollection list = new DTOCollection<>();
- list.setPaging(userList.getPaging());
+ if (userList.isEmpty()) {
+ this.setMessage(ActionResult.WARNING, "circulation.error.no_users_found");
+ return;
+ }
- for (UserDTO user : userList) {
- list.add(this.populatePermission(user));
- }
+ DTOCollection list = new DTOCollection<>();
+ list.setPaging(userList.getPaging());
+
+ for (UserDTO user : userList) {
+ list.add(this.populatePermission(user));
+ }
- try {
put("search", list.toJSONObject());
} catch (JSONException e) {
this.setMessage(ActionResult.WARNING, ERROR_INVALID_JSON);
+ } catch (SearchException e) {
+ this.setMessage(ActionResult.ERROR, "error.internal_error");
}
}
diff --git a/src/main/java/biblivre/administration/reports/Handler.java b/src/main/java/biblivre/administration/reports/Handler.java
index 846891e2..6f8aa19c 100644
--- a/src/main/java/biblivre/administration/reports/Handler.java
+++ b/src/main/java/biblivre/administration/reports/Handler.java
@@ -21,6 +21,8 @@
import biblivre.cataloging.enums.RecordDatabase;
import biblivre.circulation.lending.LendingListDTO;
+import biblivre.circulation.user.PagedUserSearchWebHelper;
+import biblivre.circulation.user.UserBO;
import biblivre.circulation.user.UserDTO;
import biblivre.core.AbstractHandler;
import biblivre.core.DTOCollection;
@@ -29,6 +31,7 @@
import biblivre.core.enums.ActionResult;
import biblivre.core.file.DiskFile;
import biblivre.core.utils.TextUtils;
+import biblivre.search.SearchException;
import org.json.JSONException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -36,23 +39,28 @@
@Component
public class Handler extends AbstractHandler {
private ReportsBO reportsBO;
- private biblivre.circulation.user.Handler userHandler;
+ @Autowired private PagedUserSearchWebHelper pagedUserSearchWebHelper;
+ @Autowired private UserBO userBO;
public void userSearch(ExtendedRequest request, ExtendedResponse response) {
- DTOCollection userList = userHandler.searchHelper(request, response, this);
+ try {
+ var pagedSearchDTO = pagedUserSearchWebHelper.getPagedUserSearchDTO(request);
- if (userList == null || userList.size() == 0) {
- this.setMessage(ActionResult.WARNING, "circulation.error.no_users_found");
- return;
- }
+ DTOCollection userList = userBO.search(pagedSearchDTO);
- DTOCollection list = new DTOCollection<>();
- list.setPaging(userList.getPaging());
+ if (userList.isEmpty()) {
+ this.setMessage(ActionResult.WARNING, "circulation.error.no_users_found");
+ return;
+ }
+
+ DTOCollection list = new DTOCollection<>();
+ list.setPaging(userList.getPaging());
- try {
put("search", list.toJSONObject());
} catch (JSONException e) {
this.setMessage(ActionResult.WARNING, ERROR_INVALID_JSON);
+ } catch (SearchException e) {
+ this.setMessage(ActionResult.ERROR, "error.internal_error");
}
}
@@ -126,9 +134,4 @@ private ReportsDTO populateDto(ExtendedRequest request) throws Exception {
public void setReportsBO(ReportsBO reportsBO) {
this.reportsBO = reportsBO;
}
-
- @Autowired
- public void setUserHandler(biblivre.circulation.user.Handler userHandler) {
- this.userHandler = userHandler;
- }
}
diff --git a/src/main/java/biblivre/administration/usertype/Handler.java b/src/main/java/biblivre/administration/usertype/Handler.java
index 8b04f65e..f40dc1e9 100644
--- a/src/main/java/biblivre/administration/usertype/Handler.java
+++ b/src/main/java/biblivre/administration/usertype/Handler.java
@@ -25,6 +25,7 @@
import biblivre.core.ExtendedResponse;
import biblivre.core.enums.ActionResult;
import biblivre.core.utils.Constants;
+import biblivre.search.SearchException;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
@@ -120,10 +121,14 @@ public void save(ExtendedRequest request, ExtendedResponse response) {
public void delete(ExtendedRequest request, ExtendedResponse response) {
Integer id = request.getInteger("id");
- if (userTypeBO.delete(id)) {
- this.setMessage(ActionResult.SUCCESS, "administration.user_type.success.delete");
- } else {
- this.setMessage(ActionResult.WARNING, "administration.user_type.error.delete");
+ try {
+ if (userTypeBO.delete(id)) {
+ this.setMessage(ActionResult.SUCCESS, "administration.user_type.success.delete");
+ } else {
+ this.setMessage(ActionResult.WARNING, "administration.user_type.error.delete");
+ }
+ } catch (SearchException e) {
+ this.setMessage(ActionResult.ERROR, "error.internal_error");
}
}
diff --git a/src/main/java/biblivre/administration/usertype/UserTypeBO.java b/src/main/java/biblivre/administration/usertype/UserTypeBO.java
index f33d58db..fa189fa7 100644
--- a/src/main/java/biblivre/administration/usertype/UserTypeBO.java
+++ b/src/main/java/biblivre/administration/usertype/UserTypeBO.java
@@ -25,6 +25,7 @@
import biblivre.core.AbstractBO;
import biblivre.core.DTOCollection;
import biblivre.core.exceptions.ValidationException;
+import biblivre.search.SearchException;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
@@ -61,13 +62,13 @@ public boolean save(UserTypeDTO userTypeDTO) {
return this.userTypeDAO.save(userTypeDTO);
}
- public boolean delete(int id) {
+ public boolean delete(int id) throws SearchException {
// Check if there's any user for this user_type
UserSearchDTO dto = new UserSearchDTO();
dto.setType(id);
DTOCollection userList = userDAO.search(dto, 1, 0);
- boolean existingUser = userList.size() > 0;
+ boolean existingUser = !userList.isEmpty();
if (existingUser) {
throw new ValidationException("administration.user_type.error.type_has_users");
diff --git a/src/main/java/biblivre/cataloging/RecordDAOImpl.java b/src/main/java/biblivre/cataloging/RecordDAOImpl.java
index 877de6b2..db23c0f8 100644
--- a/src/main/java/biblivre/cataloging/RecordDAOImpl.java
+++ b/src/main/java/biblivre/cataloging/RecordDAOImpl.java
@@ -506,13 +506,13 @@ public List getSearchResults(SearchDTO search) {
}
if (useLimit) {
- pst.setInt(index++, search.getRecordLimit());
+ pst.setLong(index++, search.getRecordLimit());
}
pst.setInt(index++, search.getSort());
- pst.setInt(index++, paging.getRecordOffset());
- pst.setInt(index, paging.getRecordsPerPage());
+ pst.setLong(index++, paging.getRecordOffset());
+ pst.setLong(index, paging.getRecordsPerPage());
ResultSet rs = pst.executeQuery();
diff --git a/src/main/java/biblivre/cataloging/holding/HoldingDAOImpl.java b/src/main/java/biblivre/cataloging/holding/HoldingDAOImpl.java
index acb8887e..77f0dff4 100644
--- a/src/main/java/biblivre/cataloging/holding/HoldingDAOImpl.java
+++ b/src/main/java/biblivre/cataloging/holding/HoldingDAOImpl.java
@@ -789,15 +789,15 @@ public List getSearchResults(SearchDTO search) {
}
if (useLimit) {
- pst.setInt(index++, search.getRecordLimit());
+ pst.setLong(index++, search.getRecordLimit());
}
index = this.populatePreparedStatement(pst, index, search);
pst.setInt(index++, search.getSort());
- pst.setInt(index++, paging.getRecordOffset());
- pst.setInt(index, paging.getRecordsPerPage());
+ pst.setLong(index++, paging.getRecordOffset());
+ pst.setLong(index, paging.getRecordsPerPage());
ResultSet rs = pst.executeQuery();
diff --git a/src/main/java/biblivre/circulation/accesscontrol/Handler.java b/src/main/java/biblivre/circulation/accesscontrol/Handler.java
index 63db0968..b7413053 100644
--- a/src/main/java/biblivre/circulation/accesscontrol/Handler.java
+++ b/src/main/java/biblivre/circulation/accesscontrol/Handler.java
@@ -21,6 +21,8 @@
import biblivre.administration.accesscards.AccessCardBO;
import biblivre.administration.accesscards.AccessCardDTO;
+import biblivre.administration.accesscards.PagedAccessCardSearchWebHelper;
+import biblivre.circulation.user.PagedUserSearchWebHelper;
import biblivre.circulation.user.UserBO;
import biblivre.circulation.user.UserDTO;
import biblivre.core.AbstractHandler;
@@ -28,6 +30,7 @@
import biblivre.core.ExtendedRequest;
import biblivre.core.ExtendedResponse;
import biblivre.core.enums.ActionResult;
+import biblivre.search.SearchException;
import java.util.Date;
import org.json.JSONException;
import org.springframework.beans.factory.annotation.Autowired;
@@ -38,50 +41,54 @@ public class Handler extends AbstractHandler {
private AccessCardBO accessCardBO;
private AccessControlBO accessControlBO;
private UserBO userBO;
- private biblivre.circulation.user.Handler userHandler;
- private biblivre.administration.accesscards.Handler cardHandler;
+ @Autowired private PagedUserSearchWebHelper pagedUserSearchWebHelper;
+ @Autowired private PagedAccessCardSearchWebHelper pagedAccessCardSearchWebHelper;
public void userSearch(ExtendedRequest request, ExtendedResponse response) {
- DTOCollection userList = userHandler.searchHelper(request, response, this);
+ var pagedSearchDTO = pagedUserSearchWebHelper.getPagedUserSearchDTO(request);
- if (userList == null) {
- return;
- }
+ try {
+ DTOCollection userList = userBO.search(pagedSearchDTO);
- DTOCollection list = new DTOCollection<>();
- list.setPaging(userList.getPaging());
+ DTOCollection list = new DTOCollection<>();
- for (UserDTO user : userList) {
- AccessControlDTO dto = accessControlBO.getByUserId(user.getId());
- if (dto == null) {
- dto = new AccessControlDTO();
- dto.setUserId(user.getId());
- }
+ list.setPaging(userList.getPaging());
- dto.setId(user.getId());
- dto.setUser(user);
+ for (UserDTO user : userList) {
+ AccessControlDTO dto = accessControlBO.getByUserId(user.getId());
+ if (dto == null) {
+ dto = new AccessControlDTO();
+ dto.setUserId(user.getId());
+ }
- if (dto.getAccessCardId() != null) {
- dto.setAccessCard(accessCardBO.get(dto.getAccessCardId()));
- }
+ dto.setId(user.getId());
+ dto.setUser(user);
- list.add(dto);
- }
+ if (dto.getAccessCardId() != null) {
+ dto.setAccessCard(accessCardBO.get(dto.getAccessCardId()));
+ }
- if (list.size() == 0) {
- this.setMessage(ActionResult.WARNING, "circulation.error.no_users_found");
- return;
- }
+ list.add(dto);
+ }
+
+ if (list.isEmpty()) {
+ this.setMessage(ActionResult.WARNING, "circulation.error.no_users_found");
+ return;
+ }
- try {
put("search", list.toJSONObject());
} catch (JSONException e) {
this.setMessage(ActionResult.WARNING, ERROR_INVALID_JSON);
+ } catch (SearchException e) {
+ this.setMessage(ActionResult.ERROR, "error.internal_error");
}
}
public void cardSearch(ExtendedRequest request, ExtendedResponse response) {
- DTOCollection cardList = cardHandler.searchHelper(request, response, this);
+ var pagedAccessCardSearchDTO =
+ pagedAccessCardSearchWebHelper.getPagedAccessCardSearchDTO(request);
+
+ DTOCollection cardList = accessCardBO.search(pagedAccessCardSearchDTO);
if (cardList == null) {
return;
@@ -107,7 +114,7 @@ public void cardSearch(ExtendedRequest request, ExtendedResponse response) {
list.add(dto);
}
- if (list.size() == 0) {
+ if (list.isEmpty()) {
this.setMessage(ActionResult.WARNING, "administration.accesscards.error.no_card_found");
return;
}
@@ -184,14 +191,4 @@ public void setAccessControlBO(AccessControlBO accessControlBO) {
public void setUserBO(UserBO userBO) {
this.userBO = userBO;
}
-
- @Autowired
- public void setUserHandler(biblivre.circulation.user.Handler userHandler) {
- this.userHandler = userHandler;
- }
-
- @Autowired
- public void setCardHandler(biblivre.administration.accesscards.Handler cardHandler) {
- this.cardHandler = cardHandler;
- }
}
diff --git a/src/main/java/biblivre/circulation/lending/Handler.java b/src/main/java/biblivre/circulation/lending/Handler.java
index 46b121e4..9299e643 100644
--- a/src/main/java/biblivre/circulation/lending/Handler.java
+++ b/src/main/java/biblivre/circulation/lending/Handler.java
@@ -28,6 +28,7 @@
import biblivre.cataloging.holding.HoldingBO;
import biblivre.cataloging.holding.HoldingDTO;
import biblivre.circulation.reservation.ReservationBO;
+import biblivre.circulation.user.PagedUserSearchWebHelper;
import biblivre.circulation.user.UserBO;
import biblivre.circulation.user.UserDTO;
import biblivre.core.AbstractHandler;
@@ -37,6 +38,7 @@
import biblivre.core.PagingDTO;
import biblivre.core.enums.ActionResult;
import biblivre.core.utils.Constants;
+import biblivre.search.SearchException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -55,7 +57,7 @@ public class Handler extends AbstractHandler {
private UserTypeBO userTypeBO;
private LendingFineBO lendingFineBO;
private ReservationBO reservationBO;
- private biblivre.circulation.user.Handler userHandler;
+ @Autowired private PagedUserSearchWebHelper pagedUserSearchWebHelper;
public void search(ExtendedRequest request, ExtendedResponse response) {
@@ -96,37 +98,38 @@ public void search(ExtendedRequest request, ExtendedResponse response) {
}
public void userSearch(ExtendedRequest request, ExtendedResponse response) {
- DTOCollection userList = userHandler.searchHelper(request, response, this);
+ try {
+ var pagedSearchDTO = pagedUserSearchWebHelper.getPagedUserSearchDTO(request);
- if (userList == null || userList.size() == 0) {
- this.setMessage(ActionResult.WARNING, "circulation.error.no_users_found");
- return;
- }
+ DTOCollection userList = userBO.search(pagedSearchDTO);
- DTOCollection list = new DTOCollection<>();
- list.setPaging(userList.getPaging());
+ if (userList.isEmpty()) {
+ this.setMessage(ActionResult.WARNING, "circulation.error.no_users_found");
+ return;
+ }
- for (UserDTO user : userList) {
- list.add(this.populateLendingList(user, false));
- }
+ DTOCollection list = new DTOCollection<>();
+ list.setPaging(userList.getPaging());
+
+ for (UserDTO user : userList) {
+ list.add(this.populateLendingList(user));
+ }
- try {
put("search", list.toJSONObject());
} catch (JSONException e) {
this.setMessage(ActionResult.WARNING, ERROR_INVALID_JSON);
+ } catch (SearchException e) {
+ this.setMessage(ActionResult.ERROR, "error.internal_error");
}
}
- private LendingListDTO populateLendingList(UserDTO user, boolean history) {
+ private LendingListDTO populateLendingList(UserDTO user) {
LendingListDTO lendingList = new LendingListDTO();
lendingList.setUser(user);
lendingList.setId(user.getId());
List lendings = lendingBO.listUserLendings(user);
- if (history) {
- lendings.addAll(lendingBO.listHistory(user));
- }
List infos = new ArrayList<>();
@@ -287,7 +290,7 @@ public void listAll(ExtendedRequest request, ExtendedResponse response) {
PagingDTO paging = new PagingDTO(lendingBO.countLendings(), limit, offset);
list.setPaging(paging);
- if (list.size() == 0) {
+ if (list.isEmpty()) {
this.setMessage(ActionResult.WARNING, "circulation.lending.no_lending_found");
return;
}
@@ -318,8 +321,6 @@ public void printReceipt(ExtendedRequest request, ExtendedResponse response) {
put("receipt", receipt);
} catch (JSONException e) {
- e.printStackTrace();
-
this.setMessage(ActionResult.WARNING, ERROR_INVALID_JSON);
}
}
@@ -358,9 +359,4 @@ public void setLendingFineBO(LendingFineBO lendingFineBO) {
public void setReservationBO(ReservationBO reservationBO) {
this.reservationBO = reservationBO;
}
-
- @Autowired
- public void setUserHandler(biblivre.circulation.user.Handler userHandler) {
- this.userHandler = userHandler;
- }
}
diff --git a/src/main/java/biblivre/circulation/lending/LendingDAO.java b/src/main/java/biblivre/circulation/lending/LendingDAO.java
index 6a77ca8a..3342c148 100644
--- a/src/main/java/biblivre/circulation/lending/LendingDAO.java
+++ b/src/main/java/biblivre/circulation/lending/LendingDAO.java
@@ -47,4 +47,6 @@ public interface LendingDAO {
Integer countLentHoldings(int recordId);
LendingDTO getLatest(int holdingSerial, int userId);
+
+ boolean hasLateLendings(int userId);
}
diff --git a/src/main/java/biblivre/circulation/lending/LendingDAOImpl.java b/src/main/java/biblivre/circulation/lending/LendingDAOImpl.java
index 8e64f8df..928449f1 100644
--- a/src/main/java/biblivre/circulation/lending/LendingDAOImpl.java
+++ b/src/main/java/biblivre/circulation/lending/LendingDAOImpl.java
@@ -411,6 +411,36 @@ public LendingDTO getLatest(int holdingSerial, int userId) {
return dto;
}
+ @Override
+ public boolean hasLateLendings(int userId) {
+ String sql =
+ """
+ SELECT CASE
+ WHEN EXISTS (
+ SELECT 1 FROM lendings
+ WHERE user_id = ? AND return_date IS NULL
+ AND expected_return_date < now()
+ ) THEN true
+ ELSE false
+ """;
+
+ try (Connection con = datasource.getConnection();
+ PreparedStatement preparedStatement = con.prepareStatement(sql)) {
+
+ preparedStatement.setInt(1, userId);
+
+ ResultSet rs = preparedStatement.executeQuery();
+
+ if (!rs.next()) {
+ throw new RuntimeException("Should never happen");
+ }
+
+ return rs.getBoolean(1);
+ } catch (Exception e) {
+ throw new DAOException(e);
+ }
+ }
+
private LendingDTO populateDTO(ResultSet rs) throws SQLException {
LendingDTO dto = new LendingDTO();
diff --git a/src/main/java/biblivre/circulation/lending/LendingFineDAO.java b/src/main/java/biblivre/circulation/lending/LendingFineDAO.java
index 5582c570..635d9054 100644
--- a/src/main/java/biblivre/circulation/lending/LendingFineDAO.java
+++ b/src/main/java/biblivre/circulation/lending/LendingFineDAO.java
@@ -14,4 +14,6 @@ public interface LendingFineDAO {
List list(UserDTO user, boolean pendingOnly);
boolean update(LendingFineDTO fine);
+
+ boolean hasPendingFine(int userID);
}
diff --git a/src/main/java/biblivre/circulation/lending/LendingFineDAOImpl.java b/src/main/java/biblivre/circulation/lending/LendingFineDAOImpl.java
index 4cccd571..83d16465 100644
--- a/src/main/java/biblivre/circulation/lending/LendingFineDAOImpl.java
+++ b/src/main/java/biblivre/circulation/lending/LendingFineDAOImpl.java
@@ -144,6 +144,38 @@ public boolean update(LendingFineDTO fine) {
}
}
+ @Override
+ public boolean hasPendingFine(int userID) {
+ try (Connection con = datasource.getConnection()) {
+
+ String sql =
+ """
+ SELECT CASE
+ WHEN EXISTS (
+ SELECT 1 FROM lending_fines
+ WHERE user_id = ? AND payment_date IS NULL
+ ) THEN true
+ ELSE false
+ END
+ """;
+
+ PreparedStatement pst = con.prepareStatement(sql);
+
+ pst.setInt(1, userID);
+
+ ResultSet rs = pst.executeQuery();
+
+ if (rs.next()) {
+ return rs.getBoolean(1);
+ }
+
+ } catch (Exception e) {
+ throw new DAOException(e);
+ }
+
+ return false;
+ }
+
private LendingFineDTO populateDTO(ResultSet rs) throws SQLException {
LendingFineDTO dto = new LendingFineDTO();
diff --git a/src/main/java/biblivre/circulation/reservation/Handler.java b/src/main/java/biblivre/circulation/reservation/Handler.java
index 46e8ba56..aec3b30f 100644
--- a/src/main/java/biblivre/circulation/reservation/Handler.java
+++ b/src/main/java/biblivre/circulation/reservation/Handler.java
@@ -28,6 +28,7 @@
import biblivre.cataloging.enums.RecordType;
import biblivre.cataloging.search.SearchDTO;
import biblivre.cataloging.search.SearchQueryDTO;
+import biblivre.circulation.user.PagedUserSearchWebHelper;
import biblivre.circulation.user.UserBO;
import biblivre.circulation.user.UserDTO;
import biblivre.circulation.user.UserSearchDTO;
@@ -37,6 +38,7 @@
import biblivre.core.ExtendedResponse;
import biblivre.core.auth.AuthorizationPoints;
import biblivre.core.enums.ActionResult;
+import biblivre.search.SearchException;
import java.util.List;
import org.json.JSONException;
import org.springframework.beans.factory.annotation.Autowired;
@@ -50,6 +52,7 @@ public class Handler extends AbstractHandler {
private ReservationBO reservationBO;
private biblivre.circulation.user.Handler userHandler;
private IndexingGroupBO indexingGroupBO;
+ @Autowired private PagedUserSearchWebHelper pagedUserSearchWebHelper;
public void search(ExtendedRequest request, ExtendedResponse response) {
String searchParameters = request.getString("search_parameters");
@@ -62,7 +65,7 @@ public void search(ExtendedRequest request, ExtendedResponse response) {
SearchDTO search = biblioRecordBO.search(searchQuery, authorizationPoints);
- if (search.size() == 0) {
+ if (search.isEmpty()) {
this.setMessage(ActionResult.WARNING, "cataloging.error.no_records_found");
}
@@ -118,24 +121,28 @@ public void paginate(ExtendedRequest request, ExtendedResponse response) {
}
public void userSearch(ExtendedRequest request, ExtendedResponse response) {
- DTOCollection userList = userHandler.searchHelper(request, response, this);
+ var pagedSearchDTO = pagedUserSearchWebHelper.getPagedUserSearchDTO(request);
- if (userList == null || userList.size() == 0) {
- this.setMessage(ActionResult.WARNING, "circulation.error.no_users_found");
- return;
- }
+ try {
+ DTOCollection userList = userBO.search(pagedSearchDTO);
- DTOCollection list = new DTOCollection<>();
- list.setPaging(userList.getPaging());
+ if (userList.isEmpty()) {
+ this.setMessage(ActionResult.WARNING, "circulation.error.no_users_found");
+ return;
+ }
- for (UserDTO user : userList) {
- list.add(this.populateReservationList(user));
- }
+ DTOCollection list = new DTOCollection<>();
+ list.setPaging(userList.getPaging());
+
+ for (UserDTO user : userList) {
+ list.add(this.populateReservationList(user));
+ }
- try {
put("search", list.toJSONObject());
} catch (JSONException e) {
this.setMessage(ActionResult.WARNING, ERROR_INVALID_JSON);
+ } catch (SearchException e) {
+ this.setMessage(ActionResult.ERROR, "error.internal_error");
}
}
@@ -202,7 +209,7 @@ public void selfSearch(ExtendedRequest request, ExtendedResponse response) {
SearchDTO search = biblioRecordBO.search(searchQuery, authorizationPoints);
- if (search.size() == 0) {
+ if (search.isEmpty()) {
this.setMessage(ActionResult.WARNING, "cataloging.error.no_records_found");
}
@@ -269,24 +276,28 @@ public void selfOpen(ExtendedRequest request, ExtendedResponse response) {
return;
}
- DTOCollection userList = userHandler.searchHelper(request, response, this);
+ var pagedSearchDTO = pagedUserSearchWebHelper.getPagedUserSearchDTO(request);
- if (userList == null || userList.size() == 0) {
- this.setMessage(ActionResult.WARNING, "circulation.error.no_users_found");
- return;
- }
+ try {
+ DTOCollection userList = userBO.search(pagedSearchDTO);
- DTOCollection list = new DTOCollection<>();
- list.setPaging(userList.getPaging());
+ if (userList.isEmpty()) {
+ this.setMessage(ActionResult.WARNING, "circulation.error.no_users_found");
+ return;
+ }
- for (UserDTO user : userList) {
- list.add(this.populateReservationList(user));
- }
+ DTOCollection list = new DTOCollection<>();
+ list.setPaging(userList.getPaging());
+
+ for (UserDTO user : userList) {
+ list.add(this.populateReservationList(user));
+ }
- try {
put("search", list.toJSONObject());
} catch (JSONException e) {
this.setMessage(ActionResult.WARNING, ERROR_INVALID_JSON);
+ } catch (SearchException e) {
+ this.setMessage(ActionResult.ERROR, "error.internal_error");
}
}
diff --git a/src/main/java/biblivre/circulation/user/Handler.java b/src/main/java/biblivre/circulation/user/Handler.java
index 0b69625d..e933042b 100644
--- a/src/main/java/biblivre/circulation/user/Handler.java
+++ b/src/main/java/biblivre/circulation/user/Handler.java
@@ -33,9 +33,9 @@
import biblivre.core.ExtendedResponse;
import biblivre.core.enums.ActionResult;
import biblivre.core.file.MemoryFile;
-import biblivre.core.utils.Constants;
import biblivre.digitalmedia.DigitalMediaBO;
import biblivre.digitalmedia.DigitalMediaEncodingUtil;
+import biblivre.search.SearchException;
import java.io.ByteArrayInputStream;
import java.util.Base64;
import java.util.Collection;
@@ -54,6 +54,7 @@ public class Handler extends AbstractHandler {
private ReservationBO reservationBO;
private DigitalMediaBO digitalMediaBO;
private UserFieldBO userFieldBO;
+ private PagedUserSearchWebHelper pagedUserSearchWebHelper;
public void open(ExtendedRequest request, ExtendedResponse response) {
Integer id = request.getInteger("id");
@@ -73,12 +74,21 @@ public void open(ExtendedRequest request, ExtendedResponse response) {
}
public void search(ExtendedRequest request, ExtendedResponse response) {
- DTOCollection list = this.searchHelper(request, response, this);
-
try {
- put("search", list.toJSONObject());
+
+ var pagedSearchDTO = pagedUserSearchWebHelper.getPagedUserSearchDTO(request);
+
+ DTOCollection userList = userBO.search(pagedSearchDTO);
+
+ if (userList.isEmpty()) {
+ setMessage(ActionResult.WARNING, "circulation.error.no_users_found");
+ }
+
+ put("search", userList.toJSONObject());
} catch (JSONException e) {
this.setMessage(ActionResult.WARNING, ERROR_INVALID_JSON);
+ } catch (SearchException e) {
+ this.setMessage(ActionResult.ERROR, "circulation.error.search_error");
}
}
@@ -86,32 +96,6 @@ public void paginate(ExtendedRequest request, ExtendedResponse response) {
this.search(request, response);
}
- public DTOCollection searchHelper(
- ExtendedRequest request, ExtendedResponse response, AbstractHandler handler) {
-
- String searchParameters = request.getString("search_parameters");
-
- UserSearchDTO searchDto = new UserSearchDTO(searchParameters);
-
- Integer limit =
- request.getInteger(
- "limit", configurationBO.getInt(Constants.CONFIG_SEARCH_RESULTS_PER_PAGE));
- Integer offset = request.getInteger("offset", 0);
-
- Integer page = request.getInteger("page", 1);
- if (page > 1) {
- offset = limit * (page - 1);
- }
-
- DTOCollection list = userBO.search(searchDto, limit, offset);
-
- if (list.size() == 0) {
- handler.setMessage(ActionResult.WARNING, "circulation.error.no_users_found");
- }
-
- return list;
- }
-
public void save(ExtendedRequest request, ExtendedResponse response) {
Integer id = request.getInteger("id");
@@ -178,21 +162,25 @@ public void save(ExtendedRequest request, ExtendedResponse response) {
}
}
- if (userBO.save(user)) {
- if (id == 0) {
- this.setMessage(ActionResult.SUCCESS, "circulation.users.success.save");
+ try {
+ UserDTO saved = userBO.save(user);
+
+ if (saved != null) {
+ if (id == 0) {
+ this.setMessage(ActionResult.SUCCESS, "circulation.users.success.saved");
+ } else {
+ this.setMessage(ActionResult.SUCCESS, "circulation.users.success.update");
+ }
+
+ put("data", saved.toJSONObject());
+ put("full_data", true);
} else {
- this.setMessage(ActionResult.SUCCESS, "circulation.users.success.update");
+ this.setMessage(ActionResult.WARNING, "circulation.users.error.saved");
}
- } else {
- this.setMessage(ActionResult.WARNING, "circulation.users.error.save");
- }
-
- try {
- put("data", user.toJSONObject());
- put("full_data", true);
} catch (JSONException e) {
this.setMessage(ActionResult.WARNING, ERROR_INVALID_JSON);
+ } catch (SearchException e) {
+ this.setMessage(ActionResult.ERROR, "error.internal_error");
}
}
@@ -201,14 +189,24 @@ public void delete(ExtendedRequest request, ExtendedResponse response) {
UserDTO user = userBO.get(id);
- String act = (user.getStatus() == UserStatus.INACTIVE) ? "delete" : "disable";
+ String action = (user.getStatus() == UserStatus.INACTIVE) ? "delete" : "disable";
- boolean success = userBO.delete(user);
+ try {
+ boolean success = false;
- if (success) {
- this.setMessage(ActionResult.SUCCESS, "circulation.users.success." + act);
- } else {
- this.setMessage(ActionResult.WARNING, "circulation.users.failure." + act);
+ if ("delete".equals(action)) {
+ success = userBO.delete(user);
+ } else {
+ success = userBO.updateUserStatus(id, UserStatus.INACTIVE);
+ }
+
+ if (success) {
+ this.setMessage(ActionResult.SUCCESS, "circulation.users.success." + action);
+ } else {
+ this.setMessage(ActionResult.WARNING, "circulation.users.failure." + action);
+ }
+ } catch (SearchException e) {
+ this.setMessage(ActionResult.ERROR, "circulation.users.failure." + action);
}
}
@@ -260,22 +258,30 @@ public void loadTabData(ExtendedRequest request, ExtendedResponse response) {
public void block(ExtendedRequest request, ExtendedResponse response) {
Integer userId = request.getInteger("user_id");
- boolean success = userBO.updateUserStatus(userId, UserStatus.BLOCKED);
- if (success) {
- this.setMessage(ActionResult.SUCCESS, "circulation.users.success.block");
- } else {
- this.setMessage(ActionResult.WARNING, "circulation.users.failure.block");
+ try {
+ boolean success = userBO.updateUserStatus(userId, UserStatus.BLOCKED);
+ if (success) {
+ this.setMessage(ActionResult.SUCCESS, "circulation.users.success.block");
+ } else {
+ this.setMessage(ActionResult.WARNING, "circulation.users.failure.block");
+ }
+ } catch (SearchException e) {
+ this.setMessage(ActionResult.ERROR, "error.internal_error");
}
}
public void unblock(ExtendedRequest request, ExtendedResponse response) {
Integer userId = request.getInteger("user_id");
- boolean success = userBO.updateUserStatus(userId, UserStatus.ACTIVE);
- if (success) {
- this.setMessage(ActionResult.SUCCESS, "circulation.users.success.unblock");
- } else {
- this.setMessage(ActionResult.WARNING, "circulation.users.failure.unblock");
+ try {
+ boolean success = userBO.updateUserStatus(userId, UserStatus.ACTIVE);
+ if (success) {
+ this.setMessage(ActionResult.SUCCESS, "circulation.users.success.unblock");
+ } else {
+ this.setMessage(ActionResult.WARNING, "circulation.users.failure.unblock");
+ }
+ } catch (SearchException e) {
+ this.setMessage(ActionResult.ERROR, "error.internal_error");
}
}
@@ -308,4 +314,9 @@ public void setDigitalMediaBO(DigitalMediaBO digitalMediaBO) {
public void setUserFieldBO(UserFieldBO userFieldBO) {
this.userFieldBO = userFieldBO;
}
+
+ @Autowired
+ public void setPagedUserSearchWebHelper(PagedUserSearchWebHelper pagedUserSearchWebHelper) {
+ this.pagedUserSearchWebHelper = pagedUserSearchWebHelper;
+ }
}
diff --git a/src/main/java/biblivre/circulation/user/IndexableUserDAO.java b/src/main/java/biblivre/circulation/user/IndexableUserDAO.java
new file mode 100644
index 00000000..29bf3162
--- /dev/null
+++ b/src/main/java/biblivre/circulation/user/IndexableUserDAO.java
@@ -0,0 +1,296 @@
+package biblivre.circulation.user;
+
+import biblivre.circulation.lending.LendingDAO;
+import biblivre.circulation.lending.LendingFineDAO;
+import biblivre.core.DTOCollection;
+import biblivre.core.PagingDTO;
+import biblivre.core.SchemaThreadLocal;
+import biblivre.search.SearchException;
+import biblivre.search.user.IndexableUser;
+import biblivre.search.user.IndexableUserQueryParameters;
+import biblivre.search.user.IndexableUserRepository;
+import co.elastic.clients.elasticsearch._types.FieldValue;
+import co.elastic.clients.elasticsearch._types.query_dsl.Query;
+import co.elastic.clients.json.JsonData;
+import co.elastic.clients.util.ObjectBuilder;
+import jakarta.annotation.Nonnull;
+import java.util.*;
+import java.util.function.Function;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.elasticsearch.client.elc.NativeQuery;
+import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
+import org.springframework.data.elasticsearch.core.SearchHits;
+import org.springframework.stereotype.Service;
+
+@Service
+@ConditionalOnProperty(value = "elasticsearch.server.url")
+public class IndexableUserDAO extends UserDAOImpl {
+ @Autowired private IndexableUserRepository indexableUserRepository;
+
+ @Value(value = "${biblivre.cloud.tentant:localhost}")
+ private String tenant;
+
+ @Autowired private LendingFineDAO lendingFineDAO;
+
+ @Autowired private LendingDAO lendingDAO;
+
+ @Autowired private ElasticsearchOperations elasticsearchOperations;
+
+ @Override
+ public @Nonnull DTOCollection search(UserSearchDTO dto, int limit, int offset)
+ throws SearchException {
+ List filterQueries = getFilterQueries(dto);
+
+ List mustQueries = getMustQueries(dto);
+
+ NativeQuery nativeQuery =
+ NativeQuery.builder()
+ .withQuery(
+ query ->
+ query.bool(
+ bool ->
+ bool.filter(filterQueries)
+ .must(mustQueries)))
+ .withPageable(PageRequest.of(offset / limit, limit))
+ .build();
+
+ SearchHits searchHits =
+ elasticsearchOperations.search(nativeQuery, IndexableUser.class);
+
+ DTOCollection result =
+ searchHits.getSearchHits().stream()
+ .map(hit -> getUserDTO(hit.getContent()))
+ .collect(DTOCollection::new, DTOCollection::add, DTOCollection::addAll);
+
+ result.setPaging(new PagingDTO(searchHits.getTotalHits(), limit, offset));
+
+ return result;
+ }
+
+ private List getFilterQueries(UserSearchDTO dto) {
+ List baseFilterQuries = buildBaseQueries(dto);
+
+ if (dto.isSimpleSearch()) {
+ return baseFilterQuries;
+ }
+
+ List filterQueries = new ArrayList<>(baseFilterQuries);
+
+ if (dto.isUserCardNeverPrinted()) {
+ filterQueries.add(buildBooleanTermQuery("userCardPrinted", false));
+ }
+
+ if (dto.isPendingFines()) {
+ filterQueries.add(buildBooleanTermQuery("hasPendingFines", true));
+ }
+
+ if (dto.isInactiveOnly()) {
+ filterQueries.add(buildBooleanTermQuery("isInactive", true));
+ }
+
+ if (dto.isLateLendings()) {
+ filterQueries.add(buildBooleanTermQuery("hasPendingLoans", true));
+ }
+
+ if (dto.isLoginAccess()) {
+ filterQueries.add(buildBooleanTermQuery("hasLogin", true));
+ }
+
+ if (dto.getCreatedStartDate() != null) {
+ filterQueries.add(buildRangeGteQuery("created", dto.getCreatedStartDate()));
+ }
+
+ if (dto.getCreatedEndDate() != null) {
+ filterQueries.add(buildRangeLteQuery("created", dto.getCreatedEndDate()));
+ }
+
+ if (dto.getModifiedStartDate() != null) {
+ filterQueries.add(buildRangeGteQuery("modified", dto.getModifiedStartDate()));
+ }
+
+ if (dto.getModifiedEndDate() != null) {
+ filterQueries.add(buildRangeLteQuery("modified", dto.getModifiedEndDate()));
+ }
+
+ if (dto.isSearchById()) {
+ filterQueries.add(buildLongTermQuery(dto));
+ }
+
+ return Collections.unmodifiableList(filterQueries);
+ }
+
+ private List buildBaseQueries(UserSearchDTO dto) {
+ Query schemaQ = buildStringTermQuery("schema", SchemaThreadLocal.get());
+
+ Query tenantQ = buildStringTermQuery("tenant", tenant);
+
+ return List.of(schemaQ, tenantQ);
+ }
+
+ private static List getMustQueries(UserSearchDTO dto) {
+ return dto.isListAll() ? Collections.emptyList() : List.of(getMustQueryForName(dto));
+ }
+
+ private static Query getMustQueryForName(UserSearchDTO dto) {
+ return new Query.Builder()
+ .match(match -> match.field("name").query(dto.getQuery()).fuzziness("AUTO"))
+ .build();
+ }
+
+ private static Query buildLongTermQuery(UserSearchDTO dto) {
+ return buildTermQuery("id", value -> value.longValue(Long.parseLong(dto.getQuery())));
+ }
+
+ private static Query buildRangeGteQuery(String field, Date value) {
+ return new Query.Builder()
+ .range(range -> range.field(field).gte(JsonData.of(value)))
+ .build();
+ }
+
+ private static Query buildRangeLteQuery(String field, Date value) {
+ return new Query.Builder()
+ .range(range -> range.field(field).lte(JsonData.of(value)))
+ .build();
+ }
+
+ private static Query buildBooleanTermQuery(String field, boolean value) {
+ return buildTermQuery(field, v -> v.booleanValue(value));
+ }
+
+ private static Query buildStringTermQuery(String field, String value) {
+ return buildTermQuery(field, v -> v.stringValue(value));
+ }
+
+ private static Query buildTermQuery(
+ String field, Function> valueFunction) {
+ return new Query.Builder().term(term -> term.field(field).value(valueFunction)).build();
+ }
+
+ private IndexableUserQueryParameters getIndexableUserQueryParameters(UserSearchDTO userSearch) {
+ return new IndexableUserQueryParameters(
+ parseInt(userSearch.getQuery()).orElse(-1),
+ userSearch.getQuery(),
+ SchemaThreadLocal.get(),
+ tenant,
+ userSearch.isLoginAccess(),
+ userSearch.isPendingFines(),
+ userSearch.isLateLendings(),
+ userSearch.isUserCardNeverPrinted(),
+ userSearch.isInactiveOnly());
+ }
+
+ @Override
+ public UserDTO save(UserDTO user) throws SearchException {
+ UserDTO saved = super.save(user);
+
+ index(user);
+
+ return saved;
+ }
+
+ private void index(UserDTO user) throws SearchException {
+ indexableUserRepository.save(getEsUser(user));
+ }
+
+ @Override
+ public boolean delete(UserDTO user) throws SearchException {
+ super.delete(user);
+
+ indexableUserRepository.delete(getEsUser(user));
+
+ return true;
+ }
+
+ @Override
+ public void markAsPrinted(Collection ids) throws SearchException {
+ super.markAsPrinted(ids);
+
+ reindex(ids);
+ }
+
+ @Override
+ public boolean updateUserStatus(Integer userId, UserStatus status) throws SearchException {
+ super.updateUserStatus(userId, status);
+
+ reindex(Set.of(userId));
+
+ return true;
+ }
+
+ @Override
+ public void reindexAll() throws SearchException {
+ indexableUserRepository.deleteBySchemaAndTenant(SchemaThreadLocal.get(), tenant);
+
+ DTOCollection allUsers = super.search(new UserSearchDTO(), Integer.MAX_VALUE, 0);
+
+ bulkIndex(allUsers.stream().map(this::getEsUser).toList());
+ }
+
+ private void reindex(Collection ids) throws SearchException {
+ Map usersMap = super.map(ids);
+
+ bulkIndex(usersMap.values().stream().map(this::getEsUser).toList());
+ }
+
+ private void bulkIndex(Collection users) {
+ indexableUserRepository.saveAll(users);
+ }
+
+ private IndexableUser getEsUser(UserDTO user) {
+ int userId = user.getId();
+
+ boolean userHasPendingFines = lendingFineDAO.hasPendingFine(userId);
+
+ boolean userHasPendingLoans = lendingDAO.hasLateLendings(userId);
+
+ return new IndexableUser(
+ userId,
+ user.getName(),
+ user.getType(),
+ user.getPhotoId(),
+ user.getStatus().toString(),
+ user.getLoginId() == null ? -1 : user.getLoginId(),
+ user.getCreated(),
+ user.getCreatedBy(),
+ user.getModified(),
+ user.getModifiedBy(),
+ user.isUserCardPrinted(),
+ user.getFields(),
+ userHasPendingFines,
+ userHasPendingLoans,
+ user.hasLogin(),
+ user.isInactive(),
+ SchemaThreadLocal.get(),
+ tenant);
+ }
+
+ private static UserDTO getUserDTO(IndexableUser indexableUser) {
+ UserDTO userDTO = new UserDTO();
+
+ userDTO.setId(indexableUser.getId());
+ userDTO.setName(indexableUser.getName());
+ userDTO.setType(indexableUser.getType());
+ userDTO.setPhotoId(indexableUser.getPhotoId());
+ userDTO.setStatus(UserStatus.fromString(indexableUser.getStatus()));
+ userDTO.setLoginId(indexableUser.getLoginId() == -1 ? null : indexableUser.getLoginId());
+ userDTO.setCreatedBy(indexableUser.getCreatedBy());
+ userDTO.setCreated(indexableUser.getCreated());
+ userDTO.setModifiedBy(indexableUser.getModifiedBy());
+ userDTO.setModified(indexableUser.getModified());
+ userDTO.setUserCardPrinted(indexableUser.isUserCardPrinted());
+ userDTO.setFields(indexableUser.getFields());
+
+ return userDTO;
+ }
+
+ private static OptionalInt parseInt(String query) {
+ try {
+ return OptionalInt.of(Integer.parseInt(query));
+ } catch (NumberFormatException e) {
+ return OptionalInt.empty();
+ }
+ }
+}
diff --git a/src/main/java/biblivre/circulation/user/PagedUserSearchDTO.java b/src/main/java/biblivre/circulation/user/PagedUserSearchDTO.java
new file mode 100644
index 00000000..883ea1dd
--- /dev/null
+++ b/src/main/java/biblivre/circulation/user/PagedUserSearchDTO.java
@@ -0,0 +1,3 @@
+package biblivre.circulation.user;
+
+public record PagedUserSearchDTO(UserSearchDTO userSearchDTO, int limit, int offset) {}
diff --git a/src/main/java/biblivre/circulation/user/PagedUserSearchWebHelper.java b/src/main/java/biblivre/circulation/user/PagedUserSearchWebHelper.java
new file mode 100644
index 00000000..f5dce56a
--- /dev/null
+++ b/src/main/java/biblivre/circulation/user/PagedUserSearchWebHelper.java
@@ -0,0 +1,32 @@
+package biblivre.circulation.user;
+
+import biblivre.core.ExtendedRequest;
+import biblivre.core.configurations.ConfigurationBO;
+import biblivre.core.utils.Constants;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class PagedUserSearchWebHelper {
+ @Autowired private ConfigurationBO configurationBO;
+
+ public PagedUserSearchDTO getPagedUserSearchDTO(ExtendedRequest request) {
+ String searchParameters = request.getString("search_parameters");
+
+ UserSearchDTO searchDto = new UserSearchDTO(searchParameters);
+
+ Integer limit =
+ request.getInteger(
+ "limit", configurationBO.getInt(Constants.CONFIG_SEARCH_RESULTS_PER_PAGE));
+
+ Integer offset = request.getInteger("offset", 0);
+
+ Integer page = request.getInteger("page", 1);
+
+ if (page > 1) {
+ offset = limit * (page - 1);
+ }
+
+ return new PagedUserSearchDTO(searchDto, limit, offset);
+ }
+}
diff --git a/src/main/java/biblivre/circulation/user/UserBO.java b/src/main/java/biblivre/circulation/user/UserBO.java
index 2c686f0b..fa40a982 100644
--- a/src/main/java/biblivre/circulation/user/UserBO.java
+++ b/src/main/java/biblivre/circulation/user/UserBO.java
@@ -27,6 +27,7 @@
import biblivre.core.translations.TranslationsMap;
import biblivre.core.utils.Constants;
import biblivre.labels.print.LabelPrintDTO;
+import biblivre.search.SearchException;
import com.lowagie.text.Chunk;
import com.lowagie.text.Document;
import com.lowagie.text.Element;
@@ -40,6 +41,7 @@
import com.lowagie.text.pdf.PdfPCell;
import com.lowagie.text.pdf.PdfPTable;
import com.lowagie.text.pdf.PdfWriter;
+import jakarta.annotation.Nonnull;
import java.io.File;
import java.io.OutputStream;
import java.nio.file.Files;
@@ -56,7 +58,16 @@ public class UserBO extends AbstractBO {
private UserDAO userDAO;
private UserTypeBO userTypeBO;
- public DTOCollection search(UserSearchDTO dto, int limit, int offset) {
+ public @Nonnull DTOCollection search(PagedUserSearchDTO pagedUserSearchDTO)
+ throws SearchException {
+ return this.search(
+ pagedUserSearchDTO.userSearchDTO(),
+ pagedUserSearchDTO.limit(),
+ pagedUserSearchDTO.offset());
+ }
+
+ public @Nonnull DTOCollection search(UserSearchDTO dto, int limit, int offset)
+ throws SearchException {
DTOCollection list = this.userDAO.search(dto, limit, offset);
Map map = userTypeBO.map();
@@ -99,15 +110,15 @@ public UserDTO getUserByLoginId(Integer loginId) {
return this.get(userId);
}
- public boolean save(UserDTO user) {
+ public UserDTO save(UserDTO user) throws SearchException {
return this.userDAO.save(user);
}
- public boolean updateUserStatus(Integer userId, UserStatus status) {
+ public boolean updateUserStatus(Integer userId, UserStatus status) throws SearchException {
return this.userDAO.updateUserStatus(userId, status);
}
- public boolean delete(UserDTO user) {
+ public boolean delete(UserDTO user) throws SearchException {
return this.userDAO.delete(user);
}
@@ -221,7 +232,7 @@ private String getText(TranslationsMap i18n, String key) {
return text;
}
- public void markAsPrinted(Set ids) {
+ public void markAsPrinted(Set ids) throws SearchException {
this.userDAO.markAsPrinted(ids);
}
diff --git a/src/main/java/biblivre/circulation/user/UserDAO.java b/src/main/java/biblivre/circulation/user/UserDAO.java
index 948ecf95..177d44a7 100644
--- a/src/main/java/biblivre/circulation/user/UserDAO.java
+++ b/src/main/java/biblivre/circulation/user/UserDAO.java
@@ -1,22 +1,25 @@
package biblivre.circulation.user;
import biblivre.core.DTOCollection;
+import biblivre.search.SearchException;
+import java.util.Collection;
import java.util.Map;
-import java.util.Set;
public interface UserDAO {
- Map map(Set ids);
+ Map map(Collection ids);
- DTOCollection search(UserSearchDTO dto, int limit, int offset);
+ DTOCollection search(UserSearchDTO dto, int limit, int offset) throws SearchException;
- boolean save(UserDTO user);
+ UserDTO save(UserDTO user) throws SearchException;
- boolean delete(UserDTO user);
+ boolean delete(UserDTO user) throws SearchException;
- void markAsPrinted(Set ids);
+ void markAsPrinted(Collection ids) throws SearchException;
- boolean updateUserStatus(Integer userId, UserStatus status);
+ boolean updateUserStatus(Integer userId, UserStatus status) throws SearchException;
Integer getUserIdByLoginId(Integer loginId);
+
+ default void reindexAll() throws SearchException {}
}
diff --git a/src/main/java/biblivre/circulation/user/UserDAOImpl.java b/src/main/java/biblivre/circulation/user/UserDAOImpl.java
index e3a73e20..0da59c6f 100644
--- a/src/main/java/biblivre/circulation/user/UserDAOImpl.java
+++ b/src/main/java/biblivre/circulation/user/UserDAOImpl.java
@@ -24,28 +24,25 @@
import biblivre.core.function.UnsafeFunction;
import biblivre.core.utils.CalendarUtils;
import biblivre.core.utils.TextUtils;
+import biblivre.search.SearchException;
+import jakarta.annotation.Nonnull;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.*;
import java.util.Map.Entry;
-import java.util.Set;
import javax.sql.DataSource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-@Service
@Slf4j
-public class UserDAOImpl extends AbstractDAO implements UserDAO {
+public abstract class UserDAOImpl extends AbstractDAO implements UserDAO {
@Override
- public Map map(Set ids) {
+ public Map map(Collection ids) {
Map map = new HashMap<>();
try (Connection con = datasource.getConnection()) {
@@ -76,7 +73,8 @@ public Map map(Set ids) {
}
@Override
- public DTOCollection search(UserSearchDTO dto, int limit, int offset) {
+ public @Nonnull DTOCollection search(UserSearchDTO dto, int limit, int offset)
+ throws SearchException {
DTOCollection list = new DTOCollection<>();
String query = dto.getQuery();
@@ -265,7 +263,7 @@ public DTOCollection search(UserSearchDTO dto, int limit, int offset) {
}
@Override
- public boolean save(UserDTO user) {
+ public UserDTO save(UserDTO user) throws SearchException {
try (Connection con = datasource.getConnection()) {
con.setAutoCommit(false);
@@ -276,8 +274,8 @@ public boolean save(UserDTO user) {
if (newUser) {
user.setId(this.getNextSerial("users_id_seq"));
sql.append(
- "INSERT INTO users (id, name, type, photo_id, status, created_by, name_ascii) ");
- sql.append("VALUES (?, ?, ?, ?, ?, ?, ?);");
+ "INSERT INTO users (id, name, type, photo_id, status, created_by, name_ascii, created) ");
+ sql.append("VALUES (?, ?, ?, ?, ?, ?, ?, now());");
pst = con.prepareStatement(sql.toString());
pst.setInt(1, user.getId());
pst.setString(2, user.getName());
@@ -297,7 +295,7 @@ public boolean save(UserDTO user) {
pst.setString(3, user.getStatus().toString());
pst.setString(4, user.getName());
pst.setInt(5, user.getCreatedBy());
- pst.setBoolean(6, user.getUserCardPrinted());
+ pst.setBoolean(6, user.isUserCardPrinted());
pst.setString(7, TextUtils.removeDiacriticals(user.getName()));
pst.setInt(8, user.getId());
}
@@ -312,14 +310,14 @@ public boolean save(UserDTO user) {
con.commit();
- return true;
+ return map(Set.of(user.getId())).get(user.getId());
} catch (Exception e) {
throw new DAOException(e);
}
}
@Override
- public boolean delete(UserDTO user) {
+ public boolean delete(UserDTO user) throws SearchException {
return withTransactionContext(
(UnsafeFunction) connection -> doDelete(connection, user));
}
@@ -384,7 +382,7 @@ private UserDTO populateDTO(ResultSet rs) throws SQLException {
dto.setName(rs.getString("name"));
dto.setType(rs.getInt("type"));
dto.setPhotoId(rs.getString("photo_id"));
- dto.setStatus(rs.getString("status"));
+ dto.setStatus(UserStatus.fromString(rs.getString("status")));
dto.setLoginId(rs.getInt("login_id"));
dto.setCreated(rs.getTimestamp("created"));
dto.setCreatedBy(rs.getInt("created_by"));
@@ -405,7 +403,7 @@ private UserDTO populateDTO(ResultSet rs) throws SQLException {
}
@Override
- public void markAsPrinted(Set ids) {
+ public void markAsPrinted(Collection ids) throws SearchException {
try (Connection con = datasource.getConnection()) {
String sql =
"UPDATE users SET user_card_printed = true "
@@ -426,7 +424,7 @@ public void markAsPrinted(Set ids) {
}
@Override
- public boolean updateUserStatus(Integer userId, UserStatus status) {
+ public boolean updateUserStatus(Integer userId, UserStatus status) throws SearchException {
try (Connection con = datasource.getConnection()) {
String sql = "UPDATE users SET status = ? " + "WHERE id = ?;";
diff --git a/src/main/java/biblivre/circulation/user/UserDTO.java b/src/main/java/biblivre/circulation/user/UserDTO.java
index 7a1c4764..84558391 100644
--- a/src/main/java/biblivre/circulation/user/UserDTO.java
+++ b/src/main/java/biblivre/circulation/user/UserDTO.java
@@ -23,9 +23,14 @@
import java.io.Serial;
import java.util.HashMap;
import java.util.Map;
+import lombok.Getter;
+import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.time.DateFormatUtils;
import org.json.JSONObject;
+@Setter
+@Getter
public class UserDTO extends AbstractDTO {
@Serial private static final long serialVersionUID = 1L;
@@ -35,7 +40,7 @@ public class UserDTO extends AbstractDTO {
private String photoId;
private UserStatus status;
private Integer loginId;
- private Boolean userCardPrinted;
+ private boolean isUserCardPrinted;
private Map fields;
@@ -46,96 +51,20 @@ public UserDTO() {
this.fields = new HashMap<>();
}
- public int getId() {
- return this.id;
- }
-
- public void setId(int id) {
- this.id = id;
- }
-
public String getEnrollment() {
return StringUtils.leftPad(String.valueOf(this.getId()), 5, "0");
}
- public int getType() {
- return this.type;
- }
-
- public void setType(Integer type) {
- this.type = type;
- }
-
- public String getPhotoId() {
- return this.photoId;
- }
-
- public void setPhotoId(String photoId) {
- this.photoId = photoId;
- }
-
- public UserStatus getStatus() {
- return this.status;
- }
-
- public void setStatus(UserStatus status) {
- this.status = status;
- }
-
- public void setStatus(String status) {
- this.status = UserStatus.fromString(status);
- }
-
- public Integer getLoginId() {
- return this.loginId;
- }
-
- public void setLoginId(Integer loginId) {
- this.loginId = loginId;
- }
-
- public String getName() {
- return this.name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public Map getFields() {
- return this.fields;
- }
-
- public void setFields(HashMap fields) {
- this.fields = fields;
- }
-
public void addField(String key, String value) {
this.fields.put(key, value);
}
- public Integer getCurrentLendings() {
- return this.currentLendings;
- }
-
- public void setCurrentLendings(Integer currentLendings) {
- this.currentLendings = currentLendings;
- }
-
- public Boolean getUserCardPrinted() {
- return this.userCardPrinted == null ? Boolean.FALSE : this.userCardPrinted;
- }
-
- public void setUserCardPrinted(Boolean userCardPrinted) {
- this.userCardPrinted = userCardPrinted;
- }
-
- public String getUsertypeName() {
- return this.usertypeName;
+ public boolean isInactive() {
+ return this.getStatus() == UserStatus.INACTIVE;
}
- public void setUsertypeName(String usertypeName) {
- this.usertypeName = usertypeName;
+ public boolean hasLogin() {
+ return loginId != null && loginId > 0;
}
@Override
@@ -153,8 +82,11 @@ public JSONObject toJSONObject() {
json.putOpt("fields", this.getFields());
json.putOpt("current_lendings", this.getCurrentLendings());
- json.putOpt("created", this.getCreated());
- json.putOpt("modified", this.getModified());
+ json.putOpt(
+ "created", DateFormatUtils.ISO_8601_EXTENDED_DATETIME_FORMAT.format(getCreated()));
+ json.putOpt(
+ "modified",
+ DateFormatUtils.ISO_8601_EXTENDED_DATETIME_FORMAT.format(getModified()));
return json;
}
diff --git a/src/main/java/biblivre/circulation/user/UserSearchDTO.java b/src/main/java/biblivre/circulation/user/UserSearchDTO.java
index 650f3bb9..ad638e8f 100644
--- a/src/main/java/biblivre/circulation/user/UserSearchDTO.java
+++ b/src/main/java/biblivre/circulation/user/UserSearchDTO.java
@@ -26,22 +26,25 @@
import java.io.Serial;
import java.text.ParseException;
import java.util.Date;
+import lombok.Getter;
+import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONException;
import org.json.JSONObject;
+@Getter
public final class UserSearchDTO extends AbstractDTO {
@Serial private static final long serialVersionUID = 1L;
- private SearchMode searchMode;
- private String field;
- private String query;
- private Integer type;
- private boolean pendingFines;
- private boolean lateLendings;
- private boolean loginAccess;
- private boolean userCardNeverPrinted;
- private boolean inactiveOnly;
+ @Setter private SearchMode searchMode;
+ @Setter private String field;
+ @Setter private String query;
+ @Setter private Integer type;
+ @Setter private boolean isPendingFines;
+ @Setter private boolean isLateLendings;
+ @Setter private boolean isLoginAccess;
+ @Setter private boolean isUserCardNeverPrinted;
+ @Setter private boolean isInactiveOnly;
private Date createdStartDate;
private Date createdEndDate;
private Date modifiedStartDate;
@@ -60,7 +63,7 @@ public UserSearchDTO(String jsonString) throws ValidationException {
private void fromJson(String jsonString) throws JSONException, ParseException {
JSONObject json = new JSONObject(jsonString);
- this.setSearchMode(SearchMode.fromString(json.optString("search_mode")));
+ this.setSearchMode(SearchMode.fromString(json.optString("mode")));
this.setField(json.optString("field"));
this.setQuery(json.optString("query"));
this.setType(json.optInt("type"));
@@ -75,82 +78,6 @@ private void fromJson(String jsonString) throws JSONException, ParseException {
this.setModifiedEndDate(json.optString("modified_end"));
}
- public SearchMode getSearchMode() {
- return this.searchMode;
- }
-
- public void setSearchMode(SearchMode searchMode) {
- this.searchMode = searchMode;
- }
-
- public String getField() {
- return this.field;
- }
-
- public void setField(String field) {
- this.field = field;
- }
-
- public String getQuery() {
- return this.query;
- }
-
- public void setQuery(String query) {
- this.query = query;
- }
-
- public Integer getType() {
- return this.type;
- }
-
- public void setType(Integer type) {
- this.type = type;
- }
-
- public boolean isPendingFines() {
- return this.pendingFines;
- }
-
- public void setPendingFines(boolean pendingFines) {
- this.pendingFines = pendingFines;
- }
-
- public boolean isLateLendings() {
- return this.lateLendings;
- }
-
- public void setLateLendings(boolean lateLendings) {
- this.lateLendings = lateLendings;
- }
-
- public boolean isLoginAccess() {
- return this.loginAccess;
- }
-
- public void setLoginAccess(boolean loginAccess) {
- this.loginAccess = loginAccess;
- }
-
- public boolean isUserCardNeverPrinted() {
- return this.userCardNeverPrinted;
- }
-
- public void setUserCardNeverPrinted(boolean userCardNeverPrinted) {
- this.userCardNeverPrinted = userCardNeverPrinted;
- }
-
- public boolean isInactiveOnly() {
- return this.inactiveOnly;
- }
-
- public void setInactiveOnly(boolean inactiveOnly) {
- this.inactiveOnly = inactiveOnly;
- }
-
- public Date getCreatedStartDate() {
- return this.createdStartDate;
- }
-
public void setCreatedStartDate(String createdStartDate) throws ParseException {
this.createdStartDate = null;
if (StringUtils.isNotBlank(createdStartDate)) {
@@ -158,10 +85,6 @@ public void setCreatedStartDate(String createdStartDate) throws ParseException {
}
}
- public Date getCreatedEndDate() {
- return this.createdEndDate;
- }
-
public void setCreatedEndDate(String createdEndDate) throws ParseException {
this.createdEndDate = null;
if (StringUtils.isNotBlank(createdEndDate)) {
@@ -169,10 +92,6 @@ public void setCreatedEndDate(String createdEndDate) throws ParseException {
}
}
- public Date getModifiedStartDate() {
- return this.modifiedStartDate;
- }
-
public void setModifiedStartDate(String modifiedStartDate) throws ParseException {
this.modifiedStartDate = null;
if (StringUtils.isNotBlank(modifiedStartDate)) {
@@ -180,14 +99,22 @@ public void setModifiedStartDate(String modifiedStartDate) throws ParseException
}
}
- public Date getModifiedEndDate() {
- return this.modifiedEndDate;
- }
-
public void setModifiedEndDate(String modifiedEndDate) throws ParseException {
this.modifiedEndDate = null;
if (StringUtils.isNotBlank(modifiedEndDate)) {
this.modifiedEndDate = TextUtils.parseDate(modifiedEndDate);
}
}
+
+ public boolean isSimpleSearch() {
+ return this.searchMode == SearchMode.SIMPLE;
+ }
+
+ public boolean isListAll() {
+ return this.query.isEmpty();
+ }
+
+ public boolean isSearchById() {
+ return StringUtils.isNumeric(query);
+ }
}
diff --git a/src/main/java/biblivre/circulation/user_cards/Handler.java b/src/main/java/biblivre/circulation/user_cards/Handler.java
index 3245f490..ab866fbe 100644
--- a/src/main/java/biblivre/circulation/user_cards/Handler.java
+++ b/src/main/java/biblivre/circulation/user_cards/Handler.java
@@ -26,6 +26,7 @@
import biblivre.core.enums.ActionResult;
import biblivre.core.file.DiskFile;
import biblivre.labels.print.LabelPrintDTO;
+import biblivre.search.SearchException;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
@@ -62,7 +63,12 @@ public void downloadPdf(ExtendedRequest request, ExtendedResponse response) {
LabelPrintDTO dto = (LabelPrintDTO) request.getScopedSessionAttribute(printId);
final DiskFile exportFile = userBO.printUserCardsToPDF(dto, request.getTranslationsMap());
- userBO.markAsPrinted(dto.getIds());
+ try {
+ userBO.markAsPrinted(dto.getIds());
+ } catch (SearchException e) {
+ this.setMessage(ActionResult.WARNING, "error.invalid_parameters");
+ return;
+ }
this.setFile(exportFile);
diff --git a/src/main/java/biblivre/core/DTOCollection.java b/src/main/java/biblivre/core/DTOCollection.java
index ea0ee9ab..d07b1a69 100644
--- a/src/main/java/biblivre/core/DTOCollection.java
+++ b/src/main/java/biblivre/core/DTOCollection.java
@@ -42,7 +42,7 @@ public void setPaging(PagingDTO paging) {
this.paging = paging;
}
- public int getRecordLimit() {
+ public long getRecordLimit() {
if (this.paging == null) {
return 0;
}
diff --git a/src/main/java/biblivre/core/PagingDTO.java b/src/main/java/biblivre/core/PagingDTO.java
index 577563fc..ef3bc238 100644
--- a/src/main/java/biblivre/core/PagingDTO.java
+++ b/src/main/java/biblivre/core/PagingDTO.java
@@ -20,15 +20,17 @@
package biblivre.core;
import java.util.Date;
+import lombok.Getter;
+import lombok.Setter;
import org.json.JSONObject;
public final class PagingDTO implements IFJson {
- private int recordCount;
- private int recordOffset;
- private int recordsPerPage;
- private int recordLimit;
+ @Getter @Setter private long recordCount;
+ @Getter @Setter private long recordOffset;
+ @Setter @Getter private long recordsPerPage;
+ @Setter @Getter private long recordLimit;
- private transient double time;
+ @Setter @Getter private transient double time;
private transient long startTime;
private transient long endTime;
@@ -36,7 +38,7 @@ public PagingDTO() {
this.startTimer();
}
- public PagingDTO(int recordCount, int recordsPerPage, int recordOffset) {
+ public PagingDTO(long recordCount, int recordsPerPage, int recordOffset) {
this.startTimer();
this.recordCount = recordCount;
@@ -44,47 +46,7 @@ public PagingDTO(int recordCount, int recordsPerPage, int recordOffset) {
this.recordOffset = recordOffset;
}
- public int getRecordCount() {
- return this.recordCount;
- }
-
- public void setRecordCount(int recordCount) {
- this.recordCount = recordCount;
- }
-
- public int getRecordsPerPage() {
- return this.recordsPerPage;
- }
-
- public void setRecordsPerPage(int recordsPerPage) {
- this.recordsPerPage = recordsPerPage;
- }
-
- public void setRecordsPerPage(String recordsPerPage) {
- try {
- this.recordsPerPage = Integer.parseInt(recordsPerPage);
- } catch (Exception e) {
- this.recordsPerPage = 20;
- }
- }
-
- public int getRecordOffset() {
- return this.recordOffset;
- }
-
- public void setRecordOffset(int recordOffset) {
- this.recordOffset = recordOffset;
- }
-
- public int getRecordLimit() {
- return this.recordLimit;
- }
-
- public void setRecordLimit(int recordLimit) {
- this.recordLimit = recordLimit;
- }
-
- public int getPage() {
+ public long getPage() {
if (this.getRecordsPerPage() == 0) {
return 1;
}
@@ -96,7 +58,7 @@ public void setPage(int page) {
this.recordOffset = (page - 1) * this.getRecordsPerPage();
}
- public int getPageCount() {
+ public long getPageCount() {
if (this.getRecordCount() == 0) {
return 0;
}
@@ -114,14 +76,6 @@ public int getPageCount() {
return (int) Math.ceil(count / this.getRecordsPerPage());
}
- public double getTime() {
- return this.time;
- }
-
- public void setTime(double time) {
- this.time = time;
- }
-
public void setTime(long start, long end) {
this.time = (end - start) / 1000.0;
}
diff --git a/src/main/java/biblivre/search/ElasticsearchClientConfiguration.java b/src/main/java/biblivre/search/ElasticsearchClientConfiguration.java
new file mode 100644
index 00000000..fb9a7349
--- /dev/null
+++ b/src/main/java/biblivre/search/ElasticsearchClientConfiguration.java
@@ -0,0 +1,31 @@
+package biblivre.search;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.elasticsearch.client.ClientConfiguration;
+import org.springframework.data.elasticsearch.client.elc.ElasticsearchConfiguration;
+import org.springframework.data.elasticsearch.support.HttpHeaders;
+
+@Configuration
+public class ElasticsearchClientConfiguration extends ElasticsearchConfiguration {
+ @Value("${elasticsearch.server.url}")
+ String serverUrl;
+
+ @Value("${elasticsearch.api.key}")
+ String apiKey;
+
+ @Override
+ public ClientConfiguration clientConfiguration() {
+ return ClientConfiguration.builder()
+ .connectedTo(serverUrl)
+ .usingSsl()
+ .withHeaders(
+ () -> {
+ var headers = new HttpHeaders();
+ headers.add("Authorization", STR."ApiKey \{apiKey}");
+
+ return headers;
+ })
+ .build();
+ }
+}
diff --git a/src/main/java/biblivre/search/SearchException.java b/src/main/java/biblivre/search/SearchException.java
new file mode 100644
index 00000000..4d89ed20
--- /dev/null
+++ b/src/main/java/biblivre/search/SearchException.java
@@ -0,0 +1,5 @@
+package biblivre.search;
+
+public class SearchException extends Exception {
+ public SearchException(String errorSearchingUsers, Throwable e) {}
+}
diff --git a/src/main/java/biblivre/search/user/IndexableUser.java b/src/main/java/biblivre/search/user/IndexableUser.java
new file mode 100644
index 00000000..222df407
--- /dev/null
+++ b/src/main/java/biblivre/search/user/IndexableUser.java
@@ -0,0 +1,80 @@
+package biblivre.search.user;
+
+import java.util.Date;
+import java.util.Map;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.domain.Persistable;
+import org.springframework.data.elasticsearch.annotations.Document;
+import org.springframework.data.elasticsearch.annotations.Field;
+import org.springframework.data.elasticsearch.annotations.FieldType;
+import org.springframework.data.elasticsearch.annotations.Setting;
+
+@Getter
+@AllArgsConstructor
+@Document(indexName = "users")
+@Setting(settingPath = "/META-INF/elasticsearch/users.index.settings.json")
+public class IndexableUser implements Persistable {
+ @Id private int id;
+
+ @Field(type = FieldType.Text, analyzer = "name")
+ private String name;
+
+ @Field(type = FieldType.Integer)
+ private int type;
+
+ private String photoId;
+
+ @Field(type = FieldType.Keyword)
+ private String status;
+
+ @Field(type = FieldType.Integer)
+ private int loginId;
+
+ @Field(type = FieldType.Date, pattern = "uuuuMMdd HHmmss.SSSXXX")
+ private Date created;
+
+ @Field(type = FieldType.Integer)
+ private int createdBy;
+
+ @Field(type = FieldType.Date, pattern = "uuuuMMdd HHmmss.SSSXXX")
+ private Date modified;
+
+ @Field(type = FieldType.Integer)
+ private int modifiedBy;
+
+ @Field(type = FieldType.Boolean)
+ private boolean userCardPrinted;
+
+ @Field(type = FieldType.Flattened)
+ private Map fields;
+
+ @Field(type = FieldType.Boolean)
+ private boolean hasPendingFines;
+
+ @Field(type = FieldType.Boolean)
+ private boolean hasPendingLoans;
+
+ @Field(type = FieldType.Boolean)
+ private boolean hasLogin;
+
+ @Field(type = FieldType.Boolean)
+ private boolean isInactive;
+
+ @Field(type = FieldType.Keyword)
+ private String schema;
+
+ @Field(type = FieldType.Keyword)
+ private String tenant;
+
+ @Override
+ public boolean isNew() {
+ return id > 0;
+ }
+
+ @Override
+ public Integer getId() {
+ return id;
+ }
+}
diff --git a/src/main/java/biblivre/search/user/IndexableUserQueryParameters.java b/src/main/java/biblivre/search/user/IndexableUserQueryParameters.java
new file mode 100644
index 00000000..8692c115
--- /dev/null
+++ b/src/main/java/biblivre/search/user/IndexableUserQueryParameters.java
@@ -0,0 +1,12 @@
+package biblivre.search.user;
+
+public record IndexableUserQueryParameters(
+ long id,
+ String name,
+ String schema,
+ String tenant,
+ boolean hasLogin,
+ boolean hasPendingFines,
+ boolean hasPendingLoans,
+ boolean hasNeverPrintedUserCard,
+ boolean isInactive) {}
diff --git a/src/main/java/biblivre/search/user/IndexableUserRepository.java b/src/main/java/biblivre/search/user/IndexableUserRepository.java
new file mode 100644
index 00000000..aad951c1
--- /dev/null
+++ b/src/main/java/biblivre/search/user/IndexableUserRepository.java
@@ -0,0 +1,7 @@
+package biblivre.search.user;
+
+import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
+
+public interface IndexableUserRepository extends ElasticsearchRepository {
+ void deleteBySchemaAndTenant(String schema, String tenant);
+}
diff --git a/src/main/resources/META-INF/elasticsearch/users.index.settings.json b/src/main/resources/META-INF/elasticsearch/users.index.settings.json
new file mode 100644
index 00000000..6400b4ca
--- /dev/null
+++ b/src/main/resources/META-INF/elasticsearch/users.index.settings.json
@@ -0,0 +1,14 @@
+{
+ "analysis": {
+ "analyzer": {
+ "name": {
+ "type": "custom",
+ "tokenizer": "standard",
+ "filter": [
+ "lowercase",
+ "asciifolding"
+ ]
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/webapp/WEB-INF/jsp/circulation/user.jsp b/src/main/webapp/WEB-INF/jsp/circulation/user.jsp
index 36f655ab..013f409c 100644
--- a/src/main/webapp/WEB-INF/jsp/circulation/user.jsp
+++ b/src/main/webapp/WEB-INF/jsp/circulation/user.jsp
@@ -44,7 +44,7 @@
$(document).ready(function() {
var global = Globalize.culture().calendars.standard;
- $('#search_box input.datepicker').Zebra_DatePicker({
+ $('input.datepicker').Zebra_DatePicker({
days: global.days.names,
days_abbr: global.days.namesAbbr,
months: global.months.names,
diff --git a/src/test/java/biblivre/administration/report/JasperReportBOTest.java b/src/test/java/biblivre/administration/report/JasperReportBOTest.java
index 2e2e6d35..084aa0c2 100644
--- a/src/test/java/biblivre/administration/report/JasperReportBOTest.java
+++ b/src/test/java/biblivre/administration/report/JasperReportBOTest.java
@@ -14,6 +14,7 @@
import biblivre.core.translations.TranslationsMap;
import biblivre.core.utils.Constants;
import biblivre.core.utils.StringPool;
+import biblivre.search.SearchException;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
@@ -34,7 +35,7 @@ public class JasperReportBOTest extends AbstractContainerDatabaseTest {
@Autowired private TranslationBO translationBO;
@Test
- public void testAllReportsInjected() {
+ public void testAllReportsInjected() throws SearchException {
SchemaThreadLocal.setSchema(Constants.SINGLE_SCHEMA);
UserDTO user = newReaderUser();