mirror of
https://github.com/sismics/docs.git
synced 2024-11-22 05:57:57 +01:00
Closes #159: cancel routes + email at route step validation
This commit is contained in:
parent
5b8cd18128
commit
706d244ff8
@ -26,6 +26,7 @@ Features
|
|||||||
--------
|
--------
|
||||||
|
|
||||||
- Responsive user interface
|
- Responsive user interface
|
||||||
|
- Workflow system ![New!](https://www.sismics.com/public/img/new.png)
|
||||||
- Optical character recognition
|
- Optical character recognition
|
||||||
- Support image, PDF, ODT and DOCX files
|
- Support image, PDF, ODT and DOCX files
|
||||||
- Flexible search engine
|
- Flexible search engine
|
||||||
|
@ -82,4 +82,9 @@ public class Constants {
|
|||||||
* Email template for password recovery.
|
* Email template for password recovery.
|
||||||
*/
|
*/
|
||||||
public static final String EMAIL_TEMPLATE_PASSWORD_RECOVERY = "password_recovery";
|
public static final String EMAIL_TEMPLATE_PASSWORD_RECOVERY = "password_recovery";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Email template for route step validate.
|
||||||
|
*/
|
||||||
|
public static final String EMAIL_TEMPLATE_ROUTE_STEP_VALIDATE = "route_step_validate";
|
||||||
}
|
}
|
||||||
|
@ -149,7 +149,7 @@ public class AclDao {
|
|||||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||||
|
|
||||||
// Create audit log
|
// Create audit log
|
||||||
Query q = em.createQuery("from Acl a where a.sourceId = :sourceId and a.perm = :perm and a.targetId = :targetId and a.type = :type");
|
Query q = em.createQuery("from Acl a where a.sourceId = :sourceId and a.perm = :perm and a.targetId = :targetId and a.type = :type and a.deleteDate is null");
|
||||||
q.setParameter("sourceId", sourceId);
|
q.setParameter("sourceId", sourceId);
|
||||||
q.setParameter("perm", perm);
|
q.setParameter("perm", perm);
|
||||||
q.setParameter("targetId", targetId);
|
q.setParameter("targetId", targetId);
|
||||||
@ -160,7 +160,7 @@ public class AclDao {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Soft delete the ACLs
|
// Soft delete the ACLs
|
||||||
q = em.createQuery("update Acl a set a.deleteDate = :dateNow where a.sourceId = :sourceId and a.perm = :perm and a.targetId = :targetId and a.type = :type");
|
q = em.createQuery("update Acl a set a.deleteDate = :dateNow where a.sourceId = :sourceId and a.perm = :perm and a.targetId = :targetId and a.type = :type and a.deleteDate is null");
|
||||||
q.setParameter("sourceId", sourceId);
|
q.setParameter("sourceId", sourceId);
|
||||||
q.setParameter("perm", perm);
|
q.setParameter("perm", perm);
|
||||||
q.setParameter("targetId", targetId);
|
q.setParameter("targetId", targetId);
|
||||||
|
@ -86,4 +86,23 @@ public class RouteDao {
|
|||||||
}
|
}
|
||||||
return dtoList;
|
return dtoList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a route and the associated steps.
|
||||||
|
*
|
||||||
|
* @param routeId Route ID
|
||||||
|
*/
|
||||||
|
public void deleteRoute(String routeId) {
|
||||||
|
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||||
|
|
||||||
|
em.createNativeQuery("update T_ROUTE_STEP rs set rs.RTP_DELETEDATE_D = :dateNow where rs.RTP_IDROUTE_C = :routeId and rs.RTP_DELETEDATE_D is null")
|
||||||
|
.setParameter("routeId", routeId)
|
||||||
|
.setParameter("dateNow", new Date())
|
||||||
|
.executeUpdate();
|
||||||
|
|
||||||
|
em.createNativeQuery("update T_ROUTE r set r.RTE_DELETEDATE_D = :dateNow where r.RTE_ID_C = :routeId and r.RTE_DELETEDATE_D is null")
|
||||||
|
.setParameter("routeId", routeId)
|
||||||
|
.setParameter("dateNow", new Date())
|
||||||
|
.executeUpdate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ public class RouteStepDao {
|
|||||||
Map<String, Object> parameterMap = new HashMap<>();
|
Map<String, Object> parameterMap = new HashMap<>();
|
||||||
List<String> criteriaList = new ArrayList<>();
|
List<String> criteriaList = new ArrayList<>();
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder("select rs.RTP_ID_C, rs.RTP_NAME_C c0, rs.RTP_TYPE_C c1, rs.RTP_TRANSITION_C c2, rs.RTP_COMMENT_C c3, rs.RTP_IDTARGET_C c4, u.USE_USERNAME_C as targetUsername, g.GRP_NAME_C, rs.RTP_ENDDATE_D c5, uv.USE_USERNAME_C as validatorUsername, rs.RTP_ORDER_N c6")
|
StringBuilder sb = new StringBuilder("select rs.RTP_ID_C, rs.RTP_NAME_C c0, rs.RTP_TYPE_C c1, rs.RTP_TRANSITION_C c2, rs.RTP_COMMENT_C c3, rs.RTP_IDTARGET_C c4, u.USE_USERNAME_C as targetUsername, g.GRP_NAME_C, rs.RTP_ENDDATE_D c5, uv.USE_USERNAME_C as validatorUsername, rs.RTP_IDROUTE_C, rs.RTP_ORDER_N c6")
|
||||||
.append(" from T_ROUTE_STEP rs ")
|
.append(" from T_ROUTE_STEP rs ")
|
||||||
.append(" join T_ROUTE r on r.RTE_ID_C = rs.RTP_IDROUTE_C ")
|
.append(" join T_ROUTE r on r.RTE_ID_C = rs.RTP_IDROUTE_C ")
|
||||||
.append(" left join T_USER uv on uv.USE_ID_C = rs.RTP_IDVALIDATORUSER_C ")
|
.append(" left join T_USER uv on uv.USE_ID_C = rs.RTP_IDVALIDATORUSER_C ")
|
||||||
@ -88,7 +88,7 @@ public class RouteStepDao {
|
|||||||
if (criteria.getEndDateIsNull() != null) {
|
if (criteria.getEndDateIsNull() != null) {
|
||||||
criteriaList.add("RTP_ENDDATE_D is " + (criteria.getEndDateIsNull() ? "" : "not") + " null");
|
criteriaList.add("RTP_ENDDATE_D is " + (criteria.getEndDateIsNull() ? "" : "not") + " null");
|
||||||
}
|
}
|
||||||
criteriaList.add("r.RTE_DELETEDATE_D is null");
|
criteriaList.add("rs.RTP_DELETEDATE_D is null");
|
||||||
|
|
||||||
if (!criteriaList.isEmpty()) {
|
if (!criteriaList.isEmpty()) {
|
||||||
sb.append(" where ");
|
sb.append(" where ");
|
||||||
@ -123,7 +123,8 @@ public class RouteStepDao {
|
|||||||
}
|
}
|
||||||
Timestamp endDateTimestamp = (Timestamp) o[i++];
|
Timestamp endDateTimestamp = (Timestamp) o[i++];
|
||||||
dto.setEndDateTimestamp(endDateTimestamp == null ? null : endDateTimestamp.getTime());
|
dto.setEndDateTimestamp(endDateTimestamp == null ? null : endDateTimestamp.getTime());
|
||||||
dto.setValidatorUserName((String) o[i]);
|
dto.setValidatorUserName((String) o[i++]);
|
||||||
|
dto.setRouteId((String) o[i]);
|
||||||
dtoList.add(dto);
|
dtoList.add(dto);
|
||||||
}
|
}
|
||||||
return dtoList;
|
return dtoList;
|
||||||
|
@ -276,7 +276,14 @@ public class UserDao {
|
|||||||
criteriaList.add("lower(u.USE_USERNAME_C) like lower(:search)");
|
criteriaList.add("lower(u.USE_USERNAME_C) like lower(:search)");
|
||||||
parameterMap.put("search", "%" + criteria.getSearch() + "%");
|
parameterMap.put("search", "%" + criteria.getSearch() + "%");
|
||||||
}
|
}
|
||||||
|
if (criteria.getUserId() != null) {
|
||||||
|
criteriaList.add("u.USE_ID_C = :userId");
|
||||||
|
parameterMap.put("userId", criteria.getUserId());
|
||||||
|
}
|
||||||
|
if (criteria.getUserName() != null) {
|
||||||
|
criteriaList.add("u.USE_USERNAME_C = :userName");
|
||||||
|
parameterMap.put("userName", criteria.getUserName());
|
||||||
|
}
|
||||||
if (criteria.getGroupId() != null) {
|
if (criteria.getGroupId() != null) {
|
||||||
sb.append(" join T_USER_GROUP ug on ug.UGP_IDUSER_C = u.USE_ID_C and ug.UGP_IDGROUP_C = :groupId and ug.UGP_DELETEDATE_D is null ");
|
sb.append(" join T_USER_GROUP ug on ug.UGP_IDUSER_C = u.USE_ID_C and ug.UGP_IDGROUP_C = :groupId and ug.UGP_DELETEDATE_D is null ");
|
||||||
parameterMap.put("groupId", criteria.getGroupId());
|
parameterMap.put("groupId", criteria.getGroupId());
|
||||||
|
@ -16,6 +16,16 @@ public class UserCriteria {
|
|||||||
*/
|
*/
|
||||||
private String groupId;
|
private String groupId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User ID.
|
||||||
|
*/
|
||||||
|
private String userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Username.
|
||||||
|
*/
|
||||||
|
private String userName;
|
||||||
|
|
||||||
public String getSearch() {
|
public String getSearch() {
|
||||||
return search;
|
return search;
|
||||||
}
|
}
|
||||||
@ -33,4 +43,22 @@ public class UserCriteria {
|
|||||||
this.groupId = groupId;
|
this.groupId = groupId;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getUserId() {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserCriteria setUserId(String userId) {
|
||||||
|
this.userId = userId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserName() {
|
||||||
|
return userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserCriteria setUserName(String userName) {
|
||||||
|
this.userName = userName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,6 +62,11 @@ public class RouteStepDto {
|
|||||||
*/
|
*/
|
||||||
private String validatorUserName;
|
private String validatorUserName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route ID.
|
||||||
|
*/
|
||||||
|
private String routeId;
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
@ -152,6 +157,15 @@ public class RouteStepDto {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getRouteId() {
|
||||||
|
return routeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteStepDto setRouteId(String routeId) {
|
||||||
|
this.routeId = routeId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transform in JSON.
|
* Transform in JSON.
|
||||||
*
|
*
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package com.sismics.docs.core.dao.jpa.dto;
|
package com.sismics.docs.core.dao.jpa.dto;
|
||||||
|
|
||||||
|
import com.google.common.base.MoreObjects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User DTO.
|
* User DTO.
|
||||||
*
|
*
|
||||||
@ -110,4 +112,13 @@ public class UserDto {
|
|||||||
public void setTotpKey(String totpKey) {
|
public void setTotpKey(String totpKey) {
|
||||||
this.totpKey = totpKey;
|
this.totpKey = totpKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return MoreObjects.toStringHelper(this)
|
||||||
|
.add("id", id)
|
||||||
|
.add("username", username)
|
||||||
|
.add("email", email)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package com.sismics.docs.core.event;
|
package com.sismics.docs.core.event;
|
||||||
|
|
||||||
import com.google.common.base.MoreObjects;
|
import com.google.common.base.MoreObjects;
|
||||||
|
import com.sismics.docs.core.dao.jpa.dto.UserDto;
|
||||||
import com.sismics.docs.core.model.jpa.PasswordRecovery;
|
import com.sismics.docs.core.model.jpa.PasswordRecovery;
|
||||||
import com.sismics.docs.core.model.jpa.User;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event fired on user's password lost event.
|
* Event fired on user's password lost event.
|
||||||
@ -13,18 +13,18 @@ public class PasswordLostEvent {
|
|||||||
/**
|
/**
|
||||||
* User.
|
* User.
|
||||||
*/
|
*/
|
||||||
private User user;
|
private UserDto user;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Password recovery request.
|
* Password recovery request.
|
||||||
*/
|
*/
|
||||||
private PasswordRecovery passwordRecovery;
|
private PasswordRecovery passwordRecovery;
|
||||||
|
|
||||||
public User getUser() {
|
public UserDto getUser() {
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUser(User user) {
|
public void setUser(UserDto user) {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
package com.sismics.docs.core.event;
|
||||||
|
|
||||||
|
import com.google.common.base.MoreObjects;
|
||||||
|
import com.sismics.docs.core.dao.jpa.dto.UserDto;
|
||||||
|
import com.sismics.docs.core.model.jpa.Document;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event fired on route step validation event.
|
||||||
|
*
|
||||||
|
* @author bgamard
|
||||||
|
*/
|
||||||
|
public class RouteStepValidateEvent {
|
||||||
|
/**
|
||||||
|
* User.
|
||||||
|
*/
|
||||||
|
private UserDto user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Document linked to the route.
|
||||||
|
*/
|
||||||
|
private Document document;
|
||||||
|
|
||||||
|
public UserDto getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUser(UserDto user) {
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Document getDocument() {
|
||||||
|
return document;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteStepValidateEvent setDocument(Document document) {
|
||||||
|
this.document = document;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return MoreObjects.toStringHelper(this)
|
||||||
|
.add("user", user)
|
||||||
|
.add("document", document)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -2,9 +2,9 @@ package com.sismics.docs.core.listener.async;
|
|||||||
|
|
||||||
import com.google.common.eventbus.Subscribe;
|
import com.google.common.eventbus.Subscribe;
|
||||||
import com.sismics.docs.core.constant.Constants;
|
import com.sismics.docs.core.constant.Constants;
|
||||||
|
import com.sismics.docs.core.dao.jpa.dto.UserDto;
|
||||||
import com.sismics.docs.core.event.PasswordLostEvent;
|
import com.sismics.docs.core.event.PasswordLostEvent;
|
||||||
import com.sismics.docs.core.model.jpa.PasswordRecovery;
|
import com.sismics.docs.core.model.jpa.PasswordRecovery;
|
||||||
import com.sismics.docs.core.model.jpa.User;
|
|
||||||
import com.sismics.docs.core.util.TransactionUtil;
|
import com.sismics.docs.core.util.TransactionUtil;
|
||||||
import com.sismics.util.EmailUtil;
|
import com.sismics.util.EmailUtil;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -38,7 +38,7 @@ public class PasswordLostAsyncListener {
|
|||||||
TransactionUtil.handle(new Runnable() {
|
TransactionUtil.handle(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
final User user = passwordLostEvent.getUser();
|
final UserDto user = passwordLostEvent.getUser();
|
||||||
final PasswordRecovery passwordRecovery = passwordLostEvent.getPasswordRecovery();
|
final PasswordRecovery passwordRecovery = passwordLostEvent.getPasswordRecovery();
|
||||||
|
|
||||||
// Send the password recovery email
|
// Send the password recovery email
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
package com.sismics.docs.core.listener.async;
|
||||||
|
|
||||||
|
import com.google.common.eventbus.Subscribe;
|
||||||
|
import com.sismics.docs.core.constant.Constants;
|
||||||
|
import com.sismics.docs.core.dao.jpa.dto.UserDto;
|
||||||
|
import com.sismics.docs.core.event.RouteStepValidateEvent;
|
||||||
|
import com.sismics.docs.core.util.TransactionUtil;
|
||||||
|
import com.sismics.util.EmailUtil;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener for route step validate.
|
||||||
|
*
|
||||||
|
* @author bgamard
|
||||||
|
*/
|
||||||
|
public class RouteStepValidateAsyncListener {
|
||||||
|
/**
|
||||||
|
* Logger.
|
||||||
|
*/
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(RouteStepValidateAsyncListener.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle events.
|
||||||
|
*
|
||||||
|
* @param routeStepValidateEvent Event
|
||||||
|
*/
|
||||||
|
@Subscribe
|
||||||
|
public void onRouteStepValidate(final RouteStepValidateEvent routeStepValidateEvent) {
|
||||||
|
if (log.isInfoEnabled()) {
|
||||||
|
log.info("Route step validate event: " + routeStepValidateEvent.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
TransactionUtil.handle(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
final UserDto user = routeStepValidateEvent.getUser();
|
||||||
|
|
||||||
|
// Send the password recovery email
|
||||||
|
Map<String, Object> paramRootMap = new HashMap<>();
|
||||||
|
paramRootMap.put("user_name", user.getUsername());
|
||||||
|
paramRootMap.put("document_id", routeStepValidateEvent.getDocument().getId());
|
||||||
|
paramRootMap.put("document_title", routeStepValidateEvent.getDocument().getTitle());
|
||||||
|
|
||||||
|
EmailUtil.sendEmail(Constants.EMAIL_TEMPLATE_ROUTE_STEP_VALIDATE, user, paramRootMap);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -104,6 +104,7 @@ public class AppContext {
|
|||||||
|
|
||||||
mailEventBus = newAsyncEventBus();
|
mailEventBus = newAsyncEventBus();
|
||||||
mailEventBus.register(new PasswordLostAsyncListener());
|
mailEventBus.register(new PasswordLostAsyncListener());
|
||||||
|
mailEventBus.register(new RouteStepValidateAsyncListener());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -203,6 +203,7 @@ public class User implements Loggable {
|
|||||||
return MoreObjects.toStringHelper(this)
|
return MoreObjects.toStringHelper(this)
|
||||||
.add("id", id)
|
.add("id", id)
|
||||||
.add("username", username)
|
.add("username", username)
|
||||||
|
.add("email", email)
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,21 @@
|
|||||||
package com.sismics.docs.core.util;
|
package com.sismics.docs.core.util;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.sismics.docs.core.constant.AclTargetType;
|
||||||
import com.sismics.docs.core.constant.AclType;
|
import com.sismics.docs.core.constant.AclType;
|
||||||
import com.sismics.docs.core.constant.PermType;
|
import com.sismics.docs.core.constant.PermType;
|
||||||
import com.sismics.docs.core.dao.jpa.AclDao;
|
import com.sismics.docs.core.dao.jpa.AclDao;
|
||||||
|
import com.sismics.docs.core.dao.jpa.DocumentDao;
|
||||||
|
import com.sismics.docs.core.dao.jpa.UserDao;
|
||||||
|
import com.sismics.docs.core.dao.jpa.criteria.UserCriteria;
|
||||||
import com.sismics.docs.core.dao.jpa.dto.RouteStepDto;
|
import com.sismics.docs.core.dao.jpa.dto.RouteStepDto;
|
||||||
|
import com.sismics.docs.core.dao.jpa.dto.UserDto;
|
||||||
|
import com.sismics.docs.core.event.RouteStepValidateEvent;
|
||||||
|
import com.sismics.docs.core.model.context.AppContext;
|
||||||
import com.sismics.docs.core.model.jpa.Acl;
|
import com.sismics.docs.core.model.jpa.Acl;
|
||||||
|
import com.sismics.docs.core.model.jpa.Document;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Routing utilities.
|
* Routing utilities.
|
||||||
@ -38,4 +49,28 @@ public class RoutingUtil {
|
|||||||
aclDao.create(acl, userId);
|
aclDao.create(acl, userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void sendRouteStepEmail(String documentId, RouteStepDto routeStepDto) {
|
||||||
|
DocumentDao documentDao = new DocumentDao();
|
||||||
|
Document document = documentDao.getById(documentId);
|
||||||
|
|
||||||
|
List<UserDto> userDtoList = Lists.newArrayList();
|
||||||
|
UserDao userDao = new UserDao();
|
||||||
|
switch (AclTargetType.valueOf(routeStepDto.getTargetType())) {
|
||||||
|
case USER:
|
||||||
|
userDtoList.addAll(userDao.findByCriteria(new UserCriteria().setUserId(routeStepDto.getTargetId()), null));
|
||||||
|
break;
|
||||||
|
case GROUP:
|
||||||
|
userDtoList.addAll(userDao.findByCriteria(new UserCriteria().setGroupId(routeStepDto.getTargetId()), null));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fire route step validate events
|
||||||
|
for (UserDto userDto : userDtoList) {
|
||||||
|
RouteStepValidateEvent routeStepValidateEvent = new RouteStepValidateEvent();
|
||||||
|
routeStepValidateEvent.setUser(userDto);
|
||||||
|
routeStepValidateEvent.setDocument(document);
|
||||||
|
AppContext.getInstance().getMailEventBus().post(routeStepValidateEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,8 @@ import com.google.common.base.Strings;
|
|||||||
import com.sismics.docs.core.constant.ConfigType;
|
import com.sismics.docs.core.constant.ConfigType;
|
||||||
import com.sismics.docs.core.constant.Constants;
|
import com.sismics.docs.core.constant.Constants;
|
||||||
import com.sismics.docs.core.dao.jpa.ConfigDao;
|
import com.sismics.docs.core.dao.jpa.ConfigDao;
|
||||||
|
import com.sismics.docs.core.dao.jpa.dto.UserDto;
|
||||||
import com.sismics.docs.core.model.jpa.Config;
|
import com.sismics.docs.core.model.jpa.Config;
|
||||||
import com.sismics.docs.core.model.jpa.User;
|
|
||||||
import com.sismics.docs.core.util.ConfigUtil;
|
import com.sismics.docs.core.util.ConfigUtil;
|
||||||
import freemarker.template.Configuration;
|
import freemarker.template.Configuration;
|
||||||
import freemarker.template.DefaultObjectWrapperBuilder;
|
import freemarker.template.DefaultObjectWrapperBuilder;
|
||||||
@ -64,7 +64,7 @@ public class EmailUtil {
|
|||||||
* @param subject Email subject
|
* @param subject Email subject
|
||||||
* @param paramMap Email parameters
|
* @param paramMap Email parameters
|
||||||
*/
|
*/
|
||||||
public static void sendEmail(String templateName, User recipientUser, String subject, Map<String, Object> paramMap) {
|
public static void sendEmail(String templateName, UserDto recipientUser, String subject, Map<String, Object> paramMap) {
|
||||||
if (log.isInfoEnabled()) {
|
if (log.isInfoEnabled()) {
|
||||||
log.info("Sending email from template=" + templateName + " to user " + recipientUser);
|
log.info("Sending email from template=" + templateName + " to user " + recipientUser);
|
||||||
}
|
}
|
||||||
@ -154,7 +154,7 @@ public class EmailUtil {
|
|||||||
* @param recipientUser Recipient user
|
* @param recipientUser Recipient user
|
||||||
* @param paramMap Email parameters
|
* @param paramMap Email parameters
|
||||||
*/
|
*/
|
||||||
public static void sendEmail(String templateName, User recipientUser, Map<String, Object> paramMap) {
|
public static void sendEmail(String templateName, UserDto recipientUser, Map<String, Object> paramMap) {
|
||||||
java.util.Locale userLocale = LocaleUtil.getLocale(System.getenv(Constants.DEFAULT_LANGUAGE_ENV));
|
java.util.Locale userLocale = LocaleUtil.getLocale(System.getenv(Constants.DEFAULT_LANGUAGE_ENV));
|
||||||
String subject = MessageUtil.getMessage(userLocale, "email.template." + templateName + ".subject");
|
String subject = MessageUtil.getMessage(userLocale, "email.template." + templateName + ".subject");
|
||||||
sendEmail(templateName, recipientUser, subject, paramMap);
|
sendEmail(templateName, recipientUser, subject, paramMap);
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
<#import "../layout.ftl" as layout>
|
||||||
|
<@layout.email>
|
||||||
|
<h2>${app_name} - ${messages['email.template.route_step_validate.subject']}</h2>
|
||||||
|
<p>${messages('email.template.route_step_validate.hello', user_name)}</p>
|
||||||
|
<p>${messages['email.template.route_step_validate.instruction1']}</p>
|
||||||
|
<p>${messages['email.template.route_step_validate.instruction2']}</p>
|
||||||
|
<a href="${base_url}/#/document/${document_id}">${document_title}</a>
|
||||||
|
</@layout.email>
|
@ -3,4 +3,8 @@ email.template.password_recovery.hello=Hello {0}.
|
|||||||
email.template.password_recovery.instruction1=We have received a request to reset your password.<br/>If you did not request help, then feel free to ignore this email.
|
email.template.password_recovery.instruction1=We have received a request to reset your password.<br/>If you did not request help, then feel free to ignore this email.
|
||||||
email.template.password_recovery.instruction2=To reset your password, please visit the link below:
|
email.template.password_recovery.instruction2=To reset your password, please visit the link below:
|
||||||
email.template.password_recovery.click_here=Click here to reset your password
|
email.template.password_recovery.click_here=Click here to reset your password
|
||||||
|
email.template.route_step_validate.subject=A document needs your attention
|
||||||
|
email.template.route_step_validate.hello=Hello {0}.
|
||||||
|
email.template.route_step_validate.instruction1=A workflow step has been assigned to you and needs your attention.
|
||||||
|
email.template.route_step_validate.instruction2=To view the document and validate the workflow, please visit the link below:
|
||||||
email.no_html.error=Your email client does not support HTML messages
|
email.no_html.error=Your email client does not support HTML messages
|
@ -110,11 +110,12 @@ public class RouteResource extends BaseResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Intialize ACLs on the first step
|
// Intialize ACLs on the first step
|
||||||
RouteStepDto routeStep = routeStepDao.getCurrentStep(documentId);
|
RouteStepDto routeStepDto = routeStepDao.getCurrentStep(documentId);
|
||||||
RoutingUtil.updateAcl(documentId, routeStep, null, principal.getId());
|
RoutingUtil.updateAcl(documentId, routeStepDto, null, principal.getId());
|
||||||
|
RoutingUtil.sendRouteStepEmail(documentId, routeStepDto);
|
||||||
|
|
||||||
JsonObjectBuilder response = Json.createObjectBuilder()
|
JsonObjectBuilder response = Json.createObjectBuilder()
|
||||||
.add("route_step", routeStep.toJson());
|
.add("route_step", routeStepDto.toJson());
|
||||||
return Response.ok().entity(response.build()).build();
|
return Response.ok().entity(response.build()).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,13 +153,13 @@ public class RouteResource extends BaseResource {
|
|||||||
|
|
||||||
// Get the current step
|
// Get the current step
|
||||||
RouteStepDao routeStepDao = new RouteStepDao();
|
RouteStepDao routeStepDao = new RouteStepDao();
|
||||||
RouteStepDto routeStep = routeStepDao.getCurrentStep(documentId);
|
RouteStepDto routeStepDto = routeStepDao.getCurrentStep(documentId);
|
||||||
if (routeStep == null) {
|
if (routeStepDto == null) {
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check permission to validate this step
|
// Check permission to validate this step
|
||||||
if (!getTargetIdList(null).contains(routeStep.getTargetId())) {
|
if (!getTargetIdList(null).contains(routeStepDto.getTargetId())) {
|
||||||
throw new ForbiddenClientException();
|
throw new ForbiddenClientException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,16 +167,16 @@ public class RouteResource extends BaseResource {
|
|||||||
ValidationUtil.validateRequired(transitionStr, "transition");
|
ValidationUtil.validateRequired(transitionStr, "transition");
|
||||||
comment = ValidationUtil.validateLength(comment, "comment", 1, 500, true);
|
comment = ValidationUtil.validateLength(comment, "comment", 1, 500, true);
|
||||||
RouteStepTransition transition = RouteStepTransition.valueOf(transitionStr);
|
RouteStepTransition transition = RouteStepTransition.valueOf(transitionStr);
|
||||||
if (routeStep.getType() == RouteStepType.VALIDATE && transition != RouteStepTransition.VALIDATED
|
if (routeStepDto.getType() == RouteStepType.VALIDATE && transition != RouteStepTransition.VALIDATED
|
||||||
|| routeStep.getType() == RouteStepType.APPROVE && transition != RouteStepTransition.APPROVED && transition != RouteStepTransition.REJECTED) {
|
|| routeStepDto.getType() == RouteStepType.APPROVE && transition != RouteStepTransition.APPROVED && transition != RouteStepTransition.REJECTED) {
|
||||||
throw new ClientException("ValidationError", "Invalid transition for this route step type");
|
throw new ClientException("ValidationError", "Invalid transition for this route step type");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate the step and update ACLs
|
// Validate the step and update ACLs
|
||||||
routeStepDao.endRouteStep(routeStep.getId(), transition, comment, principal.getId());
|
routeStepDao.endRouteStep(routeStepDto.getId(), transition, comment, principal.getId());
|
||||||
RouteStepDto newRouteStep = routeStepDao.getCurrentStep(documentId);
|
RouteStepDto newRouteStep = routeStepDao.getCurrentStep(documentId);
|
||||||
RoutingUtil.updateAcl(documentId, newRouteStep, routeStep, principal.getId());
|
RoutingUtil.updateAcl(documentId, newRouteStep, routeStepDto, principal.getId());
|
||||||
// TODO Send an email to the new route step
|
RoutingUtil.sendRouteStepEmail(documentId, routeStepDto);
|
||||||
|
|
||||||
JsonObjectBuilder response = Json.createObjectBuilder()
|
JsonObjectBuilder response = Json.createObjectBuilder()
|
||||||
.add("readable", aclDao.checkPermission(documentId, PermType.READ, getTargetIdList(null)));
|
.add("readable", aclDao.checkPermission(documentId, PermType.READ, getTargetIdList(null)));
|
||||||
@ -253,5 +254,50 @@ public class RouteResource extends BaseResource {
|
|||||||
return Response.ok().entity(json.build()).build();
|
return Response.ok().entity(json.build()).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Workflow cancellation
|
/**
|
||||||
|
* Cancel a route.
|
||||||
|
*
|
||||||
|
* @api {delete} /route Cancel a route
|
||||||
|
* @apiName DeleteRoute
|
||||||
|
* @apiRouteModel Route
|
||||||
|
* @apiParam {String} documentId Document ID
|
||||||
|
* @apiSuccess {String} status Status OK
|
||||||
|
* @apiError (client) ForbiddenError Access denied
|
||||||
|
* @apiError (client) NotFound Document or route not found
|
||||||
|
* @apiPermission user
|
||||||
|
* @apiVersion 1.5.0
|
||||||
|
*
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
@DELETE
|
||||||
|
public Response delete(@QueryParam("documentId") String documentId) {
|
||||||
|
if (!authenticate()) {
|
||||||
|
throw new ForbiddenClientException();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the document
|
||||||
|
AclDao aclDao = new AclDao();
|
||||||
|
if (!aclDao.checkPermission(documentId, PermType.WRITE, getTargetIdList(null))) {
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the current step
|
||||||
|
RouteStepDao routeStepDao = new RouteStepDao();
|
||||||
|
RouteStepDto routeStepDto = routeStepDao.getCurrentStep(documentId);
|
||||||
|
if (routeStepDto == null) {
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the temporary ACLs
|
||||||
|
RoutingUtil.updateAcl(documentId, null, routeStepDto, principal.getId());
|
||||||
|
|
||||||
|
// Delete the route and the steps
|
||||||
|
RouteDao routeDao = new RouteDao();
|
||||||
|
routeDao.deleteRoute(routeStepDto.getRouteId());
|
||||||
|
|
||||||
|
// Always return OK
|
||||||
|
JsonObjectBuilder response = Json.createObjectBuilder()
|
||||||
|
.add("status", "ok");
|
||||||
|
return Response.ok().entity(response.build()).build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -953,10 +953,11 @@ public class UserResource extends BaseResource {
|
|||||||
|
|
||||||
// Check for user existence
|
// Check for user existence
|
||||||
UserDao userDao = new UserDao();
|
UserDao userDao = new UserDao();
|
||||||
User user = userDao.getActiveByUsername(username);
|
List<UserDto> userDtoList = userDao.findByCriteria(new UserCriteria().setUserName(username), null);
|
||||||
if (user == null) {
|
if (userDtoList.isEmpty()) {
|
||||||
throw new ClientException("UserNotFound", "User not found: " + username);
|
throw new ClientException("UserNotFound", "User not found: " + username);
|
||||||
}
|
}
|
||||||
|
UserDto user = userDtoList.get(0);
|
||||||
|
|
||||||
// Create the password recovery key
|
// Create the password recovery key
|
||||||
PasswordRecoveryDao passwordRecoveryDao = new PasswordRecoveryDao();
|
PasswordRecoveryDao passwordRecoveryDao = new PasswordRecoveryDao();
|
||||||
|
@ -149,6 +149,11 @@ angular.module('docs').controller('DocumentView', function ($scope, $state, $sta
|
|||||||
comment: $scope.workflowComment
|
comment: $scope.workflowComment
|
||||||
}).then(function (data) {
|
}).then(function (data) {
|
||||||
$scope.workflowComment = '';
|
$scope.workflowComment = '';
|
||||||
|
var title = $translate.instant('document.view.workflow_validated_title');
|
||||||
|
var msg = $translate.instant('document.view.workflow_validated_message');
|
||||||
|
var btns = [{result: 'ok', label: $translate.instant('ok'), cssClass: 'btn-primary'}];
|
||||||
|
$dialog.messageBox(title, msg, btns);
|
||||||
|
|
||||||
if (data.readable) {
|
if (data.readable) {
|
||||||
$scope.document.route_step = data.route_step;
|
$scope.document.route_step = data.route_step;
|
||||||
} else {
|
} else {
|
||||||
|
@ -3,7 +3,10 @@
|
|||||||
/**
|
/**
|
||||||
* Document view workflow controller.
|
* Document view workflow controller.
|
||||||
*/
|
*/
|
||||||
angular.module('docs').controller('DocumentViewWorkflow', function ($scope, $stateParams, Restangular) {
|
angular.module('docs').controller('DocumentViewWorkflow', function ($scope, $stateParams, Restangular, $translate, $dialog) {
|
||||||
|
/**
|
||||||
|
* Load routes.
|
||||||
|
*/
|
||||||
$scope.loadRoutes = function () {
|
$scope.loadRoutes = function () {
|
||||||
Restangular.one('route').get({
|
Restangular.one('route').get({
|
||||||
documentId: $stateParams.id
|
documentId: $stateParams.id
|
||||||
@ -12,12 +15,9 @@ angular.module('docs').controller('DocumentViewWorkflow', function ($scope, $sta
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Load route models
|
/**
|
||||||
Restangular.one('routemodel').get().then(function(data) {
|
* Start the selected workflow
|
||||||
$scope.routemodels = data.routemodels;
|
*/
|
||||||
});
|
|
||||||
|
|
||||||
// Start the selected workflow
|
|
||||||
$scope.startWorkflow = function () {
|
$scope.startWorkflow = function () {
|
||||||
Restangular.one('route').post('start', {
|
Restangular.one('route').post('start', {
|
||||||
routeModelId: $scope.routemodel,
|
routeModelId: $scope.routemodel,
|
||||||
@ -28,6 +28,34 @@ angular.module('docs').controller('DocumentViewWorkflow', function ($scope, $sta
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel the current workflow.
|
||||||
|
*/
|
||||||
|
$scope.cancelWorkflow = function () {
|
||||||
|
var title = $translate.instant('document.view.workflow.cancel_workflow_title');
|
||||||
|
var msg = $translate.instant('document.view.workflow.cancel_workflow_message');
|
||||||
|
var btns = [
|
||||||
|
{result: 'cancel', label: $translate.instant('cancel')},
|
||||||
|
{result: 'ok', label: $translate.instant('ok'), cssClass: 'btn-primary'}
|
||||||
|
];
|
||||||
|
|
||||||
|
$dialog.messageBox(title, msg, btns, function (result) {
|
||||||
|
if (result === 'ok') {
|
||||||
|
Restangular.one('route').remove({
|
||||||
|
documentId: $stateParams.id
|
||||||
|
}).then(function () {
|
||||||
|
delete $scope.document.route_step;
|
||||||
|
$scope.loadRoutes();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Load route models
|
||||||
|
Restangular.one('routemodel').get().then(function(data) {
|
||||||
|
$scope.routemodels = data.routemodels;
|
||||||
|
});
|
||||||
|
|
||||||
// Load routes
|
// Load routes
|
||||||
$scope.loadRoutes();
|
$scope.loadRoutes();
|
||||||
});
|
});
|
@ -117,4 +117,11 @@ angular.module('docs').controller('SettingsWorkflowEdit', function($scope, $dial
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a route step.
|
||||||
|
*/
|
||||||
|
$scope.removeStep = function (step) {
|
||||||
|
$scope.workflow.steps.splice($scope.workflow.steps.indexOf(step), 1);
|
||||||
|
};
|
||||||
});
|
});
|
@ -94,6 +94,8 @@
|
|||||||
"error_loading_comments": "Error loading comments",
|
"error_loading_comments": "Error loading comments",
|
||||||
"workflow_current": "Current workflow step",
|
"workflow_current": "Current workflow step",
|
||||||
"workflow_comment": "Add a workflow comment",
|
"workflow_comment": "Add a workflow comment",
|
||||||
|
"workflow_validated_title": "Workflow step validated",
|
||||||
|
"workflow_validated_message": "The workflow step has been successfully validated.",
|
||||||
"content": {
|
"content": {
|
||||||
"content": "Content",
|
"content": "Content",
|
||||||
"delete_file_title": "Delete file",
|
"delete_file_title": "Delete file",
|
||||||
@ -111,7 +113,10 @@
|
|||||||
"workflow_start_label": "Which workflow to start?",
|
"workflow_start_label": "Which workflow to start?",
|
||||||
"add_more_workflow": "Add more workflows",
|
"add_more_workflow": "Add more workflows",
|
||||||
"start_workflow_submit": "Start workflow",
|
"start_workflow_submit": "Start workflow",
|
||||||
"full_name": "<strong>{{ name }}</strong> started on {{ create_date | date }}"
|
"full_name": "<strong>{{ name }}</strong> started on {{ create_date | date }}",
|
||||||
|
"cancel_workflow": "Cancel the current workflow",
|
||||||
|
"cancel_workflow_title": "Cancel the workflow",
|
||||||
|
"cancel_workflow_message": "Do you really want to cancel the current workflow?"
|
||||||
},
|
},
|
||||||
"permissions": {
|
"permissions": {
|
||||||
"permissions": "Permissions",
|
"permissions": "Permissions",
|
||||||
|
@ -1,72 +1,76 @@
|
|||||||
|
<p class="well-sm pull-right" ng-show="document.route_step && document.writable">
|
||||||
|
<span class="btn btn-danger" ng-click="cancelWorkflow()">
|
||||||
|
<span class="glyphicon glyphicon-trash"></span>
|
||||||
|
{{ 'document.view.workflow.cancel_workflow' | translate }}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
<p class="well-sm">{{ 'document.view.workflow.message' | translate }}</p>
|
<p class="well-sm">{{ 'document.view.workflow.message' | translate }}</p>
|
||||||
|
|
||||||
<form name="startWorkflowForm" class="form-horizontal" novalidate ng-show="!document.route_step">
|
<form name="startWorkflowForm" class="form-horizontal" novalidate ng-show="!document.route_step">
|
||||||
<div class="form-group" ng-class="{ 'has-error': !startWorkflowForm.routemodel.$valid && startWorkflowForm.$dirty }">
|
<div class="form-group" ng-class="{ 'has-error': !startWorkflowForm.routemodel.$valid && startWorkflowForm.$dirty }">
|
||||||
<label for="inputRouteModel" class="col-sm-3">
|
<label for="inputRouteModel" class="col-sm-3">
|
||||||
{{ 'document.view.workflow.workflow_start_label' | translate }}
|
{{ 'document.view.workflow.workflow_start_label' | translate }}
|
||||||
<p ng-if="userInfo.base_functions.indexOf('ADMIN') != -1">
|
<p ng-if="userInfo.base_functions.indexOf('ADMIN') != -1">
|
||||||
<a href="#/settings/workflow">{{ 'document.view.workflow.add_more_workflow' | translate }}</a>
|
<a href="#/settings/workflow">{{ 'document.view.workflow.add_more_workflow' | translate }}</a>
|
||||||
</p>
|
</p>
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<select required class="form-control" id="inputRouteModel" name="routemodel" ng-model="routemodel">
|
<select required class="form-control" id="inputRouteModel" name="routemodel" ng-model="routemodel">
|
||||||
<option ng-repeat="routemodel in routemodels" value="{{ routemodel.id }}">{{ routemodel.name }}</option>
|
<option ng-repeat="routemodel in routemodels" value="{{ routemodel.id }}">{{ routemodel.name }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
|
||||||
<div class="col-sm-3">
|
|
||||||
<span class="help-block" ng-show="startWorkflowForm.routemodel.$error.required && startWorkflowForm.$dirty">{{ 'validation.required' | translate }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<span class="help-block" ng-show="startWorkflowForm.routemodel.$error.required && startWorkflowForm.$dirty">{{ 'validation.required' | translate }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-3 col-sm-9">
|
<div class="col-sm-offset-3 col-sm-9">
|
||||||
<button type="submit" class="btn btn-primary"
|
<button type="submit" class="btn btn-primary"
|
||||||
ng-disabled="!startWorkflowForm.$valid"
|
ng-disabled="!startWorkflowForm.$valid"
|
||||||
ng-click="startWorkflow()">
|
ng-click="startWorkflow()">
|
||||||
{{ 'document.view.workflow.start_workflow_submit' | translate }}
|
{{ 'document.view.workflow.start_workflow_submit' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<table class="table" ng-repeat="route in routes">
|
<table class="table" ng-repeat="route in routes">
|
||||||
|
<caption translate="document.view.workflow.full_name"
|
||||||
<caption translate="document.view.workflow.full_name"
|
translate-values="{ name: route.name, create_date: route.create_date }"></caption>
|
||||||
translate-values="{ name: route.name, create_date: route.create_date }"></caption>
|
<thead>
|
||||||
<thead>
|
<tr>
|
||||||
<tr>
|
<th>Type</th>
|
||||||
<th>Type</th>
|
<th>Name</th>
|
||||||
<th>Name</th>
|
<th>For</th>
|
||||||
<th>For</th>
|
<th>Validation</th>
|
||||||
<th>Validation</th>
|
</tr>
|
||||||
</tr>
|
</thead>
|
||||||
</thead>
|
<tbody>
|
||||||
<tbody>
|
<tr ng-repeat="step in route.steps" ng-class="{
|
||||||
<tr ng-repeat="step in route.steps" ng-class="{
|
'bg-success': step.transition == 'VALIDATED' || step.transition == 'APPROVED',
|
||||||
'bg-success': step.transition == 'VALIDATED' || step.transition == 'APPROVED',
|
'bg-danger': step.transition == 'REJECTED'
|
||||||
'bg-danger': step.transition == 'REJECTED'
|
}">
|
||||||
}">
|
<td>
|
||||||
<td>
|
<span class="glyphicon glyphicon-transfer" ng-if="step.type == 'APPROVE'"></span>
|
||||||
<span class="glyphicon glyphicon-transfer" ng-if="step.type == 'APPROVE'"></span>
|
<span class="glyphicon glyphicon-ok-circle" ng-if="step.type == 'VALIDATE'"></span>
|
||||||
<span class="glyphicon glyphicon-ok-circle" ng-if="step.type == 'VALIDATE'"></span>
|
{{ 'workflow_type.' + step.type | translate }}
|
||||||
{{ 'workflow_type.' + step.type | translate }}
|
</td>
|
||||||
</td>
|
<td>{{ step.name }}</td>
|
||||||
<td>{{ step.name }}</td>
|
<td>
|
||||||
<td>
|
<acl data="step.target"></acl>
|
||||||
<acl data="step.target"></acl>
|
</td>
|
||||||
</td>
|
<td>
|
||||||
<td>
|
<span ng-show="step.end_date">
|
||||||
<span ng-show="step.end_date">
|
{{ 'workflow_transition.' + step.transition | translate }}
|
||||||
{{ 'workflow_transition.' + step.transition | translate }}
|
{{ step.end_date | timeAgo: dateTimeFormat }}
|
||||||
{{ step.end_date | timeAgo: dateTimeFormat }}
|
by
|
||||||
by
|
<a href="#/user/{{ step.validator_username }}" class="label label-default">{{ step.validator_username }}</a>
|
||||||
<a href="#/user/{{ step.validator_username }}" class="label label-default">{{ step.validator_username }}</a>
|
<span ng-show="step.comment" class="text-">
|
||||||
<span ng-show="step.comment" class="text-">
|
<br/><em>{{ step.comment }}</em>
|
||||||
<br/>
|
</span>
|
||||||
<em>{{ step.comment }}</em>
|
</span>
|
||||||
</span>
|
</td>
|
||||||
</span>
|
</tr>
|
||||||
</td>
|
</tbody>
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
</table>
|
@ -43,7 +43,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-6">
|
<div class="col-xs-5">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-addon">
|
<span class="input-group-addon">
|
||||||
<span class="glyphicon glyphicon-transfer" ng-if="step.type == 'APPROVE'"></span>
|
<span class="glyphicon glyphicon-transfer" ng-if="step.type == 'APPROVE'"></span>
|
||||||
@ -63,6 +63,9 @@
|
|||||||
typeahead-editable="false"
|
typeahead-editable="false"
|
||||||
typeahead-wait-ms="200" />
|
typeahead-wait-ms="200" />
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-xs-1">
|
||||||
|
<span class="btn btn-link glyphicon glyphicon-trash" ng-click="removeStep(step)"></span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mt-10">
|
<div class="row mt-10">
|
||||||
<div class="col-xs-12" ng-class="{ 'has-error': !editWorkflowForm['name-' + $index].$valid && editWorkflowForm.$dirty }">
|
<div class="col-xs-12" ng-class="{ 'has-error': !editWorkflowForm['name-' + $index].$valid && editWorkflowForm.$dirty }">
|
||||||
|
@ -22,7 +22,7 @@ public class TestRouteResource extends BaseJerseyTest {
|
|||||||
* Test the route resource.
|
* Test the route resource.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testRouteResource() {
|
public void testRouteResource() throws Exception {
|
||||||
// Login route1
|
// Login route1
|
||||||
clientUtil.createUser("route1");
|
clientUtil.createUser("route1");
|
||||||
String route1Token = clientUtil.login("route1");
|
String route1Token = clientUtil.login("route1");
|
||||||
@ -30,6 +30,15 @@ public class TestRouteResource extends BaseJerseyTest {
|
|||||||
// Login admin
|
// Login admin
|
||||||
String adminToken = clientUtil.login("admin", "admin", false);
|
String adminToken = clientUtil.login("admin", "admin", false);
|
||||||
|
|
||||||
|
// Change SMTP configuration to target Wiser
|
||||||
|
target().path("/app/config_smtp").request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken)
|
||||||
|
.post(Entity.form(new Form()
|
||||||
|
.param("hostname", "localhost")
|
||||||
|
.param("port", "2500")
|
||||||
|
.param("from", "contact@sismicsdocs.com")
|
||||||
|
), JsonObject.class);
|
||||||
|
|
||||||
// Get all route models
|
// Get all route models
|
||||||
JsonObject json = target().path("/routemodel")
|
JsonObject json = target().path("/routemodel")
|
||||||
.queryParam("sort_column", "2")
|
.queryParam("sort_column", "2")
|
||||||
@ -59,6 +68,7 @@ public class TestRouteResource extends BaseJerseyTest {
|
|||||||
.param("routeModelId", routeModels.getJsonObject(0).getString("id"))), JsonObject.class);
|
.param("routeModelId", routeModels.getJsonObject(0).getString("id"))), JsonObject.class);
|
||||||
JsonObject step = json.getJsonObject("route_step");
|
JsonObject step = json.getJsonObject("route_step");
|
||||||
Assert.assertEquals("Check the document's metadata", step.getString("name"));
|
Assert.assertEquals("Check the document's metadata", step.getString("name"));
|
||||||
|
Assert.assertTrue(popEmail().contains("workflow step"));
|
||||||
|
|
||||||
// Get the route on document 1
|
// Get the route on document 1
|
||||||
json = target().path("/route")
|
json = target().path("/route")
|
||||||
@ -112,6 +122,7 @@ public class TestRouteResource extends BaseJerseyTest {
|
|||||||
step = json.getJsonObject("route_step");
|
step = json.getJsonObject("route_step");
|
||||||
Assert.assertEquals("Add relevant files to the document", step.getString("name"));
|
Assert.assertEquals("Add relevant files to the document", step.getString("name"));
|
||||||
Assert.assertTrue(json.getBoolean("readable"));
|
Assert.assertTrue(json.getBoolean("readable"));
|
||||||
|
Assert.assertTrue(popEmail().contains("workflow step"));
|
||||||
|
|
||||||
// Get the route on document 1
|
// Get the route on document 1
|
||||||
json = target().path("/route")
|
json = target().path("/route")
|
||||||
@ -150,6 +161,7 @@ public class TestRouteResource extends BaseJerseyTest {
|
|||||||
step = json.getJsonObject("route_step");
|
step = json.getJsonObject("route_step");
|
||||||
Assert.assertEquals("Approve the document", step.getString("name"));
|
Assert.assertEquals("Approve the document", step.getString("name"));
|
||||||
Assert.assertTrue(json.getBoolean("readable"));
|
Assert.assertTrue(json.getBoolean("readable"));
|
||||||
|
Assert.assertTrue(popEmail().contains("workflow step"));
|
||||||
|
|
||||||
// Get the route on document 1
|
// Get the route on document 1
|
||||||
json = target().path("/route")
|
json = target().path("/route")
|
||||||
@ -186,6 +198,7 @@ public class TestRouteResource extends BaseJerseyTest {
|
|||||||
.param("transition", "APPROVED")), JsonObject.class);
|
.param("transition", "APPROVED")), JsonObject.class);
|
||||||
Assert.assertFalse(json.containsKey("route_step"));
|
Assert.assertFalse(json.containsKey("route_step"));
|
||||||
Assert.assertFalse(json.getBoolean("readable"));
|
Assert.assertFalse(json.getBoolean("readable"));
|
||||||
|
Assert.assertTrue(popEmail().contains("workflow step"));
|
||||||
|
|
||||||
// Get the route on document 1
|
// Get the route on document 1
|
||||||
json = target().path("/route")
|
json = target().path("/route")
|
||||||
@ -217,5 +230,44 @@ public class TestRouteResource extends BaseJerseyTest {
|
|||||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, route1Token)
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, route1Token)
|
||||||
.get(JsonObject.class);
|
.get(JsonObject.class);
|
||||||
Assert.assertFalse(json.containsKey("route_step"));
|
Assert.assertFalse(json.containsKey("route_step"));
|
||||||
|
|
||||||
|
// Start the default route on document 1
|
||||||
|
target().path("/route/start").request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, route1Token)
|
||||||
|
.post(Entity.form(new Form()
|
||||||
|
.param("documentId", document1Id)
|
||||||
|
.param("routeModelId", routeModels.getJsonObject(0).getString("id"))), JsonObject.class);
|
||||||
|
Assert.assertTrue(popEmail().contains("workflow step"));
|
||||||
|
|
||||||
|
// Get document 1 as route1
|
||||||
|
json = target().path("/document/" + document1Id).request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, route1Token)
|
||||||
|
.get(JsonObject.class);
|
||||||
|
Assert.assertTrue(json.containsKey("route_step"));
|
||||||
|
|
||||||
|
// Get document 1 as admin
|
||||||
|
response = target().path("/document/" + document1Id).request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken)
|
||||||
|
.get();
|
||||||
|
Assert.assertEquals(Response.Status.OK, Response.Status.fromStatusCode(response.getStatus()));
|
||||||
|
|
||||||
|
// Cancel the route on document 1
|
||||||
|
target().path("/route")
|
||||||
|
.queryParam("documentId", document1Id)
|
||||||
|
.request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, route1Token)
|
||||||
|
.delete(JsonObject.class);
|
||||||
|
|
||||||
|
// Get document 1 as route1
|
||||||
|
json = target().path("/document/" + document1Id).request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, route1Token)
|
||||||
|
.get(JsonObject.class);
|
||||||
|
Assert.assertFalse(json.containsKey("route_step"));
|
||||||
|
|
||||||
|
// Get document 1 as admin
|
||||||
|
response = target().path("/document/" + document1Id).request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken)
|
||||||
|
.get();
|
||||||
|
Assert.assertEquals(Response.Status.NOT_FOUND, Response.Status.fromStatusCode(response.getStatus()));
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user