SEBSERV-27 & SEBSERV-21 password change and refactoring

This commit is contained in:
anhefti 2019-02-25 10:13:18 +01:00
parent f760eba750
commit 97bf08e602
56 changed files with 1135 additions and 836 deletions

View file

@ -14,13 +14,26 @@ import javax.validation.constraints.Size;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
public class PasswordChange {
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.Domain.USER;
import ch.ethz.seb.sebserver.gbl.model.Entity;
public class PasswordChange implements Entity {
public static final String ATTR_NAME_OLD_PASSWORD = "oldPassword";
public static final String ATTR_NAME_NEW_PASSWORD = "newPassword";
public static final String ATTR_NAME_RETYPED_NEW_PASSWORD = "retypedNewPassword";
@NotNull
@JsonProperty(USER.ATTR_UUID)
public final String userId;
@NotNull(message = "user:password:notNull")
@Size(min = 8, max = 255, message = "user:password:size:{min}:{max}:${validatedValue}")
@JsonProperty(ATTR_NAME_OLD_PASSWORD)
private final String oldPassword;
@NotNull(message = "user:password:notNull")
@Size(min = 8, max = 255, message = "user:newPassword:size:{min}:{max}:${validatedValue}")
@JsonProperty(ATTR_NAME_NEW_PASSWORD)
private final String newPassword;
@ -29,13 +42,21 @@ public class PasswordChange {
@JsonCreator
public PasswordChange(
@JsonProperty(USER.ATTR_UUID) final String userId,
@JsonProperty(ATTR_NAME_OLD_PASSWORD) final String oldPassword,
@JsonProperty(ATTR_NAME_NEW_PASSWORD) final String newPassword,
@JsonProperty(ATTR_NAME_RETYPED_NEW_PASSWORD) final String retypedNewPassword) {
this.userId = userId;
this.oldPassword = oldPassword;
this.newPassword = newPassword;
this.retypedNewPassword = retypedNewPassword;
}
public String getOldPassword() {
return this.oldPassword;
}
public String getNewPassword() {
return this.newPassword;
}
@ -48,4 +69,19 @@ public class PasswordChange {
return this.newPassword.equals(this.retypedNewPassword);
}
@Override
public String getModelId() {
return this.userId;
}
@Override
public EntityType entityType() {
return EntityType.USER;
}
@Override
public String getName() {
return "PasswordChange";
}
}

View file

@ -32,9 +32,6 @@ import ch.ethz.seb.sebserver.gbl.model.EntityKey;
public final class UserMod implements UserAccount {
public static final String ATTR_NAME_NEW_PASSWORD = "newPassword";
public static final String ATTR_NAME_RETYPED_NEW_PASSWORD = "retypedNewPassword";
public final String uuid;
/** The foreign key identifier to the institution where the User belongs to */
@ -75,11 +72,11 @@ public final class UserMod implements UserAccount {
@NotNull(message = "user:newPassword:notNull")
@Size(min = 8, max = 255, message = "user:password:size:{min}:{max}:${validatedValue}")
@JsonProperty(ATTR_NAME_NEW_PASSWORD)
@JsonProperty(PasswordChange.ATTR_NAME_NEW_PASSWORD)
private final String newPassword;
@NotNull(message = "user:retypedNewPassword:notNull")
@JsonProperty(ATTR_NAME_RETYPED_NEW_PASSWORD)
@JsonProperty(PasswordChange.ATTR_NAME_RETYPED_NEW_PASSWORD)
private final String retypedNewPassword;
@JsonCreator
@ -89,8 +86,8 @@ public final class UserMod implements UserAccount {
@JsonProperty(USER.ATTR_INSTITUTION_ID) final Long institutionId,
@JsonProperty(USER.ATTR_NAME) final String name,
@JsonProperty(USER.ATTR_USERNAME) final String username,
@JsonProperty(ATTR_NAME_NEW_PASSWORD) final String newPassword,
@JsonProperty(ATTR_NAME_RETYPED_NEW_PASSWORD) final String retypedNewPassword,
@JsonProperty(PasswordChange.ATTR_NAME_NEW_PASSWORD) final String newPassword,
@JsonProperty(PasswordChange.ATTR_NAME_RETYPED_NEW_PASSWORD) final String retypedNewPassword,
@JsonProperty(USER.ATTR_EMAIL) final String email,
@JsonProperty(USER.ATTR_LOCALE) final Locale locale,
@JsonProperty(USER.ATTR_TIMEZONE) final DateTimeZone timeZone,
@ -126,8 +123,8 @@ public final class UserMod implements UserAccount {
public UserMod(final String modelId, final POSTMapper postAttrMapper) {
this.uuid = modelId;
this.institutionId = postAttrMapper.getLong(USER.ATTR_INSTITUTION_ID);
this.newPassword = postAttrMapper.getString(ATTR_NAME_NEW_PASSWORD);
this.retypedNewPassword = postAttrMapper.getString(ATTR_NAME_RETYPED_NEW_PASSWORD);
this.newPassword = postAttrMapper.getString(PasswordChange.ATTR_NAME_NEW_PASSWORD);
this.retypedNewPassword = postAttrMapper.getString(PasswordChange.ATTR_NAME_RETYPED_NEW_PASSWORD);
this.name = postAttrMapper.getString(USER.ATTR_NAME);
this.username = postAttrMapper.getString(USER.ATTR_USERNAME);
this.email = postAttrMapper.getString(USER.ATTR_EMAIL);

View file

@ -15,6 +15,7 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.authorization.PrivilegeType;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
@ -22,11 +23,13 @@ import ch.ethz.seb.sebserver.gbl.model.institution.Institution;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
import ch.ethz.seb.sebserver.gui.content.action.InstitutionActions;
import ch.ethz.seb.sebserver.gui.content.action.UserAccountActions;
import ch.ethz.seb.sebserver.gui.form.FormBuilder;
import ch.ethz.seb.sebserver.gui.form.FormHandle;
import ch.ethz.seb.sebserver.gui.form.PageFormService;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys;
import ch.ethz.seb.sebserver.gui.service.page.PageUtils;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
@ -60,10 +63,6 @@ public class InstitutionForm implements TemplateComposer {
@Override
public void compose(final PageContext pageContext) {
if (log.isDebugEnabled()) {
log.debug("Compose Institutoion Form within PageContext: {}", pageContext);
}
final WidgetFactory widgetFactory = this.pageFormService.getWidgetFactory();
final EntityKey entityKey = pageContext.getEntityKey();
@ -123,15 +122,14 @@ public class InstitutionForm implements TemplateComposer {
.withCondition(() -> entityKey != null))
.buildFor((entityKey == null)
? this.restService.getRestCall(NewInstitution.class)
: this.restService.getRestCall(SaveInstitution.class),
InstitutionActions.postSaveAdapter(pageContext));
: this.restService.getRestCall(SaveInstitution.class));
// propagate content actions to action-pane
final boolean writeGrant = this.currentUser.hasPrivilege(PrivilegeType.WRITE, institution);
final boolean modifyGrant = this.currentUser.hasPrivilege(PrivilegeType.MODIFY, institution);
if (pageContext.isReadonly()) {
formContext.createAction(ActionDefinition.INSTITUTION_NEW)
.withExec(InstitutionActions::newInstitution)
.withAttribute(AttributeKeys.READ_ONLY, "false")
.publishIf(() -> writeGrant);
formContext.createAction(ActionDefinition.INSTITUTION_MODIFY)
.withExec(InstitutionActions::editInstitution)
@ -145,7 +143,12 @@ public class InstitutionForm implements TemplateComposer {
formContext.createAction(ActionDefinition.INSTITUTION_DEACTIVATE)
.withExec(InstitutionActions::deactivateInstitution)
.withConfirm(PageUtils.confirmDeactivation(institution, this.restService))
.publishIf(() -> modifyGrant);
.publishIf(() -> modifyGrant)
.withParentEntityKey(entityKey)
.createAction(ActionDefinition.USER_ACCOUNT_NEW)
.withExec(UserAccountActions::newUserAccount)
.withParentEntity(institution.getEntityKey())
.publishIf(() -> this.currentUser.hasPrivilege(PrivilegeType.WRITE, EntityType.USER));
}
} else {

View file

@ -9,8 +9,6 @@
package ch.ethz.seb.sebserver.gui.content;
import org.eclipse.swt.widgets.Composite;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@ -21,6 +19,7 @@ import ch.ethz.seb.sebserver.gbl.model.institution.Institution;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
import ch.ethz.seb.sebserver.gui.content.action.InstitutionActions;
import ch.ethz.seb.sebserver.gui.content.action.UserAccountActions;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
@ -36,8 +35,6 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@GuiProfile
public class InstitutionList implements TemplateComposer {
private static final Logger log = LoggerFactory.getLogger(InstitutionList.class);
private final WidgetFactory widgetFactory;
private final RestService restService;
private final CurrentUser currentUser;
@ -54,11 +51,6 @@ public class InstitutionList implements TemplateComposer {
@Override
public void compose(final PageContext pageContext) {
if (log.isDebugEnabled()) {
log.debug("Compose Institutoion list within PageContext: {}", pageContext);
}
final Composite content = this.widgetFactory.defaultPageLayout(
pageContext.getParent(),
new LocTextKey("sebserver.institution.list.title"));
@ -86,16 +78,21 @@ public class InstitutionList implements TemplateComposer {
// propagate content actions to action-pane
pageContext.createAction(ActionDefinition.INSTITUTION_NEW)
.withExec(InstitutionActions::newInstitution)
.readonly(false)
.publishIf(() -> this.currentUser.hasPrivilege(PrivilegeType.WRITE, EntityType.INSTITUTION))
.createAction(ActionDefinition.INSTITUTION_VIEW_FROM_LIST)
.withSelectionSupplier(table::getSelection)
.withExec(InstitutionActions::viewInstitution)
.withExec(InstitutionActions::viewInstitutionFromList)
.publish()
.createAction(ActionDefinition.INSTITUTION_MODIFY_FROM__LIST)
.createAction(ActionDefinition.INSTITUTION_MODIFY_FROM_LIST)
.withSelectionSupplier(table::getSelection)
.withExec(InstitutionActions::editInstitutionFromList)
.publishIf(() -> this.currentUser.hasPrivilege(PrivilegeType.MODIFY, EntityType.INSTITUTION));
.readonly(false)
.publishIf(() -> this.currentUser.hasPrivilege(PrivilegeType.MODIFY, EntityType.INSTITUTION))
.createAction(ActionDefinition.USER_ACCOUNT_NEW)
.withExec(UserAccountActions::newUserAccount)
.publishIf(() -> this.currentUser.hasPrivilege(PrivilegeType.WRITE, EntityType.USER));
;
}

View file

@ -25,8 +25,8 @@ import ch.ethz.seb.sebserver.gui.content.activity.ActivitiesPane;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.page.event.ActivitySelectionEvent;
import ch.ethz.seb.sebserver.gui.service.page.event.ActivitySelectionListener;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionEventListener;
import ch.ethz.seb.sebserver.gui.service.page.event.PageEventListener;
import ch.ethz.seb.sebserver.gui.service.page.impl.MainPageState;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@ -119,19 +119,17 @@ public class MainPage implements TemplateComposer {
contentObjectslayout.marginWidth = 0;
contentObjects.setLayout(contentObjectslayout);
contentObjects.setData(PageEventListener.LISTENER_ATTRIBUTE_KEY,
new ActivitySelectionListener() {
new ActionEventListener() {
@Override
public int priority() {
return 2;
}
@Override
public void notify(final ActivitySelectionEvent event) {
public void notify(final ActionEvent event) {
pageContext.composerService().compose(
event.selection.activity.contentPaneComposer,
pageContext
.copyOf(contentObjects)
.withSelection(event.selection));
event.action.definition.contentPaneComposer,
event.action.pageContext().copyOf(contentObjects));
}
});
@ -141,19 +139,17 @@ public class MainPage implements TemplateComposer {
actionPane.setLayout(actionPaneGrid);
actionPane.setData(RWT.CUSTOM_VARIANT, "actionPane");
actionPane.setData(PageEventListener.LISTENER_ATTRIBUTE_KEY,
new ActivitySelectionListener() {
new ActionEventListener() {
@Override
public int priority() {
return 1;
}
@Override
public void notify(final ActivitySelectionEvent event) {
public void notify(final ActionEvent event) {
pageContext.composerService().compose(
event.selection.activity.actionPaneComposer,
pageContext
.copyOf(actionPane)
.withSelection(event.selection));
event.action.definition.actionPaneComposer,
event.action.pageContext().copyOf(actionPane));
}
});

View file

@ -0,0 +1,97 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui.content;
import org.eclipse.swt.widgets.Composite;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.user.PasswordChange;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
import ch.ethz.seb.sebserver.gui.content.action.UserAccountActions;
import ch.ethz.seb.sebserver.gui.form.FormBuilder;
import ch.ethz.seb.sebserver.gui.form.FormHandle;
import ch.ethz.seb.sebserver.gui.form.PageFormService;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.ChangePassword;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccount;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@Lazy
@Component
@GuiProfile
public class UserAccountChangePasswordForm implements TemplateComposer {
private final PageFormService pageFormService;
private final RestService restService;
protected UserAccountChangePasswordForm(
final PageFormService pageFormService,
final RestService restService) {
this.pageFormService = pageFormService;
this.restService = restService;
}
@Override
public void compose(final PageContext pageContext) {
final WidgetFactory widgetFactory = this.pageFormService.getWidgetFactory();
final EntityKey entityKey = pageContext.getEntityKey();
final UserInfo userInfo = this.restService
.getBuilder(GetUserAccount.class)
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
.call()
.get(pageContext::notifyError);
final Composite content = widgetFactory.defaultPageLayout(
pageContext.getParent(),
new LocTextKey("sebserver.useraccount.form.pwchange.title", userInfo.username));
// The Password Change form
final FormHandle<UserInfo> formHandle = this.pageFormService.getBuilder(
pageContext.copyOf(content), 4)
.readonly(pageContext.isReadonly())
.putStaticValueIf(() -> entityKey != null,
Domain.USER.ATTR_ID,
entityKey.getModelId())
.addField(FormBuilder.text(
PasswordChange.ATTR_NAME_OLD_PASSWORD,
"sebserver.useraccount.form.institution.password.old")
.asPasswordField())
.addField(FormBuilder.text(
PasswordChange.ATTR_NAME_NEW_PASSWORD,
"sebserver.useraccount.form.institution.password.new")
.asPasswordField())
.addField(FormBuilder.text(
PasswordChange.ATTR_NAME_RETYPED_NEW_PASSWORD,
"sebserver.useraccount.form.institution.password.retyped")
.asPasswordField()
.withCondition(() -> entityKey != null))
.buildFor(this.restService.getRestCall(ChangePassword.class));
pageContext.createAction(ActionDefinition.USER_ACCOUNT_CHANGE_PASSOWRD_SAVE)
.withExec(formHandle::postChanges)
.publish()
.createAction(ActionDefinition.USER_ACCOUNT_CANCEL_MODIFY)
.withExec(UserAccountActions::cancelEditUserAccount)
.withConfirm("sebserver.overall.action.modify.cancel.confirm")
.publish();
}
}

View file

@ -20,10 +20,13 @@ import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.authorization.PrivilegeType;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.Domain.USER_ROLE;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.institution.Institution;
import ch.ethz.seb.sebserver.gbl.model.user.PasswordChange;
import ch.ethz.seb.sebserver.gbl.model.user.UserAccount;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.model.user.UserMod;
@ -39,6 +42,7 @@ import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageUtils;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitution;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccount;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.NewUserAccount;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.SaveUserAccount;
@ -69,12 +73,9 @@ public class UserAccountForm implements TemplateComposer {
@Override
public void compose(final PageContext pageContext) {
if (log.isDebugEnabled()) {
log.debug("Compose User Account Form within PageContext: {}", pageContext);
}
final WidgetFactory widgetFactory = this.pageFormService.getWidgetFactory();
final EntityKey entityKey = pageContext.getEntityKey();
final EntityKey parentEntityKey = pageContext.getParentEntityKey();
final BooleanSupplier isNew = () -> entityKey == null;
final BooleanSupplier isNotNew = () -> !isNew.getAsBoolean();
final BooleanSupplier isSEBAdmin = () -> this.currentUser.get().hasRole(UserRole.SEB_SERVER_ADMIN);
@ -126,9 +127,12 @@ public class UserAccountForm implements TemplateComposer {
.addField(FormBuilder.singleSelection(
Domain.USER.ATTR_INSTITUTION_ID,
"sebserver.useraccount.form.institution",
String.valueOf(userAccount.getInstitutionId()),
(parentEntityKey != null && parentEntityKey.entityType == EntityType.INSTITUTION)
? parentEntityKey.modelId
: String.valueOf(userAccount.getInstitutionId()),
() -> PageUtils.getInstitutionSelectionResource(this.restService))
.withCondition(isSEBAdmin))
.withCondition(isSEBAdmin)
.readonlyIf(isNotNew))
.addField(FormBuilder.text(
Domain.USER.ATTR_NAME,
"sebserver.useraccount.form.name",
@ -157,44 +161,53 @@ public class UserAccountForm implements TemplateComposer {
StringUtils.join(userAccount.getRoles(), Constants.LIST_SEPARATOR_CHAR),
widgetFactory.getI18nSupport().localizedUserRoleResources()))
.addField(FormBuilder.text(
UserMod.ATTR_NAME_NEW_PASSWORD,
"sebserver.useraccount.form.password",
null)
PasswordChange.ATTR_NAME_NEW_PASSWORD,
"sebserver.useraccount.form.password")
.asPasswordField()
.withCondition(isNew))
.addField(FormBuilder.text(
UserMod.ATTR_NAME_RETYPED_NEW_PASSWORD,
"sebserver.useraccount.form.password.retyped",
null)
PasswordChange.ATTR_NAME_RETYPED_NEW_PASSWORD,
"sebserver.useraccount.form.password.retyped")
.asPasswordField()
.withCondition(isNew))
.buildFor((entityKey == null)
? this.restService.getRestCall(NewUserAccount.class)
: this.restService.getRestCall(SaveUserAccount.class),
UserAccountActions.postSaveAdapter(pageContext));
: this.restService.getRestCall(SaveUserAccount.class));
// propagate content actions to action-pane
final boolean writeGrant = this.currentUser.hasPrivilege(PrivilegeType.WRITE, userAccount);
final boolean modifyGrant = this.currentUser.hasPrivilege(PrivilegeType.MODIFY, userAccount);
if (pageContext.isReadonly()) {
formContext.createAction(ActionDefinition.USER_ACCOUNT_NEW)
.withExec(UserAccountActions::newUserAccount)
formContext.createAction(ActionDefinition.USER_ACCOUNT_CHANGE_PASSOWRD)
.withEntity(userAccount.getEntityKey())
.publishIf(() -> writeGrant);
formContext.createAction(ActionDefinition.USER_ACCOUNT_MODIFY)
.withExec(UserAccountActions::editUserAccount)
.publishIf(() -> modifyGrant);
if (!userAccount.isActive()) {
formContext.createAction(ActionDefinition.USER_ACCOUNT_ACTIVATE)
.withExec(UserAccountActions::activateUserAccount)
.publishIf(() -> modifyGrant);
} else {
formContext.createAction(ActionDefinition.USER_ACCOUNT_DEACTIVATE)
.withExec(UserAccountActions::deactivateUserAccount)
.withConfirm(PageUtils.confirmDeactivation(userAccount, this.restService))
// modifying an UserAccount is not possible if the root institution is inactive
final Institution inst = this.restService.getBuilder(GetInstitution.class)
.withURIVariable(API.PARAM_MODEL_ID, String.valueOf(userAccount.getInstitutionId()))
.call()
.getOrThrow();
if (inst.isActive()) {
formContext.createAction(ActionDefinition.USER_ACCOUNT_NEW)
.withExec(UserAccountActions::newUserAccount)
.publishIf(() -> writeGrant);
formContext.createAction(ActionDefinition.USER_ACCOUNT_MODIFY)
.withExec(UserAccountActions::editUserAccount)
.publishIf(() -> modifyGrant);
if (!userAccount.isActive()) {
formContext.createAction(ActionDefinition.USER_ACCOUNT_ACTIVATE)
.withExec(UserAccountActions::activateUserAccount)
.publishIf(() -> modifyGrant);
} else {
formContext.createAction(ActionDefinition.USER_ACCOUNT_DEACTIVATE)
.withExec(UserAccountActions::deactivateUserAccount)
.withConfirm(PageUtils.confirmDeactivation(userAccount, this.restService))
.publishIf(() -> modifyGrant);
}
}
} else {
formContext.createAction(ActionDefinition.USER_ACCOUNT_SAVE)
.withExec(formHandle::postChanges)

View file

@ -9,8 +9,6 @@
package ch.ethz.seb.sebserver.gui.content;
import org.eclipse.swt.widgets.Composite;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@ -29,8 +27,8 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccounts;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
import ch.ethz.seb.sebserver.gui.table.ColumnDefinition;
import ch.ethz.seb.sebserver.gui.table.EntityTable;
import ch.ethz.seb.sebserver.gui.table.ColumnDefinition.TableFilterAttribute;
import ch.ethz.seb.sebserver.gui.table.EntityTable;
import ch.ethz.seb.sebserver.gui.table.TableFilter.CriteriaType;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@ -39,8 +37,6 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@GuiProfile
public class UserAccountList implements TemplateComposer {
private static final Logger log = LoggerFactory.getLogger(UserAccountList.class);
private final WidgetFactory widgetFactory;
private final RestService restService;
private final CurrentUser currentUser;
@ -60,11 +56,6 @@ public class UserAccountList implements TemplateComposer {
@Override
public void compose(final PageContext pageContext) {
if (log.isDebugEnabled()) {
log.debug("Compose User Account list within PageContext: {}", pageContext);
}
// content page layout with title
final Composite content = this.widgetFactory.defaultPageLayout(
pageContext.getParent(),
@ -113,7 +104,7 @@ public class UserAccountList implements TemplateComposer {
.withSelectionSupplier(table::getSelection)
.withExec(UserAccountActions::viewUserAccountFromList)
.publish()
.createAction(ActionDefinition.USER_ACCOUNT_MODIFY)
.createAction(ActionDefinition.USER_ACCOUNT_MODIFY_FROM__LIST)
.withSelectionSupplier(table::getSelection)
.withExec(UserAccountActions::editUserAccountFromList)
.publishIf(() -> this.currentUser.hasPrivilege(PrivilegeType.MODIFY, EntityType.USER));

View file

@ -8,85 +8,205 @@
package ch.ethz.seb.sebserver.gui.content.action;
import ch.ethz.seb.sebserver.gui.content.InstitutionForm;
import ch.ethz.seb.sebserver.gui.content.InstitutionList;
import ch.ethz.seb.sebserver.gui.content.UserAccountChangePasswordForm;
import ch.ethz.seb.sebserver.gui.content.UserAccountForm;
import ch.ethz.seb.sebserver.gui.content.UserAccountList;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon;
public enum ActionDefinition {
INSTITUTION_VIEW_LIST(
new LocTextKey("sebserver.institution.action.list"),
InstitutionList.class),
INSTITUTION_VIEW_FORM(
new LocTextKey("sebserver.institution.action.form"),
InstitutionForm.class,
INSTITUTION_VIEW_LIST),
INSTITUTION_NEW(
"sebserver.institution.action.new",
ImageIcon.NEW),
new LocTextKey("sebserver.institution.action.new"),
ImageIcon.NEW,
InstitutionForm.class,
INSTITUTION_VIEW_LIST),
INSTITUTION_VIEW_FROM_LIST(
"sebserver.institution.action.list.view",
ImageIcon.SHOW),
new LocTextKey("sebserver.institution.action.list.view"),
ImageIcon.SHOW,
InstitutionForm.class,
INSTITUTION_VIEW_LIST),
INSTITUTION_MODIFY_FROM__LIST(
"sebserver.institution.action.list.modify",
ImageIcon.EDIT),
INSTITUTION_MODIFY_FROM_LIST(
new LocTextKey("sebserver.institution.action.list.modify"),
ImageIcon.EDIT,
InstitutionForm.class,
INSTITUTION_VIEW_LIST),
INSTITUTION_MODIFY(
"sebserver.institution.action.modify",
ImageIcon.EDIT),
new LocTextKey("sebserver.institution.action.modify"),
ImageIcon.EDIT,
InstitutionForm.class,
INSTITUTION_VIEW_LIST),
INSTITUTION_CANCEL_MODIFY(
"sebserver.overall.action.modify.cancel",
ImageIcon.CANCEL),
new LocTextKey("sebserver.overall.action.modify.cancel"),
ImageIcon.CANCEL,
InstitutionForm.class,
INSTITUTION_VIEW_LIST),
INSTITUTION_SAVE(
"sebserver.institution.action.save",
ImageIcon.SAVE),
new LocTextKey("sebserver.institution.action.save"),
ImageIcon.SAVE,
InstitutionForm.class,
INSTITUTION_VIEW_LIST),
INSTITUTION_ACTIVATE(
"sebserver.institution.action.activate",
ImageIcon.INACTIVE),
new LocTextKey("sebserver.institution.action.activate"),
ImageIcon.INACTIVE,
InstitutionForm.class,
INSTITUTION_VIEW_LIST),
INSTITUTION_DEACTIVATE(
"sebserver.institution.action.deactivate",
ImageIcon.ACTIVE),
new LocTextKey("sebserver.institution.action.deactivate"),
ImageIcon.ACTIVE,
InstitutionForm.class,
INSTITUTION_VIEW_LIST),
INSTITUTION_DELETE(
"sebserver.institution.action.modify",
ImageIcon.DELETE),
new LocTextKey("sebserver.institution.action.modify"),
ImageIcon.DELETE,
InstitutionList.class,
INSTITUTION_VIEW_LIST),
USER_ACCOUNT_VIEW_LIST(
new LocTextKey("sebserver.useraccount.action.list"),
UserAccountList.class),
USER_ACCOUNT_VIEW_FORM(
new LocTextKey("sebserver.useraccount.action.form"),
InstitutionForm.class,
USER_ACCOUNT_VIEW_LIST),
USER_ACCOUNT_NEW(
"sebserver.useraccount.action.new",
ImageIcon.NEW),
new LocTextKey("sebserver.useraccount.action.new"),
ImageIcon.NEW,
UserAccountForm.class,
USER_ACCOUNT_VIEW_LIST),
USER_ACCOUNT_VIEW(
"sebserver.useraccount.action.view",
ImageIcon.SHOW),
new LocTextKey("sebserver.useraccount.action.view"),
ImageIcon.SHOW,
UserAccountForm.class,
USER_ACCOUNT_VIEW_LIST),
USER_ACCOUNT_MODIFY_FROM__LIST(
new LocTextKey("sebserver.useraccount.action.list.modify"),
ImageIcon.EDIT,
UserAccountForm.class,
USER_ACCOUNT_VIEW_LIST),
USER_ACCOUNT_MODIFY(
"sebserver.useraccount.action.modify",
ImageIcon.EDIT),
new LocTextKey("sebserver.useraccount.action.modify"),
ImageIcon.EDIT,
UserAccountForm.class,
USER_ACCOUNT_VIEW_LIST),
USER_ACCOUNT_CANCEL_MODIFY(
"sebserver.overall.action.modify.cancel",
ImageIcon.CANCEL),
new LocTextKey("sebserver.overall.action.modify.cancel"),
ImageIcon.CANCEL,
UserAccountForm.class,
USER_ACCOUNT_VIEW_LIST),
USER_ACCOUNT_SAVE(
"sebserver.useraccount.action.save",
ImageIcon.SAVE),
new LocTextKey("sebserver.useraccount.action.save"),
ImageIcon.SAVE,
UserAccountForm.class,
USER_ACCOUNT_VIEW_LIST),
USER_ACCOUNT_ACTIVATE(
"sebserver.useraccount.action.activate",
ImageIcon.INACTIVE),
new LocTextKey("sebserver.useraccount.action.activate"),
ImageIcon.INACTIVE,
UserAccountForm.class,
USER_ACCOUNT_VIEW_LIST),
USER_ACCOUNT_DEACTIVATE(
"sebserver.useraccount.action.deactivate",
ImageIcon.ACTIVE),
new LocTextKey("sebserver.useraccount.action.deactivate"),
ImageIcon.ACTIVE,
UserAccountForm.class,
USER_ACCOUNT_VIEW_LIST),
USER_ACCOUNT_DELETE(
"sebserver.useraccount.action.modify",
ImageIcon.DELETE),
;
new LocTextKey("sebserver.useraccount.action.modify"),
ImageIcon.DELETE,
UserAccountList.class,
USER_ACCOUNT_VIEW_LIST),
public final String name;
USER_ACCOUNT_CHANGE_PASSOWRD(
new LocTextKey("sebserver.useraccount.action.change.password"),
ImageIcon.EDIT,
UserAccountChangePasswordForm.class,
USER_ACCOUNT_VIEW_LIST),
USER_ACCOUNT_CHANGE_PASSOWRD_SAVE(
new LocTextKey("sebserver.useraccount.action.change.password.save"),
ImageIcon.SAVE,
UserAccountForm.class,
USER_ACCOUNT_VIEW_LIST),
;
public final LocTextKey title;
public final ImageIcon icon;
public final Class<? extends TemplateComposer> contentPaneComposer;
public final Class<? extends TemplateComposer> actionPaneComposer;
public final ActionDefinition activityAlias;
private ActionDefinition(final String name, final ImageIcon icon) {
this.name = name;
private ActionDefinition(
final LocTextKey title,
final Class<? extends TemplateComposer> contentPaneComposer) {
this.title = title;
this.icon = null;
this.contentPaneComposer = contentPaneComposer;
this.actionPaneComposer = ActionPane.class;
this.activityAlias = null;
}
private ActionDefinition(
final LocTextKey title,
final Class<? extends TemplateComposer> contentPaneComposer,
final ActionDefinition activityAlias) {
this.title = title;
this.icon = null;
this.contentPaneComposer = contentPaneComposer;
this.actionPaneComposer = ActionPane.class;
this.activityAlias = activityAlias;
}
private ActionDefinition(
final LocTextKey title,
final ImageIcon icon,
final Class<? extends TemplateComposer> contentPaneComposer,
final ActionDefinition activityAlias) {
this.title = title;
this.icon = icon;
this.contentPaneComposer = contentPaneComposer;
this.actionPaneComposer = ActionPane.class;
this.activityAlias = activityAlias;
}
private ActionDefinition(
final LocTextKey title,
final ImageIcon icon,
final Class<? extends TemplateComposer> contentPaneComposer,
final Class<? extends TemplateComposer> actionPaneComposer,
final ActionDefinition activityAlias) {
this.title = title;
this.icon = icon;
this.contentPaneComposer = contentPaneComposer;
this.actionPaneComposer = actionPaneComposer;
this.activityAlias = activityAlias;
}
}

View file

@ -90,7 +90,7 @@ public class ActionPane implements TemplateComposer {
final TreeItem actionItem = ActionPane.this.widgetFactory.treeItemLocalized(
actions,
event.action.definition.name);
event.action.definition.title);
actionItem.setImage(event.action.definition.icon.getImage(
pageContext.getParent().getDisplay()));

View file

@ -9,111 +9,86 @@
package ch.ethz.seb.sebserver.gui.content.action;
import java.util.Collection;
import java.util.function.Function;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.institution.Institution;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.gui.content.activity.Activity;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys;
import ch.ethz.seb.sebserver.gui.service.page.PageMessageException;
import ch.ethz.seb.sebserver.gui.service.page.action.Action;
import ch.ethz.seb.sebserver.gui.service.page.activity.ActivitySelection;
import ch.ethz.seb.sebserver.gui.service.page.event.ActivitySelectionEvent;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.ActivateInstitution;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.DeactivateInstitution;
/** Defines the action execution functions for all Institution action. */
public final class InstitutionActions {
public static Function<Institution, Institution> postSaveAdapter(final PageContext pageContext) {
return inst -> {
goToInstitution(pageContext, inst.getModelId(), false);
return inst;
};
}
public static Result<?> newInstitution(final Action action) {
return Result.of(goToInstitution(action.pageContext, null, true));
}
public static Result<?> viewInstitution(final Action action) {
public static Action viewInstitutionFromList(final Action action) {
return fromSelection(action, false);
}
public static Result<?> editInstitutionFromList(final Action action) {
public static Action editInstitutionFromList(final Action action) {
return fromSelection(action, true);
}
public static Result<?> editInstitution(final Action action) {
return Result.of(goToInstitution(
action.pageContext,
action.pageContext.getAttribute(AttributeKeys.ENTITY_ID),
true));
public static Action editInstitution(final Action action) {
return goToInstitution(action, null, true);
}
public static Result<?> cancelEditInstitution(final Action action) {
if (action.pageContext.getEntityKey() == null) {
final ActivitySelection toList = Activity.INSTITUTION_LIST.createSelection();
action.pageContext.publishPageEvent(new ActivitySelectionEvent(toList));
return Result.of(toList);
public static Action cancelEditInstitution(final Action action) {
if (action.getEntityKey() == null) {
final PageContext pageContext = action.pageContext();
final Action toList = pageContext.createAction(ActionDefinition.INSTITUTION_VIEW_LIST);
pageContext.publishPageEvent(new ActionEvent(toList, false));
return toList;
} else {
return Result.of(goToInstitution(
action.pageContext,
action.pageContext.getAttribute(AttributeKeys.ENTITY_ID),
false));
return goToInstitution(action, null, false);
}
}
public static Result<?> activateInstitution(final Action action) {
public static Action activateInstitution(final Action action) {
return action.restService
.getBuilder(ActivateInstitution.class)
.withURIVariable(
API.PARAM_MODEL_ID,
action.pageContext.getAttribute(AttributeKeys.ENTITY_ID))
action.pageContext().getAttribute(AttributeKeys.ENTITY_ID))
.call()
.map(report -> goToInstitution(action.pageContext, report.getSingleSource().modelId, false));
.map(report -> goToInstitution(action, report.getSingleSource().modelId, false))
.getOrThrow();
}
public static Result<?> deactivateInstitution(final Action action) {
public static Action deactivateInstitution(final Action action) {
return action.restService
.getBuilder(DeactivateInstitution.class)
.withURIVariable(
API.PARAM_MODEL_ID,
action.pageContext.getAttribute(AttributeKeys.ENTITY_ID))
action.pageContext().getAttribute(AttributeKeys.ENTITY_ID))
.call()
.map(report -> goToInstitution(action.pageContext, report.getSingleSource().modelId, false));
.map(report -> goToInstitution(action, report.getSingleSource().modelId, false))
.getOrThrow();
}
private static Result<?> fromSelection(final Action action, final boolean edit) {
return Result.tryCatch(() -> {
final Collection<String> selection = action.getSelectionSupplier().get();
if (selection.isEmpty()) {
throw new PageMessageException("sebserver.institution.info.pleaseSelect");
}
private static Action fromSelection(final Action action, final boolean edit) {
final Collection<String> selection = action.getSelectionSupplier().get();
if (selection.isEmpty()) {
throw new PageMessageException("sebserver.institution.info.pleaseSelect");
}
return goToInstitution(action.pageContext, selection.iterator().next(), edit);
});
return goToInstitution(action, selection.iterator().next(), edit);
}
private static ActivitySelection goToInstitution(
final PageContext pageContext,
private static Action goToInstitution(
final Action action,
final String modelId,
final boolean edit) {
final ActivitySelection activitySelection = Activity.INSTITUTION_FORM
.createSelection()
.withAttribute(AttributeKeys.READ_ONLY, String.valueOf(!edit));
action.withAttribute(AttributeKeys.READ_ONLY, String.valueOf(!edit));
if (modelId != null) {
activitySelection.withEntity(new EntityKey(modelId, EntityType.INSTITUTION));
action.withEntity(new EntityKey(modelId, EntityType.INSTITUTION));
}
pageContext.publishPageEvent(new ActivitySelectionEvent(activitySelection));
return activitySelection;
return action;
}
}

View file

@ -9,110 +9,87 @@
package ch.ethz.seb.sebserver.gui.content.action;
import java.util.Collection;
import java.util.function.Function;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.gui.content.activity.Activity;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys;
import ch.ethz.seb.sebserver.gui.service.page.PageMessageException;
import ch.ethz.seb.sebserver.gui.service.page.action.Action;
import ch.ethz.seb.sebserver.gui.service.page.activity.ActivitySelection;
import ch.ethz.seb.sebserver.gui.service.page.event.ActivitySelectionEvent;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.ActivateUserAccount;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.DeactivateUserAccount;
public final class UserAccountActions {
public static Function<UserInfo, UserInfo> postSaveAdapter(final PageContext pageContext) {
return userAccount -> {
goToUserAccount(pageContext, userAccount.getModelId(), false);
return userAccount;
};
public static Action newUserAccount(final Action action) {
return goToUserAccount(action, null, true);
}
public static Result<?> newUserAccount(final Action action) {
return Result.of(goToUserAccount(action.pageContext, null, true));
}
public static Result<?> viewUserAccountFromList(final Action action) {
public static Action viewUserAccountFromList(final Action action) {
return fromSelection(action, false);
}
public static Result<?> editUserAccountFromList(final Action action) {
public static Action editUserAccountFromList(final Action action) {
return fromSelection(action, true);
}
public static Result<?> editUserAccount(final Action action) {
return Result.of(goToUserAccount(
action.pageContext,
action.pageContext.getAttribute(AttributeKeys.ENTITY_ID),
true));
public static Action editUserAccount(final Action action) {
return goToUserAccount(action, null, true);
}
public static Result<?> cancelEditUserAccount(final Action action) {
if (action.pageContext.getEntityKey() == null) {
final ActivitySelection toList = Activity.USER_ACCOUNT_LIST.createSelection();
action.pageContext.publishPageEvent(new ActivitySelectionEvent(toList));
return Result.of(toList);
public static Action cancelEditUserAccount(final Action action) {
if (action.pageContext().getEntityKey() == null) {
final Action toList = action.pageContext().createAction(ActionDefinition.USER_ACCOUNT_VIEW_LIST);
action.pageContext().publishPageEvent(new ActionEvent(toList, false));
return toList;
} else {
return Result.of(goToUserAccount(
action.pageContext,
action.pageContext.getAttribute(AttributeKeys.ENTITY_ID),
false));
return goToUserAccount(action, null, false);
}
}
public static Result<?> activateUserAccount(final Action action) {
public static Action activateUserAccount(final Action action) {
return action.restService
.getBuilder(ActivateUserAccount.class)
.withURIVariable(
API.PARAM_MODEL_ID,
action.pageContext.getAttribute(AttributeKeys.ENTITY_ID))
action.pageContext().getAttribute(AttributeKeys.ENTITY_ID))
.call()
.map(report -> goToUserAccount(action.pageContext, report.getSingleSource().modelId, false));
.map(report -> goToUserAccount(action, report.getSingleSource().modelId, false))
.getOrThrow();
}
public static Result<?> deactivateUserAccount(final Action action) {
public static Action deactivateUserAccount(final Action action) {
return action.restService
.getBuilder(DeactivateUserAccount.class)
.withURIVariable(
API.PARAM_MODEL_ID,
action.pageContext.getAttribute(AttributeKeys.ENTITY_ID))
action.pageContext().getAttribute(AttributeKeys.ENTITY_ID))
.call()
.map(report -> goToUserAccount(action.pageContext, report.getSingleSource().modelId, false));
.map(report -> goToUserAccount(action, report.getSingleSource().modelId, false))
.getOrThrow();
}
private static Result<?> fromSelection(final Action action, final boolean edit) {
return Result.tryCatch(() -> {
final Collection<String> selection = action.getSelectionSupplier().get();
if (selection.isEmpty()) {
throw new PageMessageException("sebserver.useraccount.info.pleaseSelect");
}
private static Action fromSelection(final Action action, final boolean edit) {
final Collection<String> selection = action.getSelectionSupplier().get();
if (selection.isEmpty()) {
throw new PageMessageException("sebserver.useraccount.info.pleaseSelect");
}
return goToUserAccount(action.pageContext, selection.iterator().next(), edit);
});
return goToUserAccount(action, selection.iterator().next(), edit);
}
private static ActivitySelection goToUserAccount(
final PageContext pageContext,
private static Action goToUserAccount(
final Action action,
final String modelId,
final boolean edit) {
final ActivitySelection activitySelection = Activity.USER_ACCOUNT_FORM
.createSelection()
.withAttribute(AttributeKeys.READ_ONLY, String.valueOf(!edit));
action.withAttribute(AttributeKeys.READ_ONLY, String.valueOf(!edit));
if (modelId != null) {
activitySelection.withEntity(new EntityKey(modelId, EntityType.USER));
action.withEntity(new EntityKey(modelId, EntityType.USER));
}
pageContext.publishPageEvent(new ActivitySelectionEvent(activitySelection));
return activitySelection;
return action;
}
}

View file

@ -8,10 +8,6 @@
package ch.ethz.seb.sebserver.gui.content.activity;
import java.util.Collection;
import java.util.EnumMap;
import java.util.Map;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Event;
@ -30,15 +26,12 @@ import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys;
import ch.ethz.seb.sebserver.gui.service.page.activity.ActivityActionHandler;
import ch.ethz.seb.sebserver.gui.service.page.activity.ActivitySelection;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.page.action.Action;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionEventListener;
import ch.ethz.seb.sebserver.gui.service.page.event.ActivitySelectionEvent;
import ch.ethz.seb.sebserver.gui.service.page.event.PageEventListener;
import ch.ethz.seb.sebserver.gui.service.page.impl.MainPageState;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
@ -50,26 +43,14 @@ public class ActivitiesPane implements TemplateComposer {
private static final String ATTR_ACTIVITY_SELECTION = "ACTIVITY_SELECTION";
private final WidgetFactory widgetFactory;
private final RestService restService;
private final CurrentUser currentUser;
// TODO are those really needed?
private final Map<ActionDefinition, ActivityActionHandler> activityActionHandler =
new EnumMap<>(ActionDefinition.class);
public ActivitiesPane(
final WidgetFactory widgetFactory,
final RestService restService,
final CurrentUser currentUser,
final Collection<ActivityActionHandler> activityActionHandler) {
final CurrentUser currentUser) {
this.widgetFactory = widgetFactory;
this.restService = restService;
this.currentUser = currentUser;
for (final ActivityActionHandler aah : activityActionHandler) {
this.activityActionHandler.put(aah.handlesAction(), aah);
}
}
@Override
@ -89,7 +70,7 @@ public class ActivitiesPane implements TemplateComposer {
pageContext.getParent(),
SWT.SINGLE | SWT.FULL_SELECTION);
final GridData navigationGridData = new GridData(SWT.FILL, SWT.FILL, true, true);
navigationGridData.horizontalIndent = 10;
//navigationGridData.horizontalIndent = 10;
navigation.setLayoutData(navigationGridData);
// Institution
@ -98,18 +79,20 @@ public class ActivitiesPane implements TemplateComposer {
// institutions (list) as root
final TreeItem institutions = this.widgetFactory.treeItemLocalized(
navigation,
Activity.INSTITUTION_LIST.title);
injectActivitySelection(institutions, Activity.INSTITUTION_LIST.createSelection());
ActionDefinition.INSTITUTION_VIEW_LIST.title);
injectActivitySelection(
institutions,
pageContext.createAction(ActionDefinition.INSTITUTION_VIEW_LIST));
} else {
// otherwise show the form of the institution for current user
final TreeItem institutions = this.widgetFactory.treeItemLocalized(
navigation,
Activity.INSTITUTION_FORM.title);
ActionDefinition.INSTITUTION_VIEW_FORM.title);
injectActivitySelection(
institutions,
Activity.INSTITUTION_FORM.createSelection()
.withEntity(new EntityKey(userInfo.institutionId, EntityType.INSTITUTION))
pageContext.createAction(ActionDefinition.INSTITUTION_VIEW_FORM)
.withEntity(userInfo.institutionId, EntityType.INSTITUTION)
.withAttribute(AttributeKeys.READ_ONLY, "true"));
}
@ -118,73 +101,40 @@ public class ActivitiesPane implements TemplateComposer {
if (this.currentUser.hasPrivilege(PrivilegeType.READ_ONLY, EntityType.USER)) {
final TreeItem userAccounts = this.widgetFactory.treeItemLocalized(
navigation,
Activity.USER_ACCOUNT_LIST.title);
injectActivitySelection(userAccounts, Activity.USER_ACCOUNT_LIST.createSelection());
ActionDefinition.USER_ACCOUNT_VIEW_LIST.title);
injectActivitySelection(
userAccounts,
pageContext.createAction(ActionDefinition.USER_ACCOUNT_VIEW_LIST));
} else {
// otherwise show the user account form for current user
final TreeItem userAccounts = this.widgetFactory.treeItemLocalized(
navigation,
Activity.USER_ACCOUNT_FORM.title);
ActionDefinition.USER_ACCOUNT_VIEW_FORM.title);
injectActivitySelection(
userAccounts,
Activity.USER_ACCOUNT_FORM.createSelection()
pageContext.createAction(ActionDefinition.USER_ACCOUNT_VIEW_FORM)
.withEntity(this.currentUser.get().getEntityKey())
.withAttribute(AttributeKeys.READ_ONLY, "true"));
}
//
// final TreeItem configs = this.widgetFactory.treeItemLocalized(
// navigation,
// "org.sebserver.activities.sebconfigs");
// ActivitySelection.set(configs, Activity.SEB_CONFIGS.createSelection());
//
// final TreeItem config = this.widgetFactory.treeItemLocalized(
// configs,
// "org.sebserver.activities.sebconfig");
// ActivitySelection.set(config, Activity.SEB_CONFIG.createSelection());
//
// final TreeItem configTemplates = this.widgetFactory.treeItemLocalized(
// configs,
// "org.sebserver.activities.sebconfig.templates");
// ActivitySelection.set(configTemplates, Activity.SEB_CONFIG_TEMPLATES.createSelection());
//
// final TreeItem exams = this.widgetFactory.treeItemLocalized(
// navigation,
// "org.sebserver.activities.exam");
// ActivitySelection.set(exams, Activity.EXAMS.createSelection());
//
// final TreeItem monitoring = this.widgetFactory.treeItemLocalized(
// navigation,
// "org.sebserver.activities.monitoring");
// ActivitySelection.set(monitoring, Activity.MONITORING.createSelection());
//
// final TreeItem runningExams = this.widgetFactory.treeItemLocalized(
// monitoring,
// "org.sebserver.activities.runningExams");
// ActivitySelection.set(runningExams, Activity.RUNNING_EXAMS.createSelection()
// .withExpandFunction(this::runningExamExpand));
// runningExams.setItemCount(1);
//
// final TreeItem logs = this.widgetFactory.treeItemLocalized(
// monitoring,
// "org.sebserver.activities.logs");
// ActivitySelection.set(logs, Activity.LOGS.createSelection());
navigation.addListener(SWT.Expand, this::handleExpand);
navigation.addListener(SWT.Selection, event -> handleSelection(pageContext, event));
navigation.setData(
PageEventListener.LISTENER_ATTRIBUTE_KEY,
new ActionEventListener() {
@Override
public void notify(final ActionEvent event) {
// final ActivityActionHandler aah =
// ActivitiesPane.this.activityActionHandler.get(event.actionDefinition);
// if (aah != null) {
// aah.notifyAction(event, navigation, pageContext);
// }
// on case of an Action with ActivitySelection, reset the MainPageState
if (event.source instanceof ActivitySelection) {
final MainPageState mainPageState = MainPageState.get();
mainPageState.activitySelection = (ActivitySelection) event.source;
final MainPageState mainPageState = MainPageState.get();
mainPageState.action = event.action;
if (!event.activity) {
final EntityKey entityKey = event.action.getEntityKey();
final String modelId = (entityKey != null) ? entityKey.modelId : null;
final TreeItem item = findItemByActionDefinition(
navigation.getItems(),
event.action.definition,
modelId);
if (item != null) {
navigation.select(item);
}
}
}
});
@ -192,79 +142,47 @@ public class ActivitiesPane implements TemplateComposer {
// page-selection on (re)load
final MainPageState mainPageState = MainPageState.get();
if (mainPageState.activitySelection == null ||
mainPageState.activitySelection.activity == Activity.NONE) {
mainPageState.activitySelection = getActivitySelection(navigation.getItem(0));
if (mainPageState.action == null) {
mainPageState.action = getActivitySelection(navigation.getItem(0));
}
pageContext.publishPageEvent(
new ActivitySelectionEvent(mainPageState.activitySelection));
new ActionEvent(mainPageState.action, false));
navigation.select(navigation.getItem(0));
}
// private void runningExamExpand(final TreeItem item) {
// item.removeAll();
// final List<EntityName> runningExamNames = this.restService
// .sebServerCall(GetRunningExamNames.class)
// .onError(t -> {
// throw new RuntimeException(t);
// });
//
// if (runningExamNames != null) {
// for (final EntityName runningExamName : runningExamNames) {
// final TreeItem runningExams = this.widgetFactory.treeItemLocalized(
// item,
// runningExamName.name);
// ActivitySelection.set(runningExams, Activity.RUNNING_EXAM.createSelection(runningExamName));
// }
// }
// }
private void handleExpand(final Event event) {
final TreeItem treeItem = (TreeItem) event.item;
System.out.println("opened: " + treeItem);
final ActivitySelection activity = getActivitySelection(treeItem);
if (activity != null) {
activity.processExpand(treeItem);
}
}
private void handleSelection(final PageContext composerCtx, final Event event) {
final TreeItem treeItem = (TreeItem) event.item;
System.out.println("selected: " + treeItem);
final MainPageState mainPageState = MainPageState.get();
final ActivitySelection activitySelection = getActivitySelection(treeItem);
if (mainPageState.activitySelection == null) {
mainPageState.activitySelection = Activity.NONE.createSelection();
}
if (!mainPageState.activitySelection.equals(activitySelection)) {
mainPageState.activitySelection = activitySelection;
final Action action = getActivitySelection(treeItem);
if (mainPageState.action.definition != action.definition) {
mainPageState.action = action;
composerCtx.publishPageEvent(
new ActivitySelectionEvent(mainPageState.activitySelection));
new ActionEvent(action, true));
}
}
static final TreeItem findItemByActivity(
static final TreeItem findItemByActionDefinition(
final TreeItem[] items,
final Activity activity,
final String objectId) {
final ActionDefinition actionDefinition,
final String modelId) {
if (items == null) {
return null;
}
for (final TreeItem item : items) {
final ActivitySelection activitySelection = getActivitySelection(item);
final String id = activitySelection.getEntityId();
if (activitySelection != null && activitySelection.activity == activity &&
(id == null || (objectId != null && objectId.equals(id)))) {
final Action action = getActivitySelection(item);
final EntityKey entityKey = action.getEntityKey();
if (action != null
&& (action.definition == actionDefinition || action.definition == actionDefinition.activityAlias) &&
(entityKey == null || (modelId != null && modelId.equals(entityKey.modelId)))) {
return item;
}
final TreeItem _item = findItemByActivity(item.getItems(), activity, objectId);
final TreeItem _item = findItemByActionDefinition(item.getItems(), actionDefinition, modelId);
if (_item != null) {
return _item;
}
@ -273,8 +191,8 @@ public class ActivitiesPane implements TemplateComposer {
return null;
}
static final TreeItem findItemByActivity(final TreeItem[] items, final Activity activity) {
return findItemByActivity(items, activity, null);
static final TreeItem findItemByActionDefinition(final TreeItem[] items, final ActionDefinition actionDefinition) {
return findItemByActionDefinition(items, actionDefinition, null);
}
static final void expand(final TreeItem item) {
@ -286,12 +204,12 @@ public class ActivitiesPane implements TemplateComposer {
expand(item.getParentItem());
}
public static ActivitySelection getActivitySelection(final TreeItem item) {
return (ActivitySelection) item.getData(ATTR_ACTIVITY_SELECTION);
public static Action getActivitySelection(final TreeItem item) {
return (Action) item.getData(ATTR_ACTIVITY_SELECTION);
}
public static void injectActivitySelection(final TreeItem item, final ActivitySelection selection) {
item.setData(ATTR_ACTIVITY_SELECTION, selection);
public static void injectActivitySelection(final TreeItem item, final Action action) {
item.setData(ATTR_ACTIVITY_SELECTION, action);
}
}

View file

@ -1,81 +0,0 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui.content.activity;
import ch.ethz.seb.sebserver.gui.content.InstitutionForm;
import ch.ethz.seb.sebserver.gui.content.InstitutionList;
import ch.ethz.seb.sebserver.gui.content.UserAccountForm;
import ch.ethz.seb.sebserver.gui.content.UserAccountList;
import ch.ethz.seb.sebserver.gui.content.action.ActionPane;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.page.activity.ActivitySelection;
import ch.ethz.seb.sebserver.gui.service.page.impl.TODOTemplate;
public enum Activity {
NONE(TODOTemplate.class, TODOTemplate.class),
INSTITUTION_LIST(
InstitutionList.class,
ActionPane.class,
new LocTextKey("sebserver.activities.institution")),
INSTITUTION_FORM(
InstitutionForm.class,
ActionPane.class,
new LocTextKey("sebserver.activities.institution")),
USER_ACCOUNT_LIST(
UserAccountList.class,
ActionPane.class,
new LocTextKey("sebserver.activities.useraccount")),
USER_ACCOUNT_FORM(
UserAccountForm.class,
ActionPane.class,
new LocTextKey("sebserver.activities.useraccount")),
// USERS(UserAccountsForm.class, ActionPane.class),
//
// EXAMS(ExamsListPage.class, ActionPane.class),
// SEB_CONFIGS(SEBConfigurationForm.class, ActionPane.class),
// SEB_CONFIG(SEBConfigurationPage.class, ActionPane.class),
// SEB_CONFIG_TEMPLATES(TODOTemplate.class, ActionPane.class),
// MONITORING(MonitoringForm.class, ActionPane.class),
// RUNNING_EXAMS(RunningExamForm.class, ActionPane.class),
// RUNNING_EXAM(RunningExamPage.class, ActionPane.class, AttributeKeys.EXAM_ID),
// LOGS(TODOTemplate.class, ActionPane.class),
;
public final LocTextKey title;
public final Class<? extends TemplateComposer> contentPaneComposer;
public final Class<? extends TemplateComposer> actionPaneComposer;
//public final String modelIdAttribute;
private Activity(
final Class<? extends TemplateComposer> objectPaneComposer,
final Class<? extends TemplateComposer> selectionPaneComposer,
final LocTextKey title) {
this.title = title;
this.contentPaneComposer = objectPaneComposer;
this.actionPaneComposer = selectionPaneComposer;
}
private Activity(
final Class<? extends TemplateComposer> objectPaneComposer,
final Class<? extends TemplateComposer> selectionPaneComposer) {
this.title = null;
this.contentPaneComposer = objectPaneComposer;
this.actionPaneComposer = selectionPaneComposer;
}
public final ActivitySelection createSelection() {
return new ActivitySelection(this);
}
}

View file

@ -1,6 +1,6 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
@ -10,13 +10,14 @@ package ch.ethz.seb.sebserver.gui.form;
import java.util.function.BooleanSupplier;
abstract class FieldBuilder {
public abstract class FieldBuilder {
int spanLabel = -1;
int spanInput = -1;
int spanEmptyCell = -1;
boolean autoEmptyCellSeparation = false;
String group = null;
BooleanSupplier condition = null;
boolean readonly = false;
final String name;
final String label;
@ -58,6 +59,16 @@ abstract class FieldBuilder {
return this;
}
public FieldBuilder readonly(final boolean readonly) {
this.readonly = readonly;
return this;
}
public FieldBuilder readonlyIf(final BooleanSupplier readonly) {
this.readonly = readonly != null && readonly.getAsBoolean();
return this;
}
abstract void build(FormBuilder builder);
}

View file

@ -44,7 +44,6 @@ public final class Form implements FormBinding {
private final Map<String, String> staticValues = new LinkedHashMap<>();
private final MultiValueMap<String, FormFieldAccessor> formFields = new LinkedMultiValueMap<>();
//private final Map<String, List<FormFieldAccessor>> formFields = new LinkedHashMap<>();
private final Map<String, Form> subForms = new LinkedHashMap<>();
private final Map<String, List<Form>> subLists = new LinkedHashMap<>();
private final Map<String, Set<String>> groups = new LinkedHashMap<>();

View file

@ -10,10 +10,10 @@ package ch.ethz.seb.sebserver.gui.form;
import java.util.List;
import java.util.function.BooleanSupplier;
import java.util.function.Function;
import java.util.function.Supplier;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
@ -25,6 +25,7 @@ import org.slf4j.LoggerFactory;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.util.Tuple;
import ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService;
@ -159,15 +160,13 @@ public class FormBuilder {
return this;
}
public <T> FormHandle<T> buildFor(
final RestCall<T> post,
final Function<T, T> postPostHandle) {
public <T extends Entity> FormHandle<T> buildFor(
final RestCall<T> post) {
return new FormHandle<>(
this.pageContext,
this.form,
post,
(postPostHandle == null) ? Function.identity() : postPostHandle,
this.polyglotPageService.getI18nSupport());
}
@ -177,6 +176,10 @@ public class FormBuilder {
empty.setText("");
}
public static TextFieldBuilder text(final String name, final String label) {
return new TextFieldBuilder(name, label, null);
}
public static TextFieldBuilder text(final String name, final String label, final String value) {
return new TextFieldBuilder(name, label, value);
}
@ -207,8 +210,9 @@ public class FormBuilder {
Label labelLocalized(final Composite parent, final String locTextKey, final int hspan) {
final Label label = this.widgetFactory.labelLocalized(parent, locTextKey);
final GridData gridData = new GridData(SWT.RIGHT, SWT.TOP, true, false, hspan, 1);
gridData.verticalIndent = 5;
gridData.verticalIndent = 4;
label.setLayoutData(gridData);
label.setData(RWT.CUSTOM_VARIANT, "head");
return label;
}
@ -216,6 +220,7 @@ public class FormBuilder {
final Label label = new Label(parent, SWT.NONE);
label.setText((StringUtils.isNoneBlank(value)) ? value : Constants.EMPTY_NOTE);
final GridData gridData = new GridData(SWT.LEFT, SWT.CENTER, true, false, hspan, 1);
gridData.verticalIndent = 4;
label.setLayoutData(gridData);
return label;
}

View file

@ -9,11 +9,11 @@
package ch.ethz.seb.sebserver.gui.form;
import java.util.function.Consumer;
import java.util.function.Function;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
import ch.ethz.seb.sebserver.gui.form.Form.FormFieldAccessor;
@ -21,12 +21,13 @@ import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.FieldValidationError;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys;
import ch.ethz.seb.sebserver.gui.service.page.action.Action;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCallError;
public class FormHandle<T> {
public class FormHandle<T extends Entity> {
private static final Logger log = LoggerFactory.getLogger(FormHandle.class);
@ -35,28 +36,26 @@ public class FormHandle<T> {
private final PageContext pageContext;
private final Form form;
private final RestCall<T> post;
private final Function<T, T> postPostHandle;
private final I18nSupport i18nSupport;
FormHandle(
final PageContext pageContext,
final Form form,
final RestCall<T> post,
final Function<T, T> postPostHandle,
final I18nSupport i18nSupport) {
this.pageContext = pageContext;
this.form = form;
this.post = post;
this.postPostHandle = postPostHandle;
this.i18nSupport = i18nSupport;
}
public final Result<T> postChanges(final Action action) {
return doAPIPost(action.definition);
public final Action postChanges(final Action action) {
return doAPIPost(action.definition)
.getOrThrow();
}
public Result<T> doAPIPost(final ActionDefinition action) {
public Result<Action> doAPIPost(final ActionDefinition actionDefinition) {
this.form.process(
name -> true,
fieldAccessor -> fieldAccessor.resetError());
@ -66,11 +65,15 @@ public class FormHandle<T> {
.withFormBinding(this.form)
.call()
.map(result -> {
this.pageContext.publishPageEvent(new ActionEvent(action, result));
return result;
final Action action = this.pageContext.createAction(actionDefinition)
.withAttribute(AttributeKeys.READ_ONLY, "true")
.withEntity(result.getEntityKey());
this.pageContext.publishPageEvent(new ActionEvent(action, false));
return action;
})
.onErrorDo(this::handleError)
.map(this.postPostHandle);
//.map(this.postPostHandle)
;
}
private void handleError(final Throwable error) {

View file

@ -1,6 +1,6 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
@ -27,7 +27,7 @@ public final class ImageUploadFieldBuilder extends FieldBuilder {
final ImageUpload imageUpload = builder.widgetFactory.imageUploadLocalized(
builder.formParent,
new LocTextKey("sebserver.overall.upload"),
builder.readonly);
builder.readonly || this.readonly);
final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false, this.spanInput, 1);
imageUpload.setLayoutData(gridData);
imageUpload.setImageBase64(this.value);

View file

@ -13,11 +13,13 @@ import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
@ -26,6 +28,7 @@ import ch.ethz.seb.sebserver.gbl.util.Tuple;
import ch.ethz.seb.sebserver.gui.widget.MultiSelection;
import ch.ethz.seb.sebserver.gui.widget.Selection;
import ch.ethz.seb.sebserver.gui.widget.SingleSelection;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
public final class SelectionFieldBuilder extends FieldBuilder {
@ -56,7 +59,7 @@ public final class SelectionFieldBuilder extends FieldBuilder {
@Override
void build(final FormBuilder builder) {
final Label lab = builder.labelLocalized(builder.formParent, this.label, this.spanLabel);
if (builder.readonly) {
if (builder.readonly || this.readonly) {
buildReadOnly(builder, lab);
} else {
buildInput(builder, lab);
@ -85,38 +88,45 @@ public final class SelectionFieldBuilder extends FieldBuilder {
/* Build the read-only representation of the selection field */
private void buildReadOnly(final FormBuilder builder, final Label lab) {
builder.form.putField(
this.name, lab,
builder.valueLabel(
builder.formParent,
getSelectionValue(this.value, this.multi),
this.spanInput));
}
if (this.multi) {
final Composite composite = new Composite(builder.formParent, SWT.NONE);
final GridLayout gridLayout = new GridLayout(1, true);
gridLayout.verticalSpacing = 1;
gridLayout.marginLeft = 0;
gridLayout.marginHeight = 0;
gridLayout.marginWidth = 0;
composite.setLayout(gridLayout);
if (StringUtils.isBlank(this.value)) {
createMuliSelectionReadonlyLabel(composite, Constants.EMPTY_NOTE);
} else {
final Collection<String> keys = Arrays.asList(StringUtils.split(this.value, Constants.LIST_SEPARATOR));
this.itemsSupplier.get()
.stream()
.filter(tuple -> keys.contains(tuple._1))
.map(tuple -> tuple._2)
.forEach(v -> createMuliSelectionReadonlyLabel(composite, v));
}
/*
* For Single selection just the selected value, for multi selection a comma
* separated list of values within a String value.
*
* @param key the key or keys, in case of multi selection a comma separated String of keys
*
* @param multi indicates multi seleciton
*
* @return selected value or comma separated String list of selected values
*/
private String getSelectionValue(final String key, final boolean multi) {
if (multi) {
final Collection<String> keys = Arrays.asList(StringUtils.split(key, Constants.LIST_SEPARATOR));
return StringUtils.join(this.itemsSupplier.get().stream()
.filter(tuple -> keys.contains(tuple._1))
.map(tuple -> tuple._2)
.collect(Collectors.toList()),
Constants.LIST_SEPARATOR);
} else {
return this.itemsSupplier.get().stream()
.filter(tuple -> key.equals(tuple._1))
.findFirst()
.map(tuple -> tuple._2)
.orElse(null);
builder.form.putField(
this.name, lab,
builder.valueLabel(
builder.formParent,
this.itemsSupplier.get().stream()
.filter(tuple -> this.value.equals(tuple._1))
.findFirst()
.map(tuple -> tuple._2)
.orElse(null),
this.spanInput));
}
}
private void createMuliSelectionReadonlyLabel(final Composite composite, final String value) {
final Label label = new Label(composite, SWT.NONE);
final GridData gridData = new GridData(SWT.FILL, SWT.CENTER, true, true);
label.setLayoutData(gridData);
label.setData(RWT.CUSTOM_VARIANT, CustomVariant.SELECTION_READONLY.key);
label.setText(value);
}
}

View file

@ -1,6 +1,6 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
@ -33,7 +33,7 @@ public final class TextFieldBuilder extends FieldBuilder {
}
final Label lab = builder.labelLocalized(builder.formParent, this.label, this.spanLabel);
if (builder.readonly) {
if (builder.readonly || this.readonly) {
builder.form.putField(this.name, lab,
builder.valueLabel(builder.formParent, this.value, this.spanInput));
} else {

View file

@ -15,7 +15,6 @@ import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.action.Action;
import ch.ethz.seb.sebserver.gui.service.page.activity.ActivitySelection;
import ch.ethz.seb.sebserver.gui.service.page.event.PageEvent;
public interface PageContext {
@ -75,6 +74,8 @@ public interface PageContext {
* @return the parent Component */
Composite getParent();
PageContext copy();
/** Create a copy of this PageContext with a new parent Composite.
*
* @param parent the new parent Composite
@ -95,11 +96,11 @@ public interface PageContext {
* @return this PageContext instance (builder pattern) */
PageContext withAttribute(String key, String value);
default PageContext withSelection(final ActivitySelection selection) {
return withSelection(selection, true);
}
PageContext withSelection(ActivitySelection selection, boolean clearAttributes);
// default PageContext withSelection(final ActivitySelection selection) {
// return withSelection(selection, true);
// }
//
// PageContext withSelection(ActivitySelection selection, boolean clearAttributes);
String getAttribute(String name);
@ -157,5 +158,4 @@ public interface PageContext {
void publishPageMessage(LocTextKey title, LocTextKey message);
void publishPageMessage(PageMessageException pme);
}

View file

@ -19,6 +19,7 @@ import org.slf4j.LoggerFactory;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.util.Tuple;
@ -57,6 +58,7 @@ public final class PageUtils {
public static List<Tuple<String>> getInstitutionSelectionResource(final RestService restService) {
return restService.getBuilder(GetInstitutionNames.class)
.withQueryParam(Domain.INSTITUTION.ATTR_ACTIVE, "true")
.call()
.getOr(Collections.emptyList())
.stream()

View file

@ -16,10 +16,12 @@ import java.util.function.Supplier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys;
import ch.ethz.seb.sebserver.gui.service.page.PageMessageException;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionPublishEvent;
@ -31,7 +33,6 @@ public final class Action implements Runnable {
private static final Logger log = LoggerFactory.getLogger(Action.class);
public final RestService restService;
public final PageContext pageContext;
public final ActionDefinition definition;
Supplier<LocTextKey> confirm;
LocTextKey successMessage;
@ -39,7 +40,8 @@ public final class Action implements Runnable {
Supplier<Set<String>> selectionSupplier;
private Function<Action, Result<?>> exec;
private PageContext pageContext;
private Function<Action, Action> exec = Function.identity();
public Action(
final ActionDefinition definition,
@ -66,13 +68,15 @@ public final class Action implements Runnable {
private void exec() {
try {
this.exec.apply(this)
.map(value -> {
this.pageContext.publishPageEvent(
new ActionEvent(this.definition, value));
return value;
})
.getOrThrow();
this.pageContext.publishPageEvent(new ActionEvent(this.exec.apply(this), false));
// this.exec.apply(this)
// .map(action -> {
// this.pageContext.publishPageEvent(
// new ActionEvent(action, false));
// return action;
// })
// .getOrThrow();
} catch (final PageMessageException pme) {
Action.this.pageContext.publishPageMessage(pme);
@ -96,7 +100,7 @@ public final class Action implements Runnable {
return this.selectionSupplier;
}
public Action withExec(final Function<Action, Result<?>> exec) {
public Action withExec(final Function<Action, Action> exec) {
this.exec = exec;
return this;
}
@ -126,6 +130,38 @@ public final class Action implements Runnable {
return this;
}
public Action withEntity(final EntityKey entityKey) {
this.pageContext = this.pageContext.withEntityKey(entityKey);
return this;
}
public Action withEntity(final Long modelId, final EntityType entityType) {
if (modelId != null) {
return withEntity(String.valueOf(modelId), entityType);
}
return this;
}
public Action withEntity(final String modelId, final EntityType entityType) {
if (modelId == null || entityType == null) {
return this;
}
this.pageContext = this.pageContext.withEntityKey(new EntityKey(modelId, entityType));
return this;
}
public Action withParentEntity(final EntityKey entityKey) {
this.pageContext = this.pageContext.withParentEntityKey(entityKey);
return this;
}
public Action withAttribute(final String name, final String value) {
this.pageContext = this.pageContext.withAttribute(name, value);
return this;
}
public PageContext publish() {
this.pageContext.publishPageEvent(new ActionPublishEvent(this));
return this.pageContext;
@ -139,4 +175,16 @@ public final class Action implements Runnable {
return this.pageContext;
}
public EntityKey getEntityKey() {
return this.pageContext.getEntityKey();
}
public PageContext pageContext() {
return this.pageContext;
}
public Action readonly(final boolean b) {
return this.withAttribute(AttributeKeys.READ_ONLY, "false");
}
}

View file

@ -1,26 +0,0 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui.service.page.activity;
import org.eclipse.swt.widgets.Tree;
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent;
public interface ActivityActionHandler {
public ActionDefinition handlesAction();
void notifyAction(
final ActionEvent event,
final Tree navigation,
final PageContext composerCtx);
}

View file

@ -1,83 +0,0 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui.service.page.activity;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import org.eclipse.swt.widgets.TreeItem;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gui.content.activity.Activity;
import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys;
public class ActivitySelection {
public static final Consumer<TreeItem> EMPTY_FUNCTION = ti -> {
};
public static final Consumer<TreeItem> COLLAPSE_NONE_EMPTY = ti -> {
ti.removeAll();
ti.setItemCount(1);
};
public final Activity activity;
final Map<String, String> attributes;
Consumer<TreeItem> expandFunction = EMPTY_FUNCTION;
public ActivitySelection(final Activity activity) {
this.activity = activity;
this.attributes = new HashMap<>();
}
public ActivitySelection withEntity(final EntityKey entityKey) {
if (entityKey != null) {
this.attributes.put(AttributeKeys.ENTITY_ID, entityKey.modelId);
this.attributes.put(AttributeKeys.ENTITY_TYPE, entityKey.entityType.name());
}
return this;
}
public ActivitySelection withParentEntity(final EntityKey parentEntityKey) {
if (parentEntityKey != null) {
this.attributes.put(AttributeKeys.PARENT_ENTITY_ID, parentEntityKey.modelId);
this.attributes.put(AttributeKeys.PARENT_ENTITY_TYPE, parentEntityKey.entityType.name());
}
return this;
}
public ActivitySelection withAttribute(final String name, final String value) {
this.attributes.put(name, value);
return this;
}
public Map<String, String> getAttributes() {
return Collections.unmodifiableMap(this.attributes);
}
public ActivitySelection withExpandFunction(final Consumer<TreeItem> expandFunction) {
if (expandFunction == null) {
this.expandFunction = EMPTY_FUNCTION;
}
this.expandFunction = expandFunction;
return this;
}
public void processExpand(final TreeItem item) {
this.expandFunction.accept(item);
}
public String getEntityId() {
return this.attributes.get(AttributeKeys.ENTITY_ID);
}
}

View file

@ -8,21 +8,19 @@
package ch.ethz.seb.sebserver.gui.service.page.event;
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
import ch.ethz.seb.sebserver.gui.service.page.action.Action;
/** This Event is used to propagate a user-action to the GUI system.
* Potentially every component can listen to an Event and react on the user-action */
public final class ActionEvent implements PageEvent {
public final ActionDefinition actionDefinition;
public final Object source;
public final Action action;
public final boolean activity;
public ActionEvent(
final ActionDefinition actionDefinition,
final Object source) {
this.actionDefinition = actionDefinition;
this.source = source;
public ActionEvent(final Action action, final boolean activity) {
super();
this.action = action;
this.activity = activity;
}
}

View file

@ -8,13 +8,6 @@
package ch.ethz.seb.sebserver.gui.service.page.event;
import java.util.function.Consumer;
import java.util.function.Predicate;
import org.eclipse.swt.widgets.Widget;
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
public interface ActionEventListener extends PageEventListener<ActionEvent> {
@Override
@ -32,42 +25,42 @@ public interface ActionEventListener extends PageEventListener<ActionEvent> {
// }
//
static ActionEventListener of(
final Predicate<ActionEvent> predicate,
final Consumer<ActionEvent> eventConsumer) {
// static ActionEventListener of(
// final Predicate<ActionEvent> predicate,
// final Consumer<ActionEvent> eventConsumer) {
//
// return new ActionEventListener() {
// @Override
// public void notify(final ActionEvent event) {
// if (predicate.test(event)) {
// eventConsumer.accept(event);
// }
// }
// };
// }
return new ActionEventListener() {
@Override
public void notify(final ActionEvent event) {
if (predicate.test(event)) {
eventConsumer.accept(event);
}
}
};
}
static ActionEventListener of(
final ActionDefinition actionDefinition,
final Consumer<ActionEvent> eventConsumer) {
return new ActionEventListener() {
@Override
public void notify(final ActionEvent event) {
if (event.actionDefinition == actionDefinition) {
eventConsumer.accept(event);
}
}
};
}
static void injectListener(
final Widget widget,
final ActionDefinition actionDefinition,
final Consumer<ActionEvent> eventConsumer) {
widget.setData(
PageEventListener.LISTENER_ATTRIBUTE_KEY,
of(actionDefinition, eventConsumer));
}
// static ActionEventListener of(
// final ActionDefinition actionDefinition,
// final Consumer<ActionEvent> eventConsumer) {
//
// return new ActionEventListener() {
// @Override
// public void notify(final ActionEvent event) {
// if (event.actionDefinition == actionDefinition) {
// eventConsumer.accept(event);
// }
// }
// };
// }
//
// static void injectListener(
// final Widget widget,
// final ActionDefinition actionDefinition,
// final Consumer<ActionEvent> eventConsumer) {
//
// widget.setData(
// PageEventListener.LISTENER_ATTRIBUTE_KEY,
// of(actionDefinition, eventConsumer));
// }
}

View file

@ -1,21 +0,0 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui.service.page.event;
import ch.ethz.seb.sebserver.gui.service.page.activity.ActivitySelection;
public class ActivitySelectionEvent implements PageEvent {
public final ActivitySelection selection;
public ActivitySelectionEvent(final ActivitySelection selection) {
this.selection = selection;
}
}

View file

@ -1,18 +0,0 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui.service.page.event;
public interface ActivitySelectionListener extends PageEventListener<ActivitySelectionEvent> {
@Override
default boolean match(final Class<? extends PageEvent> eventType) {
return eventType == ActivitySelectionEvent.class;
}
}

View file

@ -15,14 +15,13 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.ethz.seb.sebserver.gui.content.MainPage;
import ch.ethz.seb.sebserver.gui.content.activity.Activity;
import ch.ethz.seb.sebserver.gui.service.page.activity.ActivitySelection;
import ch.ethz.seb.sebserver.gui.service.page.action.Action;
public final class MainPageState {
private static final Logger log = LoggerFactory.getLogger(MainPageState.class);
public ActivitySelection activitySelection = Activity.NONE.createSelection();
public Action action = null;
private MainPageState() {
}
@ -49,6 +48,6 @@ public final class MainPageState {
public static void clear() {
final MainPageState mainPageState = get();
mainPageState.activitySelection = Activity.NONE.createSelection();
mainPageState.action = null;
}
}

View file

@ -36,7 +36,6 @@ import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageDefinition;
import ch.ethz.seb.sebserver.gui.service.page.PageMessageException;
import ch.ethz.seb.sebserver.gui.service.page.action.Action;
import ch.ethz.seb.sebserver.gui.service.page.activity.ActivitySelection;
import ch.ethz.seb.sebserver.gui.service.page.event.PageEvent;
import ch.ethz.seb.sebserver.gui.service.page.event.PageEventListener;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
@ -95,6 +94,11 @@ public class PageContextImpl implements PageContext {
return this.parent;
}
@Override
public PageContext copy() {
return copyOf(this.parent);
}
@Override
public PageContext copyOf(final Composite parent) {
return new PageContextImpl(
@ -134,26 +138,26 @@ public class PageContextImpl implements PageContext {
attrs);
}
@Override
public PageContext withSelection(final ActivitySelection selection, final boolean clearAttributes) {
if (selection == null) {
return this;
}
final Map<String, String> attrs = new HashMap<>();
if (!clearAttributes) {
attrs.putAll(this.attributes);
}
attrs.putAll(selection.getAttributes());
return new PageContextImpl(
this.restService,
this.i18nSupport,
this.composerService,
this.root,
this.parent,
attrs);
}
// @Override
// public PageContext withSelection(final ActivitySelection selection, final boolean clearAttributes) {
// if (selection == null) {
// return this;
// }
//
// final Map<String, String> attrs = new HashMap<>();
// if (!clearAttributes) {
// attrs.putAll(this.attributes);
// }
// attrs.putAll(selection.getAttributes());
//
// return new PageContextImpl(
// this.restService,
// this.i18nSupport,
// this.composerService,
// this.root,
// this.parent,
// attrs);
// }
@Override
public String getAttribute(final String name) {

View file

@ -206,7 +206,6 @@ public abstract class RestCall<T> {
} else {
this.body = formBinding.getFormUrlEncoded();
return this;
//return withHeaders(formBinding.getFormAsQueryAttributes());
}
}

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.type.TypeReference;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class ChangePassword extends RestCall<UserInfo> {
protected ChangePassword() {
super(
new TypeReference<UserInfo>() {
},
HttpMethod.PUT,
MediaType.APPLICATION_JSON_UTF8,
API.USER_ACCOUNT_ENDPOINT + API.PASSWORD_PATH_SEGMENT);
}
}

View file

@ -14,20 +14,51 @@ import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
import ch.ethz.seb.sebserver.gbl.util.Result;
/** Defines functionality for the SEB Server webservice authorization context used to
* manage a user session on GUI service. */
public interface SEBServerAuthorizationContext {
/** Indicates if this authorization context is still valid
*
* @return true if the SEBServerAuthorizationContext is valid. False of not. */
boolean isValid();
/** Indicated whether a user is logged in within this authorization context or not.
*
* @return whether a user is logged in within this authorization context or not */
boolean isLoggedIn();
/** Requests a login with username and password on SEB Server webservice.
* This uses OAuth 2 and Springs OAuth2RestTemplate to exchange user/client credentials
* with an access and refresh token.
*
* @param username the username for login
* @param password the password for login
* @return */
boolean login(String username, String password);
/** Requests a logout on SEB Server webservice if a user is currently logged in
* This uses OAuth 2 and Springs OAuth2RestTemplate to make a revoke token request for the
* currently logged in user and also invalidates this SEBServerAuthorizationContext
*
* @return true if logout was successful */
boolean logout();
/** Gets a Result of the UserInfo data of currently logged in user or of an error if no user is logged in
* or there was an unexpected error while trying to get the user information.
*
* @return Result of logged in user data or of an error on fail */
Result<UserInfo> getLoggedInUser();
/** Returns true if a current logged in user has the specified role.
*
* @param role the UserRole to check
* @return true if a current logged in user has the specified role */
public boolean hasRole(UserRole role);
/** Get the underling RestTemplate to connect and communicate with the SEB Server webservice.
*
* @return the underling RestTemplate to connect and communicate with the SEB Server webservice */
RestTemplate getRestTemplate();
}

View file

@ -176,6 +176,15 @@ public class EntityTable<ROW extends Entity> extends Composite {
this.sortOrder);
}
public String getSingleSelection() {
final TableItem[] selection = this.table.getSelection();
if (selection == null || selection.length == 0) {
return null;
}
return getRowDataId(selection[0]);
}
public Set<String> getSelection() {
final TableItem[] selection = this.table.getSelection();
if (selection == null) {

View file

@ -23,6 +23,7 @@ import org.eclipse.swt.widgets.Label;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.util.Tuple;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
public class MultiSelection extends Composite implements Selection {
@ -37,6 +38,8 @@ public class MultiSelection extends Composite implements Selection {
final GridLayout gridLayout = new GridLayout(1, true);
gridLayout.verticalSpacing = 1;
gridLayout.marginLeft = 0;
gridLayout.marginHeight = 0;
gridLayout.marginWidth = 0;
setLayout(gridLayout);
}
@ -44,25 +47,29 @@ public class MultiSelection extends Composite implements Selection {
public void applyNewMapping(final List<Tuple<String>> mapping) {
final String selectionValue = getSelectionValue();
this.selected.clear();
this.labels.clear();
for (final Tuple<String> tuple : mapping) {
final Label label = new Label(this, SWT.NONE);
final GridData gridData = new GridData(SWT.FILL, SWT.CENTER, true, true);
label.setLayoutData(gridData);
label.setData(OPTION_VALUE, tuple._1);
label.setData(RWT.CUSTOM_VARIANT, "selection");
label.setText(" " + tuple._2);
label.setData(RWT.CUSTOM_VARIANT, CustomVariant.SELECTION.key);
label.setText(tuple._2);
label.addListener(SWT.MouseDown, event -> {
final Label l = (Label) event.widget;
if (this.selected.contains(l)) {
l.setData(RWT.CUSTOM_VARIANT, "selection");
l.setData(RWT.CUSTOM_VARIANT, CustomVariant.SELECTION.key);
this.selected.remove(l);
} else {
l.setData(RWT.CUSTOM_VARIANT, "selected");
l.setData(RWT.CUSTOM_VARIANT, CustomVariant.SELECTED.key);
this.selected.add(l);
}
});
this.labels.add(label);
}
if (StringUtils.isNoneBlank(selectionValue)) {
select(selectionValue);
}
select(selectionValue);
}
public void selectOne(final String key) {
@ -70,7 +77,7 @@ public class MultiSelection extends Composite implements Selection {
.filter(label -> key.equals(label.getData(OPTION_VALUE)))
.findFirst()
.ifPresent(label -> {
label.setData(RWT.CUSTOM_VARIANT, "selected");
label.setData(RWT.CUSTOM_VARIANT, CustomVariant.SELECTED.key);
this.selected.add(label);
});
}
@ -80,14 +87,14 @@ public class MultiSelection extends Composite implements Selection {
.filter(label -> key.equals(label.getData(OPTION_VALUE)))
.findFirst()
.ifPresent(label -> {
label.setData(RWT.CUSTOM_VARIANT, "selection");
label.setData(RWT.CUSTOM_VARIANT, CustomVariant.SELECTION.key);
this.selected.remove(label);
});
}
public void deselectAll() {
for (final Label label : this.selected) {
label.setData(RWT.CUSTOM_VARIANT, "selection");
label.setData(RWT.CUSTOM_VARIANT, CustomVariant.SELECTION.key);
}
this.selected.clear();
}
@ -96,10 +103,10 @@ public class MultiSelection extends Composite implements Selection {
public void select(final String keys) {
this.selected.clear();
if (StringUtils.isNotBlank(keys)) {
final List<String> split = Arrays.asList(keys.split(keys, Constants.LIST_SEPARATOR_CHAR));
final List<String> split = Arrays.asList(StringUtils.split(keys, Constants.LIST_SEPARATOR));
for (final Label label : this.labels) {
if (split.contains(label.getData(OPTION_VALUE))) {
label.setData(RWT.CUSTOM_VARIANT, "selected");
label.setData(RWT.CUSTOM_VARIANT, CustomVariant.SELECTED.key);
this.selected.add(label);
}
}

View file

@ -49,7 +49,6 @@ import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionEventListener;
import ch.ethz.seb.sebserver.gui.service.push.ServerPushService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
import ch.ethz.seb.sebserver.gui.table.TableBuilder;
@ -105,6 +104,10 @@ public class WidgetFactory {
IMAGE_BUTTON("imageButton"),
TEXT_ACTION("action"),
SELECTION("selection"),
SELECTED("selected"),
SELECTION_READONLY("selectionReadonly"),
FOOTER("footer"),
;
@ -158,10 +161,10 @@ public class WidgetFactory {
final Composite defaultPageLayout = defaultPageLayout(parent);
final Label labelLocalizedTitle = labelLocalizedTitle(defaultPageLayout, title);
labelLocalizedTitle.setLayoutData(new GridData(SWT.TOP, SWT.LEFT, true, false));
ActionEventListener.injectListener(
labelLocalizedTitle,
actionDefinition,
eventFunction.apply(labelLocalizedTitle));
// ActionEventListener.injectListener(
// labelLocalizedTitle,
// actionDefinition,
// eventFunction.apply(labelLocalizedTitle));
return defaultPageLayout;
}

View file

@ -14,6 +14,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.stereotype.Service;
@ -135,7 +136,10 @@ public class BulkActionService {
final List<BulkActionSupportDAO<?>> dependantSupporterInHierarchicalOrder =
getDependantSupporterInHierarchicalOrder(action);
Collections.reverse(dependantSupporterInHierarchicalOrder);
return dependantSupporterInHierarchicalOrder;
return dependantSupporterInHierarchicalOrder
.stream()
.filter(v -> v != null)
.collect(Collectors.toList());
}
default:
return getDependantSupporterInHierarchicalOrder(action);

View file

@ -39,4 +39,10 @@ public interface ActivatableEntityDAO<T extends Entity, M extends ModelIdAware>
* @return The Collection of Results refer to the EntityKey instance or refer to an error if happened */
Result<Collection<EntityKey>> setActive(Set<EntityKey> all, boolean active);
/** Indicates if the activatable entity with specified model identifier is currently active
*
* @param modelId the model identifier of the entity
* @return true if the entity is active, false otherwise */
boolean isActive(String modelId);
}

View file

@ -26,6 +26,7 @@ public interface UserActivityLogDAO extends
CREATE,
IMPORT,
MODIFY,
PASSWORD_CHANGE,
DEACTIVATE,
ACTIVATE,
DELETE

View file

@ -43,6 +43,7 @@ public interface UserDAO extends ActivatableEntityDAO<UserInfo, UserMod>, BulkAc
Result<UserInfo> changePassword(String modelId, String newPassword);
/** Use this to get the SEBServerUser principal for a given username.
* This should be used for internal authorization and consider only active user accounts
*
* @param username The username of the user to get SEBServerUser from
* @return a Result of SEBServerUser for specified username. Or an exception result on error case */

View file

@ -235,6 +235,21 @@ public class ExamDAOImpl implements ExamDAO {
});
}
@Override
@Transactional(readOnly = true)
public boolean isActive(final String modelId) {
if (StringUtils.isBlank(modelId)) {
return false;
}
return this.examRecordMapper.countByExample()
.where(ExamRecordDynamicSqlSupport.id, isEqualTo(Long.valueOf(modelId)))
.and(ExamRecordDynamicSqlSupport.active, isEqualTo(BooleanUtils.toInteger(true)))
.build()
.execute()
.longValue() > 0;
}
@Override
@Transactional
public Result<Collection<EntityKey>> delete(final Set<EntityKey> all) {

View file

@ -204,6 +204,21 @@ public class InstitutionDAOImpl implements InstitutionDAO {
});
}
@Override
@Transactional(readOnly = true)
public boolean isActive(final String modelId) {
if (StringUtils.isBlank(modelId)) {
return false;
}
return this.institutionRecordMapper.countByExample()
.where(InstitutionRecordDynamicSqlSupport.id, isEqualTo(Long.valueOf(modelId)))
.and(InstitutionRecordDynamicSqlSupport.active, isEqualTo(BooleanUtils.toInteger(true)))
.build()
.execute()
.longValue() > 0;
}
@Override
@Transactional
public Result<Collection<EntityKey>> delete(final Set<EntityKey> all) {
@ -266,5 +281,4 @@ public class InstitutionDAOImpl implements InstitutionDAO {
record.getLogoImage(),
BooleanUtils.toBooleanObject(record.getActive())));
}
}

View file

@ -207,6 +207,21 @@ public class LmsSetupDAOImpl implements LmsSetupDAO {
});
}
@Override
@Transactional(readOnly = true)
public boolean isActive(final String modelId) {
if (StringUtils.isBlank(modelId)) {
return false;
}
return this.lmsSetupRecordMapper.countByExample()
.where(LmsSetupRecordDynamicSqlSupport.id, isEqualTo(Long.valueOf(modelId)))
.and(LmsSetupRecordDynamicSqlSupport.active, isEqualTo(BooleanUtils.toInteger(true)))
.build()
.execute()
.longValue() > 0;
}
@Override
@Transactional
public Result<Collection<EntityKey>> delete(final Set<EntityKey> all) {

View file

@ -22,6 +22,7 @@ import java.util.stream.Collectors;
import javax.validation.constraints.NotNull;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTimeZone;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -32,6 +33,7 @@ import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import ch.ethz.seb.sebserver.WebSecurityConfig;
import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType;
import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException;
import ch.ethz.seb.sebserver.gbl.api.APIMessage.ErrorMessage;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
@ -276,6 +278,21 @@ public class UserDaoImpl implements UserDAO {
});
}
@Override
@Transactional(readOnly = true)
public boolean isActive(final String modelId) {
if (StringUtils.isBlank(modelId)) {
return false;
}
return this.userRecordMapper.countByExample()
.where(UserRecordDynamicSqlSupport.id, isEqualTo(Long.valueOf(modelId)))
.and(UserRecordDynamicSqlSupport.active, isEqualTo(BooleanUtils.toInteger(true)))
.build()
.execute()
.longValue() > 0;
}
@Override
@Transactional
public Result<Collection<EntityKey>> delete(final Set<EntityKey> all) {
@ -307,7 +324,9 @@ public class UserDaoImpl implements UserDAO {
@Transactional(readOnly = true)
public Set<EntityKey> getDependencies(final BulkAction bulkAction) {
// all of institution
if (bulkAction.sourceType == EntityType.INSTITUTION) {
if (bulkAction.sourceType == EntityType.INSTITUTION &&
(bulkAction.type == BulkActionType.DEACTIVATE || bulkAction.type == BulkActionType.HARD_DELETE)) {
return getDependencies(bulkAction, this::allIdsOfInstitution);
}
@ -356,13 +375,13 @@ public class UserDaoImpl implements UserDAO {
private Result<Collection<EntityKey>> allIdsOfInstitution(final EntityKey institutionKey) {
return Result.tryCatch(() -> {
return this.userRecordMapper.selectIdsByExample()
return this.userRecordMapper.selectByExample()
.where(UserRecordDynamicSqlSupport.institutionId,
isEqualTo(Long.valueOf(institutionKey.modelId)))
.build()
.execute()
.stream()
.map(id -> new EntityKey(id, EntityType.USER))
.map(rec -> new EntityKey(rec.getUuid(), EntityType.USER))
.collect(Collectors.toList());
});
}
@ -390,7 +409,7 @@ public class UserDaoImpl implements UserDAO {
.selectByExample()
.where(UserRecordDynamicSqlSupport.username, isEqualTo(username))
.and(UserRecordDynamicSqlSupport.active,
isEqualToWhenPresent(BooleanUtils.toInteger(true)))
isEqualTo(BooleanUtils.toInteger(true)))
.build()
.execute());
}

View file

@ -8,21 +8,37 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.validation;
import java.util.Collection;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.stereotype.Service;
import org.springframework.validation.DirectFieldBindingResult;
import org.springframework.validation.Validator;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ActivatableEntityDAO;
@Service
@WebServiceProfile
public class BeanValidationService {
private final Validator validator;
private final Map<EntityType, ActivatableEntityDAO<?, ?>> activatableDAOs;
public BeanValidationService(
final Validator validator,
final Collection<ActivatableEntityDAO<?, ?>> activatableDAOs) {
public BeanValidationService(final Validator validator) {
this.validator = validator;
this.activatableDAOs = activatableDAOs
.stream()
.collect(Collectors.toUnmodifiableMap(
dao -> dao.entityType(),
dao -> dao));
}
public <T> Result<T> validateBean(final T bean) {
@ -35,4 +51,13 @@ public class BeanValidationService {
return Result.of(bean);
}
public boolean isActive(final EntityKey entityKey) {
final ActivatableEntityDAO<?, ?> activatableEntityDAO = this.activatableDAOs.get(entityKey.entityType);
if (activatableEntityDAO == null) {
return false;
}
return activatableEntityDAO.isActive(entityKey.modelId);
}
}

View file

@ -28,6 +28,7 @@ public class WebServiceUserDetails implements UserDetailsService {
this.userDAO = userDAO;
}
// TODO do we need an institution id here? otherwise username must be unique thought all institutions!
@Override
public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {
return this.userDAO.sebServerUserByUsername(username)

View file

@ -15,8 +15,8 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
@ -121,15 +121,16 @@ public abstract class ActivatableEntityController<T extends GrantEntity, M exten
.getOrThrow();
}
private Result<EntityProcessingReport> setActive(final String id, final boolean active) {
private Result<EntityProcessingReport> setActive(final String modelId, final boolean active) {
final EntityType entityType = this.entityDAO.entityType();
final BulkAction bulkAction = new BulkAction(
(active) ? BulkActionType.ACTIVATE : BulkActionType.DEACTIVATE,
entityType,
new EntityKey(id, entityType));
new EntityKey(modelId, entityType));
return this.entityDAO.byModelId(id)
return this.entityDAO.byModelId(modelId)
.flatMap(this.authorization::checkWrite)
.flatMap(this::validForSave)
.flatMap(entity -> this.bulkActionService.createReport(bulkAction));
}

View file

@ -249,6 +249,7 @@ public abstract class EntityController<T extends GrantEntity, M extends GrantEnt
return this.beanValidationService.validateBean(requestModel)
.flatMap(this.entityDAO::createNew)
.flatMap(this::validForSave)
.flatMap(this.userActivityLogDAO::logCreate)
.flatMap(this::notifyCreated)
.getOrThrow();

View file

@ -11,18 +11,23 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api;
import javax.validation.Valid;
import org.mybatis.dynamic.sql.SqlTable;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.validation.FieldError;
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.RestController;
import ch.ethz.seb.sebserver.WebSecurityConfig;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException;
import ch.ethz.seb.sebserver.gbl.api.APIMessage.ErrorMessage;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.user.PasswordChange;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.model.user.UserMod;
@ -31,6 +36,7 @@ import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.UserRecordDynamicSqlSupport;
import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.SEBServerUser;
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO.ActivityType;
@ -45,6 +51,7 @@ public class UserAccountController extends ActivatableEntityController<UserInfo,
private final ApplicationEventPublisher applicationEventPublisher;
private final UserDAO userDAO;
private final PasswordEncoder userPasswordEncoder;
public UserAccountController(
final UserDAO userDAO,
@ -53,7 +60,8 @@ public class UserAccountController extends ActivatableEntityController<UserInfo,
final PaginationService paginationService,
final BulkActionService bulkActionService,
final ApplicationEventPublisher applicationEventPublisher,
final BeanValidationService beanValidationService) {
final BeanValidationService beanValidationService,
@Qualifier(WebSecurityConfig.USER_PASSWORD_ENCODER_BEAN_NAME) final PasswordEncoder userPasswordEncoder) {
super(authorization,
bulkActionService,
@ -63,6 +71,7 @@ public class UserAccountController extends ActivatableEntityController<UserInfo,
beanValidationService);
this.applicationEventPublisher = applicationEventPublisher;
this.userDAO = userDAO;
this.userPasswordEncoder = userPasswordEncoder;
}
@RequestMapping(path = "/me", method = RequestMethod.GET)
@ -88,28 +97,60 @@ public class UserAccountController extends ActivatableEntityController<UserInfo,
return new UserMod(null, postParams);
}
@Override
protected Result<UserInfo> validForSave(final UserInfo userInfo) {
return Result.tryCatch(() -> {
// check of institution of UserInfo is active. Otherwise save is not valid
if (!this.beanValidationService.isActive(new EntityKey(userInfo.institutionId, EntityType.INSTITUTION))) {
throw new IllegalAPIArgumentException(
"User within an inactive institution cannot be created nor modified");
}
return userInfo;
});
}
@RequestMapping(
path = API.MODEL_ID_VAR_PATH_SEGMENT + API.PASSWORD_PATH_SEGMENT,
path = API.PASSWORD_PATH_SEGMENT,
method = RequestMethod.PUT,
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public UserInfo changePassword(
@PathVariable final String modelId,
@Valid @RequestBody final PasswordChange passwordChange) {
if (!passwordChange.newPasswordMatch()) {
throw new APIMessageException(ErrorMessage.PASSWORD_MISMATCH);
}
public UserInfo changePassword(@Valid @RequestBody final PasswordChange passwordChange) {
final String modelId = passwordChange.getModelId();
return this.userDAO.byModelId(modelId)
.flatMap(this.authorization::checkWrite)
.map(ui -> checkPasswordChange(ui, passwordChange))
.flatMap(e -> this.userDAO.changePassword(modelId, passwordChange.getNewPassword()))
.flatMap(this::revokeAccessToken)
.flatMap(e -> this.userActivityLogDAO.log(ActivityType.MODIFY, e))
.flatMap(e -> this.userActivityLogDAO.log(ActivityType.PASSWORD_CHANGE, e))
.getOrThrow();
}
private UserInfo checkPasswordChange(final UserInfo info, final PasswordChange passwordChange) {
final SEBServerUser authUser = this.userDAO.sebServerUserByUsername(info.username)
.getOrThrow();
if (!this.userPasswordEncoder.matches(passwordChange.getOldPassword(), authUser.getPassword())) {
throw new APIMessageException(APIMessage.fieldValidationError(
new FieldError(
"passwordChange",
PasswordChange.ATTR_NAME_OLD_PASSWORD,
"old password is wrong")));
}
if (!passwordChange.newPasswordMatch()) {
throw new APIMessageException(APIMessage.fieldValidationError(
new FieldError(
"passwordChange",
PasswordChange.ATTR_NAME_RETYPED_NEW_PASSWORD,
"old password is wrong")));
}
return info;
}
private Result<UserInfo> revokeAccessToken(final UserInfo userInfo) {
return Result.tryCatch(() -> {
this.applicationEventPublisher.publishEvent(

View file

@ -1,13 +1,31 @@
INSERT INTO institution VALUES
(1, 'ETH Zürich', 'ethz', null, 1)
(1, 'ETH Zürich', 'ethz', null, 1),
(2, 'Institution 2', 'inst2', null, 1),
(3, 'Institution 3', 'inst3', null, 0),
(4, 'Institution 4', 'inst4', null, 0),
(5, 'Institution 5', 'inst5', null, 0),
(6, 'Institution 6', 'inst6', null, 0),
(7, 'Institution 7', 'inst7', null, 0),
(8, 'Institution 8', 'inst8', null, 0)
;
INSERT INTO user VALUES
(1, 1, 'internalDemoAdmin', 'Admin1', 'admin', '$2a$08$c2GKYEYoUVXH1Yb8GXVXVu66ltPvbZgLMcVSXRH.LgZNF/YeaYB8m', 'admin@nomail.nomail', 'en', 'UTC', 1)
(1, 1, 'super-admin', 'super-admin', 'super-admin', '$2a$08$c2GKYEYoUVXH1Yb8GXVXVu66ltPvbZgLMcVSXRH.LgZNF/YeaYB8m', 'super-admin@nomail.nomail', 'en', 'UTC', 1),
(2, 1, 'internalDemoAdmin', 'Admin1', 'admin', '$2a$08$c2GKYEYoUVXH1Yb8GXVXVu66ltPvbZgLMcVSXRH.LgZNF/YeaYB8m', 'admin@nomail.nomail', 'en', 'UTC', 1),
(3, 1, 'inst1Admin', 'Institutional1 Admin', 'inst1Admin', '$2a$08$c2GKYEYoUVXH1Yb8GXVXVu66ltPvbZgLMcVSXRH.LgZNF/YeaYB8m', 'admin@nomail.nomail', 'de', 'UTC', 1),
(4, 2, 'inst2Admin', 'Institutional2 Admin', 'inst2Admin', '$2a$08$c2GKYEYoUVXH1Yb8GXVXVu66ltPvbZgLMcVSXRH.LgZNF/YeaYB8m', 'admin@nomail.nomail', 'en', 'UTC', 1),
(5, 2, 'examAdminInst2', 'Exam Admin 2', 'examAdmin2', '$2a$08$c2GKYEYoUVXH1Yb8GXVXVu66ltPvbZgLMcVSXRH.LgZNF/YeaYB8m', 'admin@nomail.nomail', 'de', 'UTC', 1)
;
INSERT INTO user_role VALUES
(1, 1, 'SEB_SERVER_ADMIN')
(1, 1, 'SEB_SERVER_ADMIN'),
(2, 1, 'INSTITUTIONAL_ADMIN'),
(3, 1, 'EXAM_ADMIN'),
(4, 1, 'EXAM_SUPPORTER'),
(5, 2, 'SEB_SERVER_ADMIN'),
(6, 3, 'INSTITUTIONAL_ADMIN'),
(7, 4, 'INSTITUTIONAL_ADMIN'),
(8, 5, 'EXAM_ADMIN')
;

View file

@ -52,12 +52,6 @@ sebserver.mainpage.minimize.tooltip=Minimize
sebserver.activitiespane.title=Activities
sebserver.actionpane.title=Actions
# Activities
sebserver.activities.institution=Institution
sebserver.activities.useraccount=User Account
################################
# Institution
################################
@ -67,6 +61,7 @@ sebserver.institution.list.column.name=Name
sebserver.institution.list.column.urlSuffix=URL Suffix
sebserver.institution.list.column.active=Active
sebserver.institution.action.list=Institution
sebserver.institution.action.new=New Institution
sebserver.institution.action.list.view=View Selected
sebserver.institution.action.modify=Edit Institution
@ -101,13 +96,16 @@ sebserver.useraccount.list.column.email=Mail
sebserver.useraccount.list.column.language=Language
sebserver.useraccount.list.column.active=Active
sebserver.useraccount.action.list=User Account
sebserver.useraccount.action.new=New User Account
sebserver.useraccount.action.view=View Selected
sebserver.useraccount.action.modify=Edit Selected
sebserver.useraccount.action.list.modify=Edit Selected
sebserver.useraccount.action.modify=Edit
sebserver.useraccount.action.save=Save User Account
sebserver.useraccount.action.activate=Active
sebserver.useraccount.action.deactivate=Active
sebserver.useraccount.action.delete=Delete User Account
sebserver.useraccount.action.change.password=Change Password
sebserver.useraccount.info.pleaseSelect=Please Select an User Account first.
@ -123,5 +121,10 @@ sebserver.useraccount.form.roles=User Roles
sebserver.useraccount.form.password=Password
sebserver.useraccount.form.password.retyped=Retyped Password
sebserver.useraccount.form.pwchange.title=Change Password : {0}
sebserver.useraccount.form.institution.password.old=Old Password
sebserver.useraccount.form.institution.password.new=New Password
sebserver.useraccount.form.institution.password.retyped=Retyped New Password

View file

@ -26,6 +26,10 @@ Label {
text-shadow: none;
}
Label.head {
font: bold 12px Verdana, "Lucida Sans", Arial, Helvetica, sans-serif;
}
Label.action {
font: 12px Verdana, "Lucida Sans", Arial, Helvetica, sans-serif;
color: #82BE1E;
@ -71,6 +75,10 @@ Label.selection {
padding: 4px 6px 3px 6px;
}
Label.selectionReadonly {
padding: 4px 6px 3px 6px;
}
Label:hover.selection {
color: #4a4a4a;
background-color: #b5b5b5;
@ -80,8 +88,8 @@ Label:hover.selection {
Label.selected {
color: #4a4a4a;
background-color: #b5b5b5;
background-image: gradient(linear, left top, left bottom, from(#b5b5b5),to(#b5b5b5));
background-color: #c5c5c5;
background-image: gradient(linear, left top, left bottom, from(#c5c5c5),to(#c5c5c5));
padding: 4px 6px 3px 6px;
}
@ -368,7 +376,7 @@ Tree {
border: none;
color: #1f407a;
margin: 0px 0px 0px 0px;
padding: 0px 0px 0px 0px;
padding: 0px 0px 0px 40px;
}
Tree[BORDER] {
@ -383,13 +391,14 @@ TreeItem {
text-shadow: none;
background-image: none;
margin: 20px 20px 20px 20px;
padding: 20px 20px 20px 20px;
padding: 20px 20px 20px 40px;
}
TreeItem:linesvisible:even {
background-color: #f3f3f4;
}
Tree-RowOverlay {
background-color: transparent;
color: inherit;
@ -402,12 +411,12 @@ Tree-RowOverlay:hover {
}
Tree-RowOverlay:selected {
background-color: #b5b5b5;
background-color: #82be1e;
color: #1F407A;
}
Tree-RowOverlay:selected:unfocused {
background-color: #b5b5b5;
background-color: #82be1e;
color: #1f407a;
}
@ -438,6 +447,7 @@ TreeItem.actions {
margin: 0 0 0 0;
}
Tree-RowOverlay:hover.actions {
background-color: #82be1e;
color: #4a4a4a;
@ -448,6 +458,31 @@ Tree-RowOverlay:selected.actions {
color: #4a4a4a;
}
Tree-Indent {
width: 16px;
background-image: none;
}
Tree-Indent:collapsed {
background-image: none;
}
Tree-Indent:collapsed:hover {
background-image: none;
}
Tree-Indent:expanded {
background-image: none;
}
Tree-Indent:expanded:hover {
background-image: none;
}
Tree-Indent:line {
background-image: none;
}
/* TabFolder default theme */
TabFolder {
@ -665,14 +700,14 @@ Table-RowOverlay:hover {
Table-RowOverlay:selected {
color: #4a4a4a;
background-color: #b5b5b5;
background-image: gradient(linear, left top, left bottom, from(#b5b5b5),to(#b5b5b5));
background-color: #c5c5c5;
background-image: gradient(linear, left top, left bottom, from(#c5c5c5),to(#c5c5c5));
}
Table-RowOverlay:selected:unfocused {
color: #4a4a4a;
background-color: #b5b5b5;
background-image: gradient(linear, left top, left bottom, from(#b5b5b5),to(#b5b5b5));
background-color: #c5c5c5;
background-image: gradient(linear, left top, left bottom, from(#c5c5c5),to(#c5c5c5));
}
Table-RowOverlay:linesvisible:even:hover {
@ -683,13 +718,13 @@ Table-RowOverlay:linesvisible:even:hover {
Table-RowOverlay:linesvisible:even:selected {
color: #4a4a4a;
background-color: #b5b5b5;
background-image: gradient(linear, left top, left bottom, from(#b5b5b5),to(#b5b5b5));
background-color: #c5c5c5;
background-image: gradient(linear, left top, left bottom, from(#c5c5c5),to(#c5c5c5));
}
Table-RowOverlay:linesvisible:even:selected:unfocused {
background-color: #b5b5b5;
background-image: gradient(linear, left top, left bottom, from(#b5b5b5),to(#b5b5b5));
background-color: #c5c5c5;
background-image: gradient(linear, left top, left bottom, from(#c5c5c5),to(#c5c5c5));
color: #4a4a4a;
}

View file

@ -417,9 +417,9 @@ public class InstitutionAPITest extends AdministrationAPIIntegrationTester {
assertNotNull(dependencies);
assertTrue(dependencies.size() == 3);
assertTrue(dependencies.contains(new EntityKey("1", EntityType.USER)));
assertTrue(dependencies.contains(new EntityKey("2", EntityType.USER)));
assertTrue(dependencies.contains(new EntityKey("5", EntityType.USER)));
assertTrue(dependencies.contains(new EntityKey("user1", EntityType.USER)));
assertTrue(dependencies.contains(new EntityKey("user2", EntityType.USER)));
assertTrue(dependencies.contains(new EntityKey("user5", EntityType.USER)));
}
static void assertContainsInstitution(final String name, final Collection<Institution> institutions) {

View file

@ -24,6 +24,7 @@ import java.util.stream.Stream;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.junit.Test;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.test.context.jdbc.Sql;
@ -39,10 +40,10 @@ import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.EntityName;
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
import ch.ethz.seb.sebserver.gbl.model.Page;
import ch.ethz.seb.sebserver.gbl.model.institution.Institution;
import ch.ethz.seb.sebserver.gbl.model.user.PasswordChange;
import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.model.user.UserMod;
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO.ActivityType;
@ -449,8 +450,8 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
.param(Domain.USER.ATTR_USERNAME, "NewTestUser")
.param(Domain.USER.ATTR_LOCALE, Locale.ENGLISH.toLanguageTag())
.param(Domain.USER.ATTR_TIMEZONE, DateTimeZone.UTC.getID())
.param(UserMod.ATTR_NAME_NEW_PASSWORD, "12345678")
.param(UserMod.ATTR_NAME_RETYPED_NEW_PASSWORD, "12345678"))
.param(PasswordChange.ATTR_NAME_NEW_PASSWORD, "12345678")
.param(PasswordChange.ATTR_NAME_RETYPED_NEW_PASSWORD, "12345678"))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(),
new TypeReference<UserInfo>() {
@ -621,6 +622,51 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
assertEquals("[EXAM_ADMIN, EXAM_SUPPORTER]", String.valueOf(modifiedUserResult.roles));
}
@Test
public void testModifyUserOnInactiveInstitutionNotAllowed() throws Exception {
// create new institution with seb-admin that is not active
final Institution institution = new RestAPITestHelper()
.withAccessToken(getSebAdminAccess())
.withPath(API.INSTITUTION_ENDPOINT)
.withMethod(HttpMethod.POST)
.withAttribute("name", "new institution")
.withAttribute("urlSuffix", "new_inst")
.withAttribute("active", "false")
.withExpectedStatus(HttpStatus.OK)
.getAsObject(new TypeReference<Institution>() {
});
assertNotNull(institution);
assertNotNull(institution.id);
assertEquals("new institution", institution.name);
// try to create a user for this institution should not be possible
final Collection<APIMessage> errors = this.jsonMapper.readValue(
this.mockMvc.perform(post(this.endpoint + API.USER_ACCOUNT_ENDPOINT)
.header("Authorization", "Bearer " + getSebAdminAccess())
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.param(Domain.USER.ATTR_INSTITUTION_ID, String.valueOf(institution.id))
.param(Domain.USER.ATTR_NAME, "NewTestUser")
.param(Domain.USER.ATTR_USERNAME, "NewTestUser")
.param(Domain.USER.ATTR_LOCALE, Locale.ENGLISH.toLanguageTag())
.param(Domain.USER.ATTR_TIMEZONE, DateTimeZone.UTC.getID())
.param(PasswordChange.ATTR_NAME_NEW_PASSWORD, "12345678")
.param(PasswordChange.ATTR_NAME_RETYPED_NEW_PASSWORD, "12345678"))
.andExpect(status().isBadRequest())
.andReturn().getResponse().getContentAsString(),
new TypeReference<Collection<APIMessage>>() {
});
assertNotNull(errors);
assertTrue(errors.size() == 1);
assertEquals(
"Illegal API request argument",
errors.iterator().next().systemMessage);
assertEquals(
"User within an inactive institution cannot be created nor modified",
errors.iterator().next().details);
}
// @Test
// public void modifyUserWithPOSTMethod() throws Exception {
// final String token = getSebAdminAccess();
@ -685,8 +731,8 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.param(Domain.USER.ATTR_INSTITUTION_ID, "2")
.param(Domain.USER.ATTR_NAME, "NewTestUser")
.param(UserMod.ATTR_NAME_NEW_PASSWORD, "12345678")
.param(UserMod.ATTR_NAME_RETYPED_NEW_PASSWORD, "12345678"))
.param(PasswordChange.ATTR_NAME_NEW_PASSWORD, "12345678")
.param(PasswordChange.ATTR_NAME_RETYPED_NEW_PASSWORD, "12345678"))
.andExpect(status().isForbidden())
.andReturn().getResponse().getContentAsString();
@ -712,8 +758,8 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.param(Domain.USER.ATTR_INSTITUTION_ID, "2")
.param(Domain.USER.ATTR_NAME, "NewTestUser")
.param(UserMod.ATTR_NAME_NEW_PASSWORD, "12345678")
.param(UserMod.ATTR_NAME_RETYPED_NEW_PASSWORD, "12345678"))
.param(PasswordChange.ATTR_NAME_NEW_PASSWORD, "12345678")
.param(PasswordChange.ATTR_NAME_RETYPED_NEW_PASSWORD, "12345678"))
.andExpect(status().isForbidden())
.andReturn().getResponse().getContentAsString();
@ -748,12 +794,14 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
});
final PasswordChange passwordChange = new PasswordChange(
examAdmin1.uuid,
"admin",
"newPassword",
"newPassword");
final String modifiedUserJson = this.jsonMapper.writeValueAsString(passwordChange);
this.mockMvc.perform(
put(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/" + examAdmin1.uuid + API.PASSWORD_PATH_SEGMENT)
put(this.endpoint + API.USER_ACCOUNT_ENDPOINT + API.PASSWORD_PATH_SEGMENT)
.header("Authorization", "Bearer " + sebAdminToken)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(modifiedUserJson))
@ -796,17 +844,18 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
// must be longer then 8 chars
PasswordChange passwordChange = new PasswordChange(
examAdmin1.uuid,
"admin",
"new",
"new");
String modifiedUserJson = this.jsonMapper.writeValueAsString(passwordChange);
List<APIMessage> messages = this.jsonMapper.readValue(
this.mockMvc.perform(
put(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/" + examAdmin1.uuid
+ API.PASSWORD_PATH_SEGMENT)
.header("Authorization", "Bearer " + sebAdminToken)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(modifiedUserJson))
put(this.endpoint + API.USER_ACCOUNT_ENDPOINT + API.PASSWORD_PATH_SEGMENT)
.header("Authorization", "Bearer " + sebAdminToken)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(modifiedUserJson))
.andExpect(status().isBadRequest())
.andReturn().getResponse().getContentAsString(),
new TypeReference<List<APIMessage>>() {
@ -815,21 +864,22 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
assertNotNull(messages);
assertTrue(1 == messages.size());
assertEquals("1200", messages.get(0).messageCode);
assertEquals("[user, password, size, 8, 255, new]", String.valueOf(messages.get(0).getAttributes()));
assertEquals("[user, newPassword, size, 8, 255, new]", String.valueOf(messages.get(0).getAttributes()));
// wrong password retype
passwordChange = new PasswordChange(
examAdmin1.uuid,
"admin",
"12345678",
"87654321");
modifiedUserJson = this.jsonMapper.writeValueAsString(passwordChange);
messages = this.jsonMapper.readValue(
this.mockMvc.perform(
put(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/" + examAdmin1.uuid
+ API.PASSWORD_PATH_SEGMENT)
.header("Authorization", "Bearer " + sebAdminToken)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(modifiedUserJson))
put(this.endpoint + API.USER_ACCOUNT_ENDPOINT + API.PASSWORD_PATH_SEGMENT)
.header("Authorization", "Bearer " + sebAdminToken)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(modifiedUserJson))
.andExpect(status().isBadRequest())
.andReturn().getResponse().getContentAsString(),
new TypeReference<List<APIMessage>>() {
@ -837,7 +887,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
assertNotNull(messages);
assertTrue(1 == messages.size());
assertEquals("1300", messages.get(0).messageCode);
assertEquals("1200", messages.get(0).messageCode);
}
@Test