Commit fbc5d4b9 authored by Matija Obreza's avatar Matija Obreza

User's teams

parent 617b84be
......@@ -19,8 +19,8 @@ package org.genesys2.server.model.impl;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
......@@ -29,6 +29,7 @@ import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import javax.persistence.PrePersist;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
......@@ -46,19 +47,29 @@ import org.hibernate.search.annotations.Store;
public class Team extends VersionedAuditedModel {
private static final long serialVersionUID = -6992621329254944604L;
@Column(nullable = false, unique = true)
@Column(length = 36, unique = true)
private String uuid;
@Column(nullable = false, unique = true, length = 200)
@Field(name = "name", store = Store.NO)
private String name;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinTable(name = "teammember", joinColumns = @JoinColumn(name = "teamId"), inverseJoinColumns=@JoinColumn(name="userId"))
@OneToMany(fetch = FetchType.LAZY)
@JoinTable(name = "teammember", joinColumns = @JoinColumn(name = "teamId"), inverseJoinColumns = @JoinColumn(name = "userId"))
private Set<User> members;
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "teaminstitute", joinColumns = @JoinColumn(name = "teamId"), inverseJoinColumns = @JoinColumn(name = "instituteId"))
@OrderBy("code")
private List<FaoInstitute> institutes = new ArrayList<FaoInstitute>();
@PrePersist
void ensureUUID() {
if (this.uuid == null) {
this.uuid = UUID.randomUUID().toString();
}
}
public String getName() {
return name;
}
......@@ -92,4 +103,17 @@ public class Team extends VersionedAuditedModel {
public void setInstitutes(List<FaoInstitute> institutes) {
this.institutes = institutes;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
@Override
public String toString() {
return "Team " + this.name + " id=" + this.id;
}
}
......@@ -43,7 +43,7 @@ public interface TeamService {
* @param user
* @return
*/
@PreAuthorize("hasRole('ADMINISTRATOR'")
@PreAuthorize("hasRole('ADMINISTRATOR')")
Team addTeamMember(Team team, User user);
/**
......@@ -52,7 +52,7 @@ public interface TeamService {
* @param team
* @param user
*/
@PreAuthorize("hasRole('ADMINISTRATOR'")
@PreAuthorize("hasRole('ADMINISTRATOR')")
Team removeTeamMember(Team team, User user);
/**
......@@ -62,10 +62,13 @@ public interface TeamService {
*/
void removeMe(Team team);
@PreAuthorize("hasRole('ADMINISTRATOR'")
void removeMe(long teamId);
@PreAuthorize("hasRole('ADMINISTRATOR')")
Team addTeamInstitute(Team team, FaoInstitute institute);
@PreAuthorize("hasRole('ADMINISTRATOR'")
@PreAuthorize("hasRole('ADMINISTRATOR')")
Team removeTeamInsitute(Team team, FaoInstitute institute);
/**
......@@ -75,12 +78,21 @@ public interface TeamService {
*/
List<Team> listMyTeams();
/**
* List user's teams
*
* @param user
* @return
*/
List<Team> listUserTeams(User user);
/**
* List all teams
*
* @return
*/
@PreAuthorize("hasRole('ADMINISTRATOR'")
@PreAuthorize("hasRole('ADMINISTRATOR')")
Page<Team> listTeams(Pageable pageable);
}
......@@ -68,7 +68,7 @@ public class TeamServiceImpl implements TeamService {
@Override
@Transactional(readOnly = false)
@PreAuthorize("hasRole('ADMINISTRATOR'")
@PreAuthorize("hasRole('ADMINISTRATOR')")
public Team addTeamMember(Team team, User user) {
if (team.getMembers().contains(user)) {
LOG.info("User already member of this team");
......@@ -82,7 +82,7 @@ public class TeamServiceImpl implements TeamService {
}
@Override
@PreAuthorize("hasRole('ADMINISTRATOR'")
@PreAuthorize("hasRole('ADMINISTRATOR')")
@Transactional(readOnly = false)
public Team removeTeamMember(Team team, User user) {
if (team.getMembers().remove(user)) {
......@@ -93,6 +93,12 @@ public class TeamServiceImpl implements TeamService {
return team;
}
@Override
@Transactional(readOnly = false)
public void removeMe(long teamId) {
removeMe(teamRepository.findOne(teamId));
}
@Override
@Transactional(readOnly = false)
......@@ -122,7 +128,7 @@ public class TeamServiceImpl implements TeamService {
@Override
@Transactional(readOnly = false)
@PreAuthorize("hasRole('ADMINISTRATOR'")
@PreAuthorize("hasRole('ADMINISTRATOR')")
public Team addTeamInstitute(Team team, FaoInstitute institute) {
if (team.getInstitutes().contains(institute)) {
LOG.info("Institute already assigned to this team");
......@@ -137,7 +143,7 @@ public class TeamServiceImpl implements TeamService {
@Override
@Transactional(readOnly = false)
@PreAuthorize("hasRole('ADMINISTRATOR'")
@PreAuthorize("hasRole('ADMINISTRATOR')")
public Team removeTeamInsitute(Team team, FaoInstitute institute) {
if (team.getInstitutes().contains(institute)) {
team.getInstitutes().remove(institute);
......@@ -150,11 +156,16 @@ public class TeamServiceImpl implements TeamService {
@Override
public List<Team> listMyTeams() {
User user = getCurrentUser();
return listUserTeams(user);
}
@Override
public List<Team> listUserTeams(User user) {
return teamRepository.listForUser(user);
}
@Override
@PreAuthorize("hasRole('ADMINISTRATOR'")
@PreAuthorize("hasRole('ADMINISTRATOR')")
public Page<Team> listTeams(Pageable pageable) {
return teamRepository.findAll(pageable);
}
......
......@@ -20,6 +20,7 @@ import org.apache.commons.lang.StringUtils;
import org.genesys2.server.exception.UserException;
import org.genesys2.server.model.impl.User;
import org.genesys2.server.service.ContentService;
import org.genesys2.server.service.TeamService;
import org.genesys2.server.service.UserService;
import org.genesys2.spring.ResourceNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -42,6 +43,9 @@ public class UserProfileController extends BaseController {
@Autowired
private Validator validator;
@Autowired
private TeamService teamService;
@Autowired
private ContentService contentService;
......@@ -72,6 +76,7 @@ public class UserProfileController extends BaseController {
}
model.addAttribute("user", user);
model.addAttribute("teams", teamService.listUserTeams(user));
return "/user/profile";
}
......
......@@ -23,6 +23,7 @@ import org.genesys2.server.model.genesys.Parameter;
import org.genesys2.server.model.impl.Crop;
import org.genesys2.server.model.impl.CropRule;
import org.genesys2.server.model.impl.CropTaxonomy;
import org.genesys2.server.model.impl.Team;
import org.genesys2.server.model.impl.User;
import org.springframework.data.domain.Page;
......@@ -55,6 +56,11 @@ public class OAuth2Cleanup {
cropTaxa.setCrop(null);
cropTaxa.getTaxonomy().setCropTaxonomies(null);
}
if (t instanceof Team) {
Team team = (Team) t;
team.setMembers(null);
team.setInstitutes(null);
}
return t;
}
......
......@@ -16,11 +16,14 @@ public class RestController {
public RestController() {
super();
}
@ExceptionHandler(Exception.class)
@ResponseBody
public ExceptionJson handleIOException(Exception ex, HttpServletRequest request, HttpServletResponse response) throws IOException {
LOG.warn("Unexpected error: " + ex.getLocalizedMessage());
if (LOG.isDebugEnabled()) {
LOG.debug("Unexpected error: " + ex.getLocalizedMessage());
}
return new ExceptionJson(ex);
}
......
......@@ -16,17 +16,28 @@
package org.genesys2.server.servlet.controller.rest;
import java.util.List;
import net.sf.oval.ConstraintViolation;
import net.sf.oval.Validator;
import net.sf.oval.constraint.MinLength;
import net.sf.oval.constraint.NotBlank;
import net.sf.oval.constraint.NotNull;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.genesys2.server.ServiceEndpoints;
import org.genesys2.server.exception.AuthorizationException;
import org.genesys2.server.model.impl.User;
import org.genesys2.server.service.TeamService;
import org.genesys2.server.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
......@@ -34,12 +45,17 @@ import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@PreAuthorize("isAuthenticated()")
@RequestMapping(value = { "/api/v0", "/json/v0" })
public class UserController {
public class UserController extends RestController {
private static final Log LOG = LogFactory.getLog(UserController.class);
private static final String JSON_OK = "{\"response\":\"OK\"}";
@Autowired
protected UserService userService;
@Autowired
protected TeamService teamService;
/**
* Returns logged in user's profile
*
......@@ -53,4 +69,33 @@ public class UserController {
User user = userService.getUserByEmail(authenticationName);
return OAuth2Cleanup.clean(user);
}
@RequestMapping(value = "/me/teams", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public @ResponseBody
Object createTeam(@RequestBody TeamJson teamJson) {
Validator validator = new Validator();
List<ConstraintViolation> violations = validator.validate(teamJson);
if (violations.size() > 0) {
// TODO We could do better messages on validation error
throw new ModelValidationException("Validation error", violations);
}
LOG.info("Creating team " + teamJson);
return OAuth2Cleanup.clean(teamService.addTeam(teamJson.name));
}
@RequestMapping(value = "/me/teams/{teamId}/leave", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public @ResponseBody
Object leaveTeam(@PathVariable("teamId") Long teamId) {
LOG.info("Leaving team " + teamId);
teamService.removeMe(teamId);
return JSON_OK;
}
public static class TeamJson {
@NotBlank
@NotNull
@MinLength(value = 4)
public String name;
}
}
\ No newline at end of file
......@@ -44,6 +44,7 @@ actions=Actions
add=Add
edit=Edit
save=Save
create=Create
cancel=Cancel
delete=Delete
......@@ -305,3 +306,7 @@ request.start-request=Request available germplasm
request.your-email=Your E-mail address:
team.user-teams=User's Teams
team.create-new-team=Create a new team
team.team-name=Team name
team.leave-team=Leave team
......@@ -24,7 +24,7 @@ db.url=jdbc:mysql://127.0.0.1/genesys2?useUnicode=true&characterEncoding=UTF-8&u
db.driverClassName = com.mysql.jdbc.Driver
db.username = root
db.password =
db.showSql=false
db.showSql=true
db.hbm2ddl=do-nothing
c3p0.acquireIncrement=1
......
......@@ -28,6 +28,54 @@
</div>
</security:authorize>
</div>
<h3><spring:message code="team.user-teams" /></h3>
<ul class="funny-list">
<c:forEach items="${teams}" var="team" varStatus="status">
<li class="${status.count % 2 == 0 ? 'even' : 'odd'}"><a href="<c:url value="/team/${team.uuid}" />"><c:out value="${team.name}" /></a>
<security:authorize access="isAuthenticated() && principal.user.id == #user.id">
<a x-team-id="${team.id}" class="pull-right"><spring:message code="team.leave-team" /></a>
</security:authorize>
</li>
</c:forEach>
</ul>
<security:authorize access="isAuthenticated() && principal.user.id == #user.id">
<h4><spring:message code="team.create-new-team" /></h4>
<form id="new-team-form" class="form-horizontal">
<div class="form-group">
<label for="team-name" class="col-lg-2 control-label"><spring:message code="team.team-name" /></label>
<div class="col-lg-3"><input type="text" name="name" id="team-name" class="span3 form-control" /></div>
<div class="col-lg-1">
<input type="submit" value="<spring:message code="create"/>" class="btn btn-primary" />
</div>
</div>
</form>
<script src="<c:url value="/html/js/main.js" />"></script>
<script src="<c:url value="/html/js/jsonclient.js" />"></script>
<script type="text/javascript">
jQuery(document).ready(function() {
$("#new-team-form input[type=submit]").on("click", function(e) {
e.preventDefault();
x01("<c:url value="/json/v0/me/teams" />", { success: function(e) {
window.location.reload();
}}, $(this.form).serializeObject());
});
$("a[x-team-id]").on("click", function(e) {
e.preventDefault();
x01("<c:url value="/json/v0/me/teams/" />" + $(this).attr('x-team-id') + "/leave",
{ success: function(e) {
window.location.reload();
}});
});
});
</script>
</security:authorize>
</body>
</html>
\ No newline at end of file
function x0(path, method, object) {
$.ajax(path, {
type : method,
dataType : 'json',
contentType: 'application/json; charset=utf-8',
data: (object==null ? null : JSON.stringify(object)),
beforeSend : function(xhr) {
},
success : function(respObject) {
//$("#responseBody")[0].value = JSON.stringify(respObject);
console.log(respObject);
},
error: function(jqXHR, textStatus, errorThrown) {
//$("#responseBody")[0].value = errorThrown;
console.log(textStatus);
console.log(errorThrown);
}
});
}
function x01(path, handler, object) {
$.ajax(path, $.extend({}, {
type : 'POST',
dataType : 'json',
contentType: 'application/json; charset=utf-8',
data: (object==null ? null : JSON.stringify(object)),
beforeSend : function(xhr) {
},
success : function(respObject) {
console.log(respObject);
},
error: function(jqXHR, textStatus, errorThrown) {
console.log(textStatus);
console.log(errorThrown);
}
}, handler));
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment