package controllers;
import com.fasterxml.jackson.databind.node.ObjectNode;
import controllers.Navigation.Level;
import managers.*;
import models.*;
import models.base.FileOperationException;
import models.base.ValidationException;
import models.enums.AvatarSize;
import models.enums.GroupType;
import models.enums.LinkType;
import models.services.NotificationService;
import play.Configuration;
import play.api.i18n.Lang;
import play.data.DynamicForm;
import play.data.Form;
import play.data.FormFactory;
import play.db.jpa.Transactional;
import play.i18n.MessagesApi;
import play.libs.Json;
import play.mvc.Call;
import play.mvc.Http;
import play.mvc.Result;
import play.mvc.Security;
import views.html.Group.*;
import views.html.Group.snippets.streamRaw;
import javax.inject.Inject;
import java.io.File;
import java.util.Collections;
import java.util.List;
@Transactional
@Security.Authenticated(Secured.class)
public class GroupController extends BaseController {
GroupManager groupManager;
GroupAccountManager groupAccountManager;
MediaManager mediaManager;
FriendshipManager friendshipManager;
PostManager postManager;
AccountManager accountManager;
FolderManager folderManager;
AvatarManager avatarManager;
NotificationService notificationService;
Configuration configuration;
FormFactory formFactory;
MessagesApi messagesApi;
@Inject
public GroupController(GroupManager groupManager,
GroupAccountManager groupAccountManager,
MediaManager mediaManager,
FriendshipManager friendshipManager,
PostManager postManager,
AccountManager accountManager,
FolderManager folderManager,
AvatarManager avatarManager,
NotificationService notificationService,
Configuration configuration,
FormFactory formFactory,
MessagesApi messagesApi) {
this.groupManager = groupManager;
this.groupAccountManager = groupAccountManager;
this.mediaManager = mediaManager;
this.friendshipManager = friendshipManager;
this.postManager = postManager;
this.accountManager = accountManager;
this.folderManager = folderManager;
this.avatarManager = avatarManager;
this.notificationService = notificationService;
this.configuration = configuration;
this.formFactory = formFactory;
this.messagesApi = messagesApi;
this.groupForm = formFactory.form(Group.class);
this.folderForm = formFactory.form(Folder.class);
this.postForm = formFactory.form(Post.class);
this.limit = configuration.getInt("htwplus.post.limit");
}
Form<Group> groupForm;
Form<Folder> folderForm;
Form<Post> postForm;
int limit;
static final int PAGE = 1;
public Result index() {
Navigation.set(Level.GROUPS, "Übersicht");
Account account = Component.currentAccount();
List<GroupAccount> groupRequests = groupAccountManager.findRequests(account);
List<Group> groupAccounts = groupAccountManager.findGroupsEstablished(account);
List<Group> courseAccounts = groupAccountManager.findCoursesEstablished(account);
return ok(index.render(groupAccounts, courseAccounts, groupRequests, groupForm));
}
@Transactional(readOnly = true)
public Result view(Long id) {
Group group = groupManager.findById(id);
if (group == null) {
flash("error", messagesApi.get(Lang.defaultLang(), "group.group_not_found"));
return redirect(controllers.routes.GroupController.index());
}
if (Secured.viewGroup(group)) {
return redirect(controllers.routes.GroupController.stream(group.id, PAGE, false));
}
Navigation.set(Level.GROUPS, "Info", group.title, controllers.routes.GroupController.view(group.id));
return ok(view.render(group));
}
@Transactional(readOnly = true)
public Result stream(Long id, int page, boolean raw) {
Group group = groupManager.findById(id);
if (group == null) {
flash("error", messagesApi.get(Lang.defaultLang(), "group.group_not_found"));
return redirect(controllers.routes.GroupController.index());
}
if (!Secured.viewGroup(group)) {
return redirect(controllers.routes.GroupController.view(group.id));
}
Navigation.set(Level.GROUPS, "Newsstream", group.title, controllers.routes.GroupController.stream(group.id, PAGE, false));
List<Post> posts = postManager.getPostsForGroup(group, limit, page);
if (raw) {
return ok(streamRaw.render(group, posts, postForm, postManager.countPostsForGroup(group), limit, page));
} else {
return ok(stream.render(group, posts, postForm, postManager.countPostsForGroup(group), limit, page));
}
}
@Transactional(readOnly = true)
public Result media(Long groupId, Long folderId) {
Group group = groupManager.findById(groupId);
Folder folder;
if (group == null) {
flash("error", messagesApi.get(Lang.defaultLang(), "group.group_not_found"));
return redirect(controllers.routes.GroupController.index());
}
if (!Secured.viewGroup(group)) {
return redirect(controllers.routes.GroupController.view(groupId));
}
if(folderId != 0) {
folder = folderManager.findById(folderId);
} else {
folder = group.rootFolder;
}
Navigation.set(Level.GROUPS, "Media", group.title, controllers.routes.GroupController.stream(group.id, PAGE, false));
List<Media> mediaSet = folder.files;
List<Folder> folderList = folder.folders;
List<Folder> navigationFolder = folder.findAncestors(folder);
Collections.reverse(navigationFolder);
// hacky, but prevents accessing MediaController from view. use dto instead
for (Media media : mediaSet) {
media.sizeInByte = mediaManager.bytesToString(media.size, false);
}
return ok(media.render(group, mediaSet, folderList, folder, navigationFolder, folderForm));
}
public Result create() {
Navigation.set(Level.GROUPS, "Erstellen");
return ok(create.render(groupForm));
}
public Result add() {
Navigation.set(Level.GROUPS, "Erstellen");
// Get data from request
Form<Group> filledForm = groupForm.bindFromRequest();
// Perform JPA Validation
if (filledForm.hasErrors()) {
return badRequest(create.render(filledForm));
} else {
Group group = filledForm.get();
int groupType;
try {
groupType = Integer.parseInt(filledForm.data().get("type"));
} catch (NumberFormatException ex) {
filledForm.reject("type", "Bitte eine Sichtbarkeit wählen!");
return ok(create.render(filledForm));
}
String successMsg;
switch (groupType) {
case 0:
group.groupType = GroupType.open;
successMsg = "Öffentliche Gruppe";
break;
case 1:
group.groupType = GroupType.close;
successMsg = "Geschlossene Gruppe";
break;
case 2:
group.groupType = GroupType.course;
successMsg = "Kurs";
String token = filledForm.data().get("token");
if (!Group.validateToken(token)) {
filledForm.reject("token", "Bitte einen Token zwischen 4 und 45 Zeichen eingeben!");
return ok(create.render(filledForm));
}
if (!Secured.createCourse()) {
flash("error", "Du darfst leider keinen Kurs erstellen");
return badRequest(create.render(filledForm));
}
break;
default:
filledForm.reject("Nicht möglich!");
return ok(create.render(filledForm));
}
groupManager.createWithGroupAccount(group, Component.currentAccount());
flash("success", successMsg + " erstellt!");
return redirect(controllers.routes.GroupController.stream(group.id, PAGE, false));
}
}
@Transactional
public Result edit(Long id) {
Group group = groupManager.findById(id);
if (group == null) {
flash("error", messagesApi.get(Lang.defaultLang(), "group.group_not_found"));
return redirect(controllers.routes.GroupController.index());
}
// Check rights
if (!Secured.editGroup(group)) {
return redirect(controllers.routes.GroupController.view(id));
}
Navigation.set(Level.GROUPS, "Bearbeiten", group.title, controllers.routes.GroupController.stream(group.id, PAGE, false));
Form<Group> groupForm = formFactory.form(Group.class).fill(group);
groupForm.data().put("type", String.valueOf(group.groupType.ordinal()));
return ok(edit.render(group, groupForm));
}
@Transactional
public Result update(Long groupId) {
Group group = groupManager.findById(groupId);
if (group == null) {
flash("error", messagesApi.get(Lang.defaultLang(), "group.group_not_found"));
return redirect(controllers.routes.GroupController.index());
}
Navigation.set(Level.GROUPS, "Bearbeiten", group.title, controllers.routes.GroupController.stream(group.id, PAGE, false));
// Check rights
if (!Secured.editGroup(group)) {
return redirect(controllers.routes.GroupController.index());
}
Form<Group> filledForm = groupForm.bindFromRequest();
int groupType = Integer.parseInt(filledForm.data().get("type"));
String description = filledForm.data().get("description");
switch (groupType) {
case 0:
group.groupType = GroupType.open;
group.token = null;
break;
case 1:
group.groupType = GroupType.close;
group.token = null;
break;
case 2:
group.groupType = GroupType.course;
String token = filledForm.data().get("token");
if (!Group.validateToken(token)) {
filledForm.reject("token", "Bitte einen Token zwischen 4 und 45 Zeichen eingeben!");
return ok(edit.render(group, filledForm));
}
if (!Secured.createCourse()) {
flash("error", "Du darfst leider keinen Kurs erstellen");
return badRequest(edit.render(group, filledForm));
}
group.token = token;
break;
default:
filledForm.reject("Nicht möglich!");
return ok(edit.render(group, filledForm));
}
group.description = description;
groupManager.update(group);
flash("success", "'" + group.title + "' erfolgreich bearbeitet!");
return redirect(controllers.routes.GroupController.stream(groupId, PAGE, false));
}
@Transactional
public Result delete(Long id) {
Group group = groupManager.findById(id);
if (group == null) {
flash("error", messagesApi.get(Lang.defaultLang(), "group.group_not_found"));
return redirect(controllers.routes.GroupController.index());
}
if (Secured.deleteGroup(group)) {
groupManager.delete(group);
flash("info", "'" + group.title + "' wurde erfolgreich gelöscht!");
} else {
flash("error", "Dazu hast du keine Berechtigung!");
}
return redirect(controllers.routes.GroupController.index());
}
public Result token(Long groupId) {
Group group = groupManager.findById(groupId);
if (group == null) {
flash("error", messagesApi.get(Lang.defaultLang(), "group.group_not_found"));
return redirect(controllers.routes.GroupController.index());
}
Navigation.set(Level.GROUPS, "Token eingeben", group.title, controllers.routes.GroupController.stream(group.id, PAGE, false));
return ok(token.render(group, groupForm));
}
public Result validateToken(Long groupId) {
Group group = groupManager.findById(groupId);
if (group == null) {
flash("error", messagesApi.get(Lang.defaultLang(), "group.group_not_found"));
return redirect(controllers.routes.GroupController.index());
}
if (Secured.isMemberOfGroup(group, Component.currentAccount())) {
flash("error", "Du bist bereits Mitglied dieser Gruppe!");
return redirect(controllers.routes.GroupController.stream(groupId, PAGE, false));
}
Navigation.set(Level.GROUPS, "Token eingeben", group.title, controllers.routes.GroupController.stream(group.id, PAGE, false));
Form<Group> filledForm = groupForm.bindFromRequest();
String enteredToken = filledForm.data().get("token");
if (enteredToken.equals(group.token)) {
Account account = Component.currentAccount();
groupAccountManager.create(new GroupAccount(group, account, LinkType.establish));
flash("success", "Kurs erfolgreich beigetreten!");
return redirect(controllers.routes.GroupController.stream(groupId, PAGE, false));
} else {
flash("error", "Hast du dich vielleicht vertippt? Der Token ist leider falsch.");
return badRequest(token.render(group, filledForm));
}
}
@Transactional
public Result join(long id) {
Account account = Component.currentAccount();
Group group = groupManager.findById(id);
if (group == null) {
flash("error", messagesApi.get(Lang.defaultLang(), "group.group_not_found"));
return redirect(controllers.routes.GroupController.index());
}
if (Secured.isMemberOfGroup(group, account)) {
flash("error", "Du bist bereits Mitglied dieser Gruppe!");
return redirect(controllers.routes.GroupController.stream(id, PAGE, false));
}
// is already requested?
GroupAccount groupAccount = groupAccountManager.find(account, group);
if (groupAccount != null && groupAccount.linkType.equals(LinkType.request)) {
flash("info", "Deine Beitrittsanfrage wurde bereits verschickt!");
return redirect(controllers.routes.GroupController.index());
}
if (groupAccount != null && groupAccount.linkType.equals(LinkType.reject)) {
flash("error", "Deine Beitrittsanfrage wurde bereits abgelehnt!");
return redirect(controllers.routes.GroupController.index());
}
// invitation?
if (groupAccount != null && groupAccount.linkType.equals(LinkType.invite)) {
groupAccount.linkType = LinkType.establish;
groupAccountManager.update(groupAccount);
flash("success", "'" + group.title + "' erfolgreich beigetreten!");
return redirect(controllers.routes.GroupController.index());
} else if (group.groupType.equals(GroupType.open)) {
groupAccountManager.create(new GroupAccount(group, account, LinkType.establish));
flash("success", "'" + group.title + "' erfolgreich beigetreten!");
return redirect(controllers.routes.GroupController.stream(id, PAGE, false));
} else if (group.groupType.equals(GroupType.close)) {
groupAccountManager.create(new GroupAccount(group, account, LinkType.request));
group.temporarySender = account;
notificationService.createNotification(group, Group.GROUP_NEW_REQUEST);
flash("success", messagesApi.get(Lang.defaultLang(), "group.group_request_sent"));
return redirect(controllers.routes.GroupController.index());
} else if (group.groupType.equals(GroupType.course)) {
return redirect(controllers.routes.GroupController.token(id));
}
return redirect(controllers.routes.GroupController.index());
}
public Result removeMember(long groupId, long accountId) {
Account account = accountManager.findById(accountId);
Group group = groupManager.findById(groupId);
if (group == null) {
flash("error", messagesApi.get(Lang.defaultLang(), "group.group_not_found"));
return redirect(controllers.routes.GroupController.index());
}
GroupAccount groupAccount = groupAccountManager.find(account, group);
Call defaultRedirect = controllers.routes.GroupController.index();
if (!Secured.removeGroupMember(group, account)) {
return redirect(controllers.routes.GroupController.index());
}
if (groupAccount != null) {
groupAccountManager.delete(groupAccount);
if (account.equals(Component.currentAccount())) {
flash("info", "Gruppe erfolgreich verlassen!");
} else {
flash("info", "Mitglied erfolgreich entfernt!");
defaultRedirect = controllers.routes.GroupController.edit(groupId);
}
if (groupAccount.linkType.equals(LinkType.request)) {
flash("info", "Anfrage zurückgezogen!");
}
if (groupAccount.linkType.equals(LinkType.reject)) {
flash("info", "Anfrage gelöscht!");
}
} else {
flash("info", "Das geht leider nicht :(");
}
return redirect(defaultRedirect);
}
/**
* Accepts a group entry request.
*
* @param groupId Group ID
* @param accountId Account ID
* @return Result
*/
@Transactional
public Result acceptRequest(long groupId, long accountId) {
Account account = accountManager.findById(accountId);
Group group = groupManager.findById(groupId);
if (group == null) {
flash("error", messagesApi.get(Lang.defaultLang(), "group.group_not_found"));
return redirect(controllers.routes.GroupController.index());
}
if (account != null && Secured.isOwnerOfGroup(group, Component.currentAccount())) {
GroupAccount groupAccount = groupAccountManager.find(account, group);
if (groupAccount != null) {
groupAccount.linkType = LinkType.establish;
groupAccountManager.update(groupAccount);
}
} else {
flash("error", messagesApi.get(Lang.defaultLang(), "group.group_not_found"));
return redirect(controllers.routes.GroupController.index());
}
group.temporarySender = group.owner;
group.addTemporaryRecipient(account);
notificationService.createNotification(group, Group.GROUP_REQUEST_SUCCESS);
return redirect(controllers.routes.GroupController.index());
}
/**
* Declines a group entry request.
*
* @param groupId Group ID
* @param accountId Account ID
* @return Result
*/
@Transactional
public Result declineRequest(long groupId, long accountId) {
Account account = accountManager.findById(accountId);
Group group = groupManager.findById(groupId);
if (group == null) {
flash("error", messagesApi.get(Lang.defaultLang(), "group.group_not_found"));
return redirect(controllers.routes.GroupController.index());
}
if (account != null && Secured.isOwnerOfGroup(group, Component.currentAccount())) {
GroupAccount groupAccount = groupAccountManager.find(account, group);
if (groupAccount != null) {
groupAccount.linkType = LinkType.reject;
}
}
group.temporarySender = group.owner;
group.addTemporaryRecipient(account);
notificationService.createNotification(group, Group.GROUP_REQUEST_DECLINE);
return redirect(controllers.routes.GroupController.index());
}
@Transactional
public Result invite(long groupId) {
Group group = groupManager.findById(groupId);
if (group == null) {
flash("error", messagesApi.get(Lang.defaultLang(), "group.group_not_found"));
return redirect(controllers.routes.GroupController.index());
}
Navigation.set(Level.GROUPS, "Kontakte einladen", group.title, controllers.routes.GroupController.stream(group.id, PAGE, false));
return ok(invite.render(group, friendshipManager.friendsToInvite(Component.currentAccount(), group), groupAccountManager.findAccountsByGroup(group, LinkType.invite)));
}
@Transactional
public Result inviteMember(long groupId) {
Group group = groupManager.findById(groupId);
if (group == null) {
flash("error", messagesApi.get(Lang.defaultLang(), "group.group_not_found"));
return redirect(controllers.routes.GroupController.index());
}
Account currentUser = Component.currentAccount();
if (Secured.inviteMember(group)) {
// bind invite list to group
DynamicForm form = formFactory.form().bindFromRequest();
group.inviteList = form.data().values();
// if no one is invited, abort
if (group.inviteList.size() < 1) {
flash("error", messagesApi.get(Lang.defaultLang(), "group.invite_no_invite"));
return redirect(controllers.routes.GroupController.invite(groupId));
}
// create GroupAccount link for all invitations
for (String accountId : group.inviteList) {
try {
Account inviteAccount = accountManager.findById(Long.parseLong(accountId));
GroupAccount groupAccount = groupAccountManager.find(inviteAccount, group);
// Create group account link to inviteAccount and add to notification recipient list
// if the inviteAccount is not already member, the sender and recipients are friends
// and the group account link is not already set up.
if (!Secured.isMemberOfGroup(group, inviteAccount) && friendshipManager.alreadyFriendly(currentUser, inviteAccount) && groupAccount == null) {
groupAccountManager.create(new GroupAccount(group, inviteAccount, LinkType.invite));
// add inviteAccount to temporaryRecipients list for notifications later
group.addTemporaryRecipient(inviteAccount);
}
} catch (Exception e) {
e.printStackTrace();
flash("error", "Etwas ist schief gelaufen.");
return redirect(controllers.routes.GroupController.invite(groupId));
}
}
group.temporarySender = currentUser;
notificationService.createNotification(group, Group.GROUP_INVITATION);
}
flash("success", messagesApi.get(Lang.defaultLang(), "group.invite_invited"));
return redirect(controllers.routes.GroupController.stream(groupId, PAGE, false));
}
public Result acceptInvitation(long groupId, long accountId) {
Group group = groupManager.findById(groupId);
if (group == null) {
flash("error", messagesApi.get(Lang.defaultLang(), "group.group_not_found"));
return redirect(controllers.routes.GroupController.index());
}
Account account = accountManager.findById(accountId);
GroupAccount groupAccount = groupAccountManager.find(account, group);
if (groupAccount != null && Secured.acceptInvitation(groupAccount)) {
join(group.id);
}
return redirect(controllers.routes.GroupController.stream(groupId, PAGE, false));
}
public Result declineInvitation(long groupId, long accountId) {
Group group = groupManager.findById(groupId);
if (group == null) {
flash("error", messagesApi.get(Lang.defaultLang(), "group.group_not_found"));
return redirect(controllers.routes.GroupController.index());
}
Account account = accountManager.findById(accountId);
GroupAccount groupAccount = groupAccountManager.find(account, group);
if (groupAccount != null && Secured.acceptInvitation(groupAccount)) {
groupAccountManager.delete(groupAccount);
}
flash("success", "Einladung abgelehnt!");
return redirect(controllers.routes.GroupController.index());
}
public Result createFolder(Long folderId) {
Folder parentFolder = folderManager.findById(folderId);
Group group = folderManager.findRoot(parentFolder).group;
Folder folder = null;
Form<Folder> filledForm = folderForm.bindFromRequest();
if(filledForm.hasErrors()) {
if(filledForm.data().get("name").isEmpty()) {
flash("error", "Bitte einen Ordnernamen angeben.");
return redirect(routes.GroupController.media(group.id, folderId));
}
}
if(Secured.isMemberOfGroup(group, Component.currentAccount())) {
folder = new Folder(filledForm.data().get("name"), Component.currentAccount(), parentFolder, null, null);
folderManager.create(folder);
return redirect(routes.GroupController.media(group.id, folder.id));
}
flash("error", messagesApi.get(Lang.defaultLang(), "post.join_group_first"));
return redirect(routes.GroupController.media(group.id, folderId));
}
public Result deleteFolder(Long folderId) {
Folder folder = folderManager.findById(folderId);
Long groupId = folderManager.findRoot(folder).group.id;
Call defaultRedirect = controllers.routes.GroupController.media(groupId, folderId);
if(folder != null && Secured.deleteFolder(folder)) {
if(folder.parent == null) {
defaultRedirect = routes.GroupController.view(groupId);
} else {
defaultRedirect = routes.GroupController.media(groupId, folder.parent.id);
}
folderManager.delete(folder);
}
return redirect(defaultRedirect);
}
/**
* Handles the upload of the temporary avatar image
*
* @param id
* @return
*/
public Result createTempAvatar(Long id) {
Group group = groupManager.findById(id);
if (group == null) {
return notFound();
}
ObjectNode result = Json.newObject();
Http.MultipartFormData body = request().body().asMultipartFormData();
if (body == null) {
result.put("error", "No file attached");
return badRequest(result);
}
Http.MultipartFormData.FilePart avatar = body.getFile("avatarimage");
if (avatar == null) {
result.put("error", "No file with key 'avatarimage'");
return badRequest(result);
}
try {
avatarManager.setTempAvatar(avatar, group.id);
} catch (ValidationException e) {
result.put("error", e.getMessage());
return badRequest(result);
}
result.put("success", getTempAvatar(id).toString());
return ok(result);
}
/**
* Get the temporary avatar image
*
* @param id
* @return
*/
public Result getTempAvatar(Long id) {
ObjectNode result = Json.newObject();
Group group = groupManager.findById(id);
if (group == null) {
return notFound();
}
if (!Secured.editGroup(group)) {
result.put("error", "Not allowed.");
return forbidden(result);
}
File tempAvatar = avatarManager.getTempAvatar(group.id);
if (tempAvatar != null) {
return ok(tempAvatar);
} else {
return notFound();
}
}
/**
* Create the real avatar with the given dimensions
*
* @param id
* @return
*/
public Result createAvatar(long id) {
ObjectNode result = Json.newObject();
Group group = groupManager.findById(id);
if (group == null) {
return notFound();
}
if (!Secured.editGroup(group)) {
result.put("error", "Not allowed.");
return forbidden(result);
}
Form<Avatar> form = formFactory.form(Avatar.class).bindFromRequest();
if (form.hasErrors()) {
result.set("error", form.errorsAsJson());
return badRequest(result);
}
try {
groupManager.saveAvatar(form.get(), group);
result.put("success", "saved");
return ok(result);
} catch (FileOperationException e) {
result.put("error", "Unexpected Error while saving avatar.");
return internalServerError(result);
}
}
/**
* Get the avatar of a group.
*
* @param id group ID
* @param size Size - Possible values: "small", "medium", "large"
* @return
*/
public Result getAvatar(long id, String size) {
Group group = groupManager.findById(id);
if (group != null) {
File avatar;
switch (size) {
case "small":
avatar = avatarManager.getAvatar(AvatarSize.SMALL, group.id);
break;
case "medium":
avatar = avatarManager.getAvatar(AvatarSize.MEDIUM, group.id);
break;
case "large":
avatar = avatarManager.getAvatar(AvatarSize.LARGE, group.id);
break;
default:
avatar = avatarManager.getAvatar(AvatarSize.SMALL, group.id);
}
response().setHeader("Content-disposition", "inline");
if (avatar != null) {
return ok(avatar);
} else {
return notFound();
}
} else {
return notFound();
}
}
}