register user, status filter, activity in lists

This commit is contained in:
anhefti 2020-02-11 16:16:27 +01:00
parent fa3b327180
commit c9ebeacf1e
26 changed files with 252 additions and 193 deletions

View file

@ -33,7 +33,6 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.Activatable;
import ch.ethz.seb.sebserver.gbl.model.Domain.USER; import ch.ethz.seb.sebserver.gbl.model.Domain.USER;
import ch.ethz.seb.sebserver.gbl.model.Domain.USER_ROLE; 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.EntityKey;
@ -46,7 +45,7 @@ import ch.ethz.seb.sebserver.gbl.util.Utils;
* *
* This domain model is immutable and thread-save */ * This domain model is immutable and thread-save */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class UserInfo implements UserAccount, Activatable, Serializable { public final class UserInfo implements UserAccount, Serializable {
private static final long serialVersionUID = 2526446136264377808L; private static final long serialVersionUID = 2526446136264377808L;

View file

@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.gbl.model.user;
/** All activity types */ /** All activity types */
public enum UserLogActivityType { public enum UserLogActivityType {
REGISTER,
CREATE, CREATE,
IMPORT, IMPORT,
EXPORT, EXPORT,

View file

@ -43,7 +43,7 @@ public final class UserMod implements UserAccount {
public final String uuid; public final String uuid;
/** The foreign key identifier to the institution where the User belongs to */ /** The foreign key identifier to the institution where the User belongs to */
@NotNull @NotNull(message = "user:institutionId:notNull")
@JsonProperty(USER.ATTR_INSTITUTION_ID) @JsonProperty(USER.ATTR_INSTITUTION_ID)
public final Long institutionId; public final Long institutionId;

View file

@ -425,7 +425,7 @@ public class ExamForm implements TemplateComposer {
this.resourceService::localizedExamConfigStatusName) this.resourceService::localizedExamConfigStatusName)
.widthProportion(1)) .widthProportion(1))
.withDefaultActionIf( .withDefaultActionIf(
() -> editable, () -> modifyGrant,
this::viewExamConfigPageAction) this::viewExamConfigPageAction)
.compose(pageContext.copyOf(content)); .compose(pageContext.copyOf(content));
@ -447,7 +447,7 @@ public class ExamForm implements TemplateComposer {
.newAction(ActionDefinition.EXAM_CONFIGURATION_EXAM_CONFIG_VIEW_PROP) .newAction(ActionDefinition.EXAM_CONFIGURATION_EXAM_CONFIG_VIEW_PROP)
.withParentEntityKey(entityKey) .withParentEntityKey(entityKey)
.withEntityKey(configMapKey) .withEntityKey(configMapKey)
.publishIf(() -> modifyGrant && editable && configurationTable.hasAnyContent()) .publishIf(() -> modifyGrant && configurationTable.hasAnyContent())
.newAction(ActionDefinition.EXAM_CONFIGURATION_DELETE_FROM_LIST) .newAction(ActionDefinition.EXAM_CONFIGURATION_DELETE_FROM_LIST)
.withEntityKey(entityKey) .withEntityKey(entityKey)
@ -514,7 +514,7 @@ public class ExamForm implements TemplateComposer {
.asMarkup() .asMarkup()
.widthProportion(4)) .widthProportion(4))
.withDefaultActionIf( .withDefaultActionIf(
() -> editable, () -> modifyGrant,
() -> actionBuilder () -> actionBuilder
.newAction(ActionDefinition.EXAM_INDICATOR_MODIFY_FROM_LIST) .newAction(ActionDefinition.EXAM_INDICATOR_MODIFY_FROM_LIST)
.withParentEntityKey(entityKey) .withParentEntityKey(entityKey)
@ -526,7 +526,7 @@ public class ExamForm implements TemplateComposer {
.newAction(ActionDefinition.EXAM_INDICATOR_NEW) .newAction(ActionDefinition.EXAM_INDICATOR_NEW)
.withParentEntityKey(entityKey) .withParentEntityKey(entityKey)
.publishIf(() -> modifyGrant && editable) .publishIf(() -> modifyGrant)
.newAction(ActionDefinition.EXAM_INDICATOR_MODIFY_FROM_LIST) .newAction(ActionDefinition.EXAM_INDICATOR_MODIFY_FROM_LIST)
.withParentEntityKey(entityKey) .withParentEntityKey(entityKey)
@ -534,7 +534,7 @@ public class ExamForm implements TemplateComposer {
indicatorTable::getSelection, indicatorTable::getSelection,
PageAction::applySingleSelectionAsEntityKey, PageAction::applySingleSelectionAsEntityKey,
INDICATOR_EMPTY_SELECTION_TEXT_KEY) INDICATOR_EMPTY_SELECTION_TEXT_KEY)
.publishIf(() -> modifyGrant && indicatorTable.hasAnyContent() && editable) .publishIf(() -> modifyGrant && indicatorTable.hasAnyContent())
.newAction(ActionDefinition.EXAM_INDICATOR_DELETE_FROM_LIST) .newAction(ActionDefinition.EXAM_INDICATOR_DELETE_FROM_LIST)
.withEntityKey(entityKey) .withEntityKey(entityKey)
@ -542,7 +542,7 @@ public class ExamForm implements TemplateComposer {
indicatorTable::getSelection, indicatorTable::getSelection,
this::deleteSelectedIndicator, this::deleteSelectedIndicator,
INDICATOR_EMPTY_SELECTION_TEXT_KEY) INDICATOR_EMPTY_SELECTION_TEXT_KEY)
.publishIf(() -> modifyGrant && indicatorTable.hasAnyContent() && editable); .publishIf(() -> modifyGrant && indicatorTable.hasAnyContent());
} }
} }

View file

@ -113,12 +113,10 @@ public class InstitutionList implements TemplateComposer {
Institution::getUrlSuffix) Institution::getUrlSuffix)
.sortable() .sortable()
.withFilter(this.urlSuffixFilter)) .withFilter(this.urlSuffixFilter))
.withColumn(new ColumnDefinition<Institution>( .withColumn(new ColumnDefinition<>(
Domain.INSTITUTION.ATTR_ACTIVE, Domain.INSTITUTION.ATTR_ACTIVE,
ACTIVE_TEXT_KEY, ACTIVE_TEXT_KEY,
entity -> this.pageService this.pageService.getResourceService().<Institution> localizedActivityFunction())
.getResourceService()
.localizedActivityResource().apply(entity.active))
.sortable() .sortable()
.withFilter(this.activityFilter)) .withFilter(this.activityFilter))
.withDefaultAction(pageActionBuilder .withDefaultAction(pageActionBuilder
@ -152,16 +150,7 @@ public class InstitutionList implements TemplateComposer {
.newAction(ActionDefinition.INSTITUTION_TOGGLE_ACTIVITY) .newAction(ActionDefinition.INSTITUTION_TOGGLE_ACTIVITY)
.withExec(this.pageService.activationToggleActionFunction(table, EMPTY_SELECTION_TEXT_KEY)) .withExec(this.pageService.activationToggleActionFunction(table, EMPTY_SELECTION_TEXT_KEY))
.withConfirm(this.pageService.confirmDeactivation(table)) .withConfirm(this.pageService.confirmDeactivation(table))
.publishIf(() -> instGrant.m() && table.hasAnyContent(), false) .publishIf(() -> instGrant.m() && table.hasAnyContent(), false);
// Removed as discussed in SEBSERV-52
// .newAction(ActionDefinition.INSTITUTION_USER_ACCOUNT_NEW)
// .withSelect(
// table::getSelection,
// PageAction::applySingleSelectionAsParentEntityKey,
// EMPTY_SELECTION_TEXT_KEY)
// .publishIf(() -> table.hasAnyContent() && userGrant.w())
;
} }
private final Consumer<Set<Institution>> getSelectionPublisher(final PageContext pageContext) { private final Consumer<Set<Institution>> getSelectionPublisher(final PageContext pageContext) {

View file

@ -141,7 +141,7 @@ public class LmsSetupList implements TemplateComposer {
.withColumn(new ColumnDefinition<>( .withColumn(new ColumnDefinition<>(
Domain.LMS_SETUP.ATTR_ACTIVE, Domain.LMS_SETUP.ATTR_ACTIVE,
ACTIVITY_TEXT_KEY, ACTIVITY_TEXT_KEY,
LmsSetup::getActive) this.pageService.getResourceService().<LmsSetup> localizedActivityFunction())
.withFilter(this.activityFilter) .withFilter(this.activityFilter)
.sortable()) .sortable())
.withDefaultAction(actionBuilder .withDefaultAction(actionBuilder

View file

@ -86,13 +86,13 @@ public class MainPage implements TemplateComposer {
final Composite content = PageService.createManagedVScrolledComposite( final Composite content = PageService.createManagedVScrolledComposite(
mainSash, mainSash,
scrolledComposite -> { scrolledComposite -> {
final Composite reusult = new Composite(scrolledComposite, SWT.NONE); final Composite result = new Composite(scrolledComposite, SWT.NONE);
reusult.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); result.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
final GridLayout contentOuterlayout = new GridLayout(); final GridLayout contentOuterlayout = new GridLayout();
contentOuterlayout.marginHeight = 0; contentOuterlayout.marginHeight = 0;
contentOuterlayout.marginWidth = 0; contentOuterlayout.marginWidth = 0;
reusult.setLayout(contentOuterlayout); result.setLayout(contentOuterlayout);
return reusult; return result;
}, },
false); false);

View file

@ -22,6 +22,7 @@ import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam; import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent; import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
import ch.ethz.seb.sebserver.gbl.model.user.UserRole; import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
@ -145,6 +146,10 @@ public class MonitoringClientConnection implements TemplateComposer {
.withURIVariable(API.PARAM_MODEL_ID, exam.getModelId()) .withURIVariable(API.PARAM_MODEL_ID, exam.getModelId())
.withURIVariable(API.EXAM_API_SEB_CONNECTION_TOKEN, connectionToken); .withURIVariable(API.EXAM_API_SEB_CONNECTION_TOKEN, connectionToken);
final ClientConnectionData connectionData = getConnectionData
.call()
.getOrThrow();
final ClientConnectionDetails clientConnectionDetails = new ClientConnectionDetails( final ClientConnectionDetails clientConnectionDetails = new ClientConnectionDetails(
this.pageService, this.pageService,
pageContext.copyOf(content), pageContext.copyOf(content),
@ -227,7 +232,8 @@ public class MonitoringClientConnection implements TemplateComposer {
return action; return action;
}) })
.noEventPropagation() .noEventPropagation()
.publishIf(() -> currentUser.get().hasRole(UserRole.EXAM_SUPPORTER)); .publishIf(() -> currentUser.get().hasRole(UserRole.EXAM_SUPPORTER) &&
connectionData.clientConnection.status == ConnectionStatus.ACTIVE);
} }

View file

@ -9,6 +9,8 @@
package ch.ethz.seb.sebserver.gui.content; package ch.ethz.seb.sebserver.gui.content;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
@ -29,6 +31,7 @@ import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam; import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
import ch.ethz.seb.sebserver.gbl.model.user.UserRole; import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
@ -64,6 +67,8 @@ public class MonitoringRunningExam implements TemplateComposer {
private static final LocTextKey EMPTY_SELECTION_TEXT_KEY = private static final LocTextKey EMPTY_SELECTION_TEXT_KEY =
new LocTextKey("sebserver.monitoring.exam.connection.emptySelection"); new LocTextKey("sebserver.monitoring.exam.connection.emptySelection");
private static final LocTextKey EMPTY_ACTIVE_SELECTION_TEXT_KEY =
new LocTextKey("sebserver.monitoring.exam.connection.emptySelection.active");
private static final LocTextKey CONFIRM_QUIT_SELECTED = private static final LocTextKey CONFIRM_QUIT_SELECTED =
new LocTextKey("sebserver.monitoring.exam.connection.action.instruction.quit.selected.confirm"); new LocTextKey("sebserver.monitoring.exam.connection.action.instruction.quit.selected.confirm");
private static final LocTextKey CONFIRM_QUIT_ALL = private static final LocTextKey CONFIRM_QUIT_ALL =
@ -178,9 +183,9 @@ public class MonitoringRunningExam implements TemplateComposer {
.withEntityKey(entityKey) .withEntityKey(entityKey)
.withConfirm(() -> CONFIRM_QUIT_SELECTED) .withConfirm(() -> CONFIRM_QUIT_SELECTED)
.withSelect( .withSelect(
clientTable::getSelection, () -> this.selectionForQuitInstruction(clientTable),
action -> this.quitSebClients(action, clientTable, false), action -> this.quitSebClients(action, clientTable, false),
EMPTY_SELECTION_TEXT_KEY) EMPTY_ACTIVE_SELECTION_TEXT_KEY)
.noEventPropagation() .noEventPropagation()
.publishIf(privilege) .publishIf(privilege)
@ -289,6 +294,17 @@ public class MonitoringRunningExam implements TemplateComposer {
}; };
} }
private Set<EntityKey> selectionForQuitInstruction(final ClientConnectionTable clientTable) {
final Set<String> connectionTokens = clientTable.getConnectionTokens(
ClientConnection.getStatusPredicate(ConnectionStatus.ACTIVE),
true);
if (connectionTokens == null || connectionTokens.isEmpty()) {
return Collections.emptySet();
}
return clientTable.getSelection();
}
private PageAction quitSebClients( private PageAction quitSebClients(
final PageAction action, final PageAction action,
final ClientConnectionTable clientTable, final ClientConnectionTable clientTable,

View file

@ -31,16 +31,20 @@ import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.EntityName; import ch.ethz.seb.sebserver.gbl.model.EntityName;
import ch.ethz.seb.sebserver.gbl.model.user.PasswordChange; 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.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.gbl.util.Tuple; import ch.ethz.seb.sebserver.gbl.util.Tuple;
import ch.ethz.seb.sebserver.gui.InstitutionalAuthenticationEntryPoint; import ch.ethz.seb.sebserver.gui.InstitutionalAuthenticationEntryPoint;
import ch.ethz.seb.sebserver.gui.form.FormBuilder; import ch.ethz.seb.sebserver.gui.form.FormBuilder;
import ch.ethz.seb.sebserver.gui.form.FormHandle;
import ch.ethz.seb.sebserver.gui.service.ResourceService; import ch.ethz.seb.sebserver.gui.service.ResourceService;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; 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;
import ch.ethz.seb.sebserver.gui.service.page.PageService; import ch.ethz.seb.sebserver.gui.service.page.PageService;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitutionInfo; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitutionInfo;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.RegisterNewUser;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.WebserviceURIService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.WebserviceURIService;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@ -71,6 +75,14 @@ public class RegisterPage implements TemplateComposer {
new LocTextKey("sebserver.useraccount.form.institution"); new LocTextKey("sebserver.useraccount.form.institution");
static final LocTextKey FORM_LANG_TEXT_KEY = static final LocTextKey FORM_LANG_TEXT_KEY =
new LocTextKey("sebserver.useraccount.form.language"); new LocTextKey("sebserver.useraccount.form.language");
static final LocTextKey ACTION_CREATE =
new LocTextKey("sebserver.login.register.do");
static final LocTextKey ACTION_CANCEL =
new LocTextKey("sebserver.overall.action.cancel");
static final LocTextKey MESSAGE_SUCCESS_TILE =
new LocTextKey("sebserver.page.message");
static final LocTextKey MESSAGE_SUCCESS_TEXT =
new LocTextKey("sebserver.login.register.success");
private final PageService pageService; private final PageService pageService;
private final ResourceService resourceService; private final ResourceService resourceService;
@ -101,6 +113,20 @@ public class RegisterPage implements TemplateComposer {
@Override @Override
public void compose(final PageContext pageContext) { public void compose(final PageContext pageContext) {
final Composite parent = PageService.createManagedVScrolledComposite(
pageContext.getParent(),
scrolledComposite -> {
final Composite result = new Composite(scrolledComposite, SWT.NONE);
result.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
final GridLayout contentOuterlayout = new GridLayout();
contentOuterlayout.marginHeight = 0;
contentOuterlayout.marginWidth = 0;
result.setLayout(contentOuterlayout);
result.setData(RWT.CUSTOM_VARIANT, "register");
return result;
},
false);
final String institutionId = InstitutionalAuthenticationEntryPoint final String institutionId = InstitutionalAuthenticationEntryPoint
.extractInstitutionalEndpoint(); .extractInstitutionalEndpoint();
@ -119,14 +145,11 @@ public class RegisterPage implements TemplateComposer {
.sorted(ResourceService.RESOURCE_COMPARATOR) .sorted(ResourceService.RESOURCE_COMPARATOR)
.collect(Collectors.toList()); .collect(Collectors.toList());
final Composite content = this.widgetFactory.defaultPageLayout( this.widgetFactory.labelLocalizedTitle(parent, TITLE_TEXT_KEY);
pageContext.getParent(),
TITLE_TEXT_KEY);
content.setData(RWT.CUSTOM_VARIANT, WidgetFactory.CustomVariant.LOGIN.key);
// The UserAccount form // The UserAccount form
final FormBuilder formBuilder = this.pageService.formBuilder( final FormHandle<UserInfo> registerForm = this.pageService.formBuilder(
pageContext.copyOf(content)) pageContext.copyOf(parent))
.readonly(false) .readonly(false)
.putStaticValueIf( .putStaticValueIf(
() -> !this.multilingual, () -> !this.multilingual,
@ -169,25 +192,44 @@ public class RegisterPage implements TemplateComposer {
.addField(FormBuilder.text( .addField(FormBuilder.text(
PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD, PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD,
FORM_PASSWORD_CONFIRM_TEXT_KEY) FORM_PASSWORD_CONFIRM_TEXT_KEY)
.asPasswordField()); .asPasswordField())
//formBuilder.formParent.setData(RWT.CUSTOM_VARIANT, WidgetFactory.CustomVariant.LOGIN_BACK.key); .build();
formBuilder.build();
final Composite buttons = new Composite(content, SWT.NONE); final Composite buttons = new Composite(parent, SWT.NONE);
buttons.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); buttons.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
buttons.setLayout(new GridLayout(2, false)); final GridLayout gridLayout = new GridLayout(2, false);
gridLayout.marginWidth = 20;
gridLayout.marginTop = 0;
gridLayout.marginBottom = 20;
buttons.setLayout(gridLayout);
buttons.setData(RWT.CUSTOM_VARIANT, WidgetFactory.CustomVariant.LOGIN_BACK.key); buttons.setData(RWT.CUSTOM_VARIANT, WidgetFactory.CustomVariant.LOGIN_BACK.key);
final Button registerButton = this.widgetFactory.buttonLocalized(buttons, "sebserver.login.register"); final Button registerButton = this.widgetFactory.buttonLocalized(buttons, ACTION_CREATE);
GridData gridData = new GridData(SWT.LEFT, SWT.TOP, false, false); GridData gridData = new GridData(SWT.LEFT, SWT.TOP, false, false);
gridData.verticalIndent = 10; gridData.verticalIndent = 10;
registerButton.setLayoutData(gridData); registerButton.setLayoutData(gridData);
registerButton.addListener(SWT.Selection, event -> { registerButton.addListener(SWT.Selection, event -> {
registerForm.getForm().clearErrors();
final Result<UserInfo> onError = this.pageService
.getRestService()
.getBuilder(RegisterNewUser.class)
.withRestTemplate(this.restTemplate)
.withFormBinding(registerForm.getForm())
.call()
.onError(registerForm::handleError);
if (onError.hasError()) {
return;
}
pageContext.forwardToLoginPage();
pageContext.publishPageMessage(MESSAGE_SUCCESS_TILE, MESSAGE_SUCCESS_TEXT);
}); });
final Button cancelButton = this.widgetFactory.buttonLocalized(buttons, "sebserver.overall.action.cancel"); final Button cancelButton = this.widgetFactory.buttonLocalized(buttons, ACTION_CANCEL);
gridData = new GridData(SWT.LEFT, SWT.TOP, false, false); gridData = new GridData(SWT.LEFT, SWT.TOP, false, false);
gridData.verticalIndent = 10; gridData.verticalIndent = 10;
cancelButton.setLayoutData(gridData); cancelButton.setLayoutData(gridData);

View file

@ -145,7 +145,7 @@ public class SebClientConfigList implements TemplateComposer {
.withColumn(new ColumnDefinition<>( .withColumn(new ColumnDefinition<>(
Domain.SEB_CLIENT_CONFIGURATION.ATTR_ACTIVE, Domain.SEB_CLIENT_CONFIGURATION.ATTR_ACTIVE,
ACTIVE_TEXT_KEY, ACTIVE_TEXT_KEY,
SebClientConfig::getActive) this.pageService.getResourceService().<SebClientConfig> localizedActivityFunction())
.withFilter(this.activityFilter) .withFilter(this.activityFilter)
.sortable()) .sortable())
.withDefaultAction(pageActionBuilder .withDefaultAction(pageActionBuilder

View file

@ -163,12 +163,6 @@ public class SebExamConfigList implements TemplateComposer {
PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY) PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY)
.publishIf(() -> examConfigGrant.im() && configTable.hasAnyContent()) .publishIf(() -> examConfigGrant.im() && configTable.hasAnyContent())
.newAction(ActionDefinition.SEB_EXAM_CONFIG_MODIFY_FROM_LIST)
.withSelect(
configTable.getGrantedSelection(this.currentUser, NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUION),
PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY)
.publishIf(() -> examConfigGrant.im() && configTable.hasAnyContent())
.newAction(ActionDefinition.SEB_EXAM_CONFIG_IMPORT_TO_NEW_CONFIG) .newAction(ActionDefinition.SEB_EXAM_CONFIG_IMPORT_TO_NEW_CONFIG)
.withExec(SebExamConfigImportPopup.importFunction(this.pageService, true)) .withExec(SebExamConfigImportPopup.importFunction(this.pageService, true))
.noEventPropagation() .noEventPropagation()

View file

@ -216,14 +216,14 @@ public class SebExamConfigPropForm implements TemplateComposer {
.withEntityKey(entityKey) .withEntityKey(entityKey)
.publishIf(() -> modifyGrant && isReadonly) .publishIf(() -> modifyGrant && isReadonly)
.newAction(ActionDefinition.SEB_EXAM_CONFIG_VIEW)
.withEntityKey(entityKey)
.publishIf(() -> isReadonly)
.newAction(ActionDefinition.SEB_EXAM_CONFIG_MODIFY) .newAction(ActionDefinition.SEB_EXAM_CONFIG_MODIFY)
.withEntityKey(entityKey) .withEntityKey(entityKey)
.publishIf(() -> modifyGrant && isReadonly && !settingsReadonly) .publishIf(() -> modifyGrant && isReadonly && !settingsReadonly)
.newAction(ActionDefinition.SEB_EXAM_CONFIG_VIEW)
.withEntityKey(entityKey)
.publishIf(() -> modifyGrant && isReadonly && settingsReadonly)
.newAction(ActionDefinition.SEB_EXAM_CONFIG_EXPORT_PLAIN_XML) .newAction(ActionDefinition.SEB_EXAM_CONFIG_EXPORT_PLAIN_XML)
.withEntityKey(entityKey) .withEntityKey(entityKey)
.withExec(action -> { .withExec(action -> {
@ -286,7 +286,7 @@ public class SebExamConfigPropForm implements TemplateComposer {
.withExec(this.pageService.backToCurrentFunction()) .withExec(this.pageService.backToCurrentFunction())
.publishIf(() -> !isReadonly); .publishIf(() -> !isReadonly);
if (isAttachedToExam) { if (isAttachedToExam && isReadonly) {
widgetFactory.labelSeparator(content); widgetFactory.labelSeparator(content);
widgetFactory.labelLocalized( widgetFactory.labelLocalized(

View file

@ -182,7 +182,7 @@ public class UserAccountList implements TemplateComposer {
.withColumn(new ColumnDefinition<>( .withColumn(new ColumnDefinition<>(
Domain.USER.ATTR_ACTIVE, Domain.USER.ATTR_ACTIVE,
ACTIVE_TEXT_KEY, ACTIVE_TEXT_KEY,
UserInfo::getActive) this.pageService.getResourceService().<UserInfo> localizedActivityFunction())
.sortable() .sortable()
.withFilter(this.activityFilter) .withFilter(this.activityFilter)
.widthProportion(1)) .widthProportion(1))

View file

@ -74,12 +74,6 @@ public enum ActionDefinition {
PageStateDefinitionImpl.INSTITUTION_LIST, PageStateDefinitionImpl.INSTITUTION_LIST,
ActionCategory.INSTITUTION_LIST), ActionCategory.INSTITUTION_LIST),
INSTITUTION_USER_ACCOUNT_NEW(
new LocTextKey("sebserver.useraccount.action.new"),
ImageIcon.USER,
PageStateDefinitionImpl.USER_ACCOUNT_EDIT,
ActionCategory.INSTITUTION_LIST),
USER_ACCOUNT_VIEW_LIST( USER_ACCOUNT_VIEW_LIST(
new LocTextKey("sebserver.useraccount.action.list"), new LocTextKey("sebserver.useraccount.action.list"),
PageStateDefinitionImpl.USER_ACCOUNT_LIST), PageStateDefinitionImpl.USER_ACCOUNT_LIST),
@ -272,7 +266,7 @@ public enum ActionDefinition {
PageStateDefinitionImpl.EXAM_VIEW, PageStateDefinitionImpl.EXAM_VIEW,
ActionCategory.EXAM_CONFIG_MAPPING_LIST), ActionCategory.EXAM_CONFIG_MAPPING_LIST),
EXAM_CONFIGURATION_EXAM_CONFIG_VIEW_PROP( EXAM_CONFIGURATION_EXAM_CONFIG_VIEW_PROP(
new LocTextKey("sebserver.examconfig.action.view"), new LocTextKey("sebserver.exam.configuration.action.list.view"),
ImageIcon.SHOW, ImageIcon.SHOW,
PageStateDefinitionImpl.SEB_EXAM_CONFIG_PROP_VIEW, PageStateDefinitionImpl.SEB_EXAM_CONFIG_PROP_VIEW,
ActionCategory.EXAM_CONFIG_MAPPING_LIST), ActionCategory.EXAM_CONFIG_MAPPING_LIST),
@ -388,7 +382,7 @@ public enum ActionDefinition {
PageStateDefinitionImpl.SEB_EXAM_CONFIG_PROP_VIEW, PageStateDefinitionImpl.SEB_EXAM_CONFIG_PROP_VIEW,
ActionCategory.SEB_EXAM_CONFIG_LIST), ActionCategory.SEB_EXAM_CONFIG_LIST),
SEB_EXAM_CONFIG_VIEW_PROP( SEB_EXAM_CONFIG_VIEW_PROP(
new LocTextKey("sebserver.examconfig.action.view"), new LocTextKey("sebserver.examconfig.action.view.properties"),
ImageIcon.SHOW, ImageIcon.SHOW,
PageStateDefinitionImpl.SEB_EXAM_CONFIG_PROP_VIEW, PageStateDefinitionImpl.SEB_EXAM_CONFIG_PROP_VIEW,
ActionCategory.FORM), ActionCategory.FORM),

View file

@ -67,6 +67,9 @@ public class ActivitiesPane implements TemplateComposer {
final UserInfo userInfo = this.currentUser final UserInfo userInfo = this.currentUser
.getOrHandleError(t -> this.pageService.logoutOnError(t, pageContext)); .getOrHandleError(t -> this.pageService.logoutOnError(t, pageContext));
final boolean isSupporterOnly = userInfo.hasRole(UserRole.EXAM_SUPPORTER) &&
!userInfo.hasAnyRole(UserRole.EXAM_ADMIN, UserRole.INSTITUTIONAL_ADMIN, UserRole.SEB_SERVER_ADMIN);
if (this.pageService.getI18nSupport().hasText(TITLE_KEY)) { if (this.pageService.getI18nSupport().hasText(TITLE_KEY)) {
final Label activities = this.widgetFactory.labelLocalized( final Label activities = this.widgetFactory.labelLocalized(
pageContext.getParent(), pageContext.getParent(),
@ -185,7 +188,7 @@ public class ActivitiesPane implements TemplateComposer {
PrivilegeType.READ, PrivilegeType.READ,
EntityType.CONFIGURATION_NODE); EntityType.CONFIGURATION_NODE);
if (clientConfigRead || examConfigRead) { if ((clientConfigRead || examConfigRead) && !isSupporterOnly) {
final TreeItem sebConfigs = this.widgetFactory.treeItemLocalized( final TreeItem sebConfigs = this.widgetFactory.treeItemLocalized(
navigation, navigation,
ActivityDefinition.SEB_CONFIGURATION.displayName); ActivityDefinition.SEB_CONFIGURATION.displayName);
@ -236,8 +239,9 @@ public class ActivitiesPane implements TemplateComposer {
// ---- EXAM ADMINISTRATION ------------------------------------------------------------ // ---- EXAM ADMINISTRATION ------------------------------------------------------------
final boolean lmsRead = this.currentUser.hasInstitutionalPrivilege(PrivilegeType.READ, EntityType.LMS_SETUP); final boolean lmsRead = this.currentUser.hasInstitutionalPrivilege(PrivilegeType.READ, EntityType.LMS_SETUP);
final boolean examRead = this.currentUser.get().hasAnyRole(UserRole.EXAM_SUPPORTER, UserRole.EXAM_ADMIN) || final boolean examRead = userInfo.hasAnyRole(UserRole.EXAM_SUPPORTER, UserRole.EXAM_ADMIN) ||
this.currentUser.hasInstitutionalPrivilege(PrivilegeType.READ, EntityType.EXAM); this.currentUser.hasInstitutionalPrivilege(PrivilegeType.READ, EntityType.EXAM);
final boolean examWrite = this.currentUser.hasInstitutionalPrivilege(PrivilegeType.WRITE, EntityType.EXAM);
// Exam Administration // Exam Administration
final TreeItem examadmin = this.widgetFactory.treeItemLocalized( final TreeItem examadmin = this.widgetFactory.treeItemLocalized(
@ -246,7 +250,7 @@ public class ActivitiesPane implements TemplateComposer {
if (examRead || lmsRead) { if (examRead || lmsRead) {
// LMS Setup // LMS Setup
if (lmsRead) { if (lmsRead && !isSupporterOnly) {
final TreeItem lmsSetup = this.widgetFactory.treeItemLocalized( final TreeItem lmsSetup = this.widgetFactory.treeItemLocalized(
examadmin, examadmin,
ActivityDefinition.LMS_SETUP.displayName); ActivityDefinition.LMS_SETUP.displayName);
@ -257,9 +261,9 @@ public class ActivitiesPane implements TemplateComposer {
.create()); .create());
} }
// Exam (Quiz Discovery)
if (examRead) { if (examRead) {
if (examWrite) {
// Quiz Discovery // Quiz Discovery
final TreeItem quizDiscovery = this.widgetFactory.treeItemLocalized( final TreeItem quizDiscovery = this.widgetFactory.treeItemLocalized(
examadmin, examadmin,
@ -269,6 +273,7 @@ public class ActivitiesPane implements TemplateComposer {
actionBuilder actionBuilder
.newAction(ActionDefinition.QUIZ_DISCOVERY_VIEW_LIST) .newAction(ActionDefinition.QUIZ_DISCOVERY_VIEW_LIST)
.create()); .create());
}
// Exam // Exam
final TreeItem exam = this.widgetFactory.treeItemLocalized( final TreeItem exam = this.widgetFactory.treeItemLocalized(

View file

@ -242,6 +242,12 @@ public final class Form implements FormBinding {
.isPresent(); .isPresent();
} }
public void clearErrors() {
process(
Utils.truePredicate(),
ffa -> ffa.resetError());
}
public void setFieldError(final String fieldName, final String errorMessage) { public void setFieldError(final String fieldName, final String errorMessage) {
final List<FormFieldAccessor> list = this.formFields.get(fieldName); final List<FormFieldAccessor> list = this.formFields.get(fieldName);
if (list != null) { if (list != null) {

View file

@ -29,6 +29,7 @@ import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.API; import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.Activatable;
import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.model.EntityName; import ch.ethz.seb.sebserver.gbl.model.EntityName;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam; import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
@ -414,6 +415,11 @@ public class ResourceService {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
public <T extends Activatable> Function<T, String> localizedActivityFunction() {
final Function<Boolean, String> localizedActivityResource = localizedActivityResource();
return activatable -> localizedActivityResource.apply(activatable.isActive());
}
public Function<Boolean, String> localizedActivityResource() { public Function<Boolean, String> localizedActivityResource() {
return activity -> activity return activity -> activity
? this.i18nSupport.getText(ACTIVE_TEXT_KEY) ? this.i18nSupport.getText(ACTIVE_TEXT_KEY)

View file

@ -185,6 +185,7 @@ public class WidgetFactory {
LOGIN("login"), LOGIN("login"),
LOGIN_BACK("login-back"), LOGIN_BACK("login-back"),
SCROLL("scroll"),
LIST_NAVIGATION("list-nav") LIST_NAVIGATION("list-nav")

View file

@ -123,7 +123,7 @@ public class AuthorizationServiceImpl implements AuthorizationService {
.andForRole(UserRole.EXAM_ADMIN) .andForRole(UserRole.EXAM_ADMIN)
.withInstitutionalPrivilege(PrivilegeType.WRITE) .withInstitutionalPrivilege(PrivilegeType.WRITE)
.andForRole(UserRole.EXAM_SUPPORTER) .andForRole(UserRole.EXAM_SUPPORTER)
.withInstitutionalPrivilege(PrivilegeType.MODIFY) .withInstitutionalPrivilege(PrivilegeType.READ)
.create(); .create();
// grants for configuration // grants for configuration
addPrivilege(EntityType.CONFIGURATION) addPrivilege(EntityType.CONFIGURATION)
@ -134,7 +134,7 @@ public class AuthorizationServiceImpl implements AuthorizationService {
.andForRole(UserRole.EXAM_ADMIN) .andForRole(UserRole.EXAM_ADMIN)
.withInstitutionalPrivilege(PrivilegeType.WRITE) .withInstitutionalPrivilege(PrivilegeType.WRITE)
.andForRole(UserRole.EXAM_SUPPORTER) .andForRole(UserRole.EXAM_SUPPORTER)
.withInstitutionalPrivilege(PrivilegeType.MODIFY) .withInstitutionalPrivilege(PrivilegeType.READ)
.create(); .create();
// grants for configuration value // grants for configuration value
addPrivilege(EntityType.CONFIGURATION_VALUE) addPrivilege(EntityType.CONFIGURATION_VALUE)
@ -145,7 +145,7 @@ public class AuthorizationServiceImpl implements AuthorizationService {
.andForRole(UserRole.EXAM_ADMIN) .andForRole(UserRole.EXAM_ADMIN)
.withInstitutionalPrivilege(PrivilegeType.WRITE) .withInstitutionalPrivilege(PrivilegeType.WRITE)
.andForRole(UserRole.EXAM_SUPPORTER) .andForRole(UserRole.EXAM_SUPPORTER)
.withInstitutionalPrivilege(PrivilegeType.MODIFY) .withInstitutionalPrivilege(PrivilegeType.READ)
.create(); .create();
// grants for configuration attributes // grants for configuration attributes

View file

@ -13,6 +13,7 @@ import java.util.function.Predicate;
import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.model.user.UserAccount;
import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog; import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog;
import ch.ethz.seb.sebserver.gbl.model.user.UserLogActivityType; import ch.ethz.seb.sebserver.gbl.model.user.UserLogActivityType;
import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Result;
@ -26,52 +27,58 @@ public interface UserActivityLogDAO extends
/** Create a user activity log entry for the current user of activity type CREATE /** Create a user activity log entry for the current user of activity type CREATE
* *
* @param entity the Entity * @param entity the Entity
* @return Result of the Entity or referring to an Error id happened */ * @return Result of the Entity or referring to an Error if happened */
public <E extends Entity> Result<E> logCreate(E entity); <E extends Entity> Result<E> logCreate(E entity);
/** Create a user activity log entry for a user registration event
*
* @param account the UserAccount
* @return Result of the UserAccount or referring to an Error if happened */
Result<UserAccount> logRegisterAccount(UserAccount account);
/** Creates a user activity log entry for SEB Exam Configuration save in history action /** Creates a user activity log entry for SEB Exam Configuration save in history action
* *
* @param entity the Entity * @param entity the Entity
* @return Result of the Entity or referring to an Error id happened */ * @return Result of the Entity or referring to an Error if happened */
public <E extends Entity> Result<E> logSaveToHistory(E entity); <E extends Entity> Result<E> logSaveToHistory(E entity);
/** Creates a user activity log entry for SEB Exam Configuration undoy action /** Creates a user activity log entry for SEB Exam Configuration undo action
* *
* @param entity the Entity * @param entity the Entity
* @return Result of the Entity or referring to an Error id happened */ * @return Result of the Entity or referring to an Error if happened */
public <E extends Entity> Result<E> logUndo(E entity); <E extends Entity> Result<E> logUndo(E entity);
/** Create a user activity log entry for the current user of activity type IMPORT /** Create a user activity log entry for the current user of activity type IMPORT
* *
* @param entity the Entity * @param entity the Entity
* @return Result of the Entity or referring to an Error id happened */ * @return Result of the Entity or referring to an Error if happened */
public <E extends Entity> Result<E> logImport(E entity); <E extends Entity> Result<E> logImport(E entity);
/** Create a user activity log entry for the current user of activity type EXPORT /** Create a user activity log entry for the current user of activity type EXPORT
* *
* @param entity the Entity * @param entity the Entity
* @return Result of the Entity or referring to an Error id happened */ * @return Result of the Entity or referring to an Error if happened */
public <E extends Entity> Result<E> logExport(E entity); <E extends Entity> Result<E> logExport(E entity);
/** Create a user activity log entry for the current user of activity type MODIFY /** Create a user activity log entry for the current user of activity type MODIFY
* *
* @param entity the Entity * @param entity the Entity
* @return Result of the Entity or referring to an Error id happened */ * @return Result of the Entity or referring to an Error if happened */
public <E extends Entity> Result<E> logModify(E entity); <E extends Entity> Result<E> logModify(E entity);
/** Creates a user activity log entry for the current user. /** Creates a user activity log entry for the current user.
* *
* @param activityType the activity type * @param activityType the activity type
* @param entity the Entity * @param entity the Entity
* @param message an optional message * @param message an optional message
* @return Result of the Entity or referring to an Error id happened */ * @return Result of the Entity or referring to an Error if happened */
<E extends Entity> Result<E> log(UserLogActivityType activityType, E entity, String message); <E extends Entity> Result<E> log(UserLogActivityType activityType, E entity, String message);
/** Creates a user activity log entry for the current user. /** Creates a user activity log entry for the current user.
* *
* @param actionType the action type * @param actionType the action type
* @param entity the Entity * @param entity the Entity
* @return Result of the Entity or referring to an Error id happened */ * @return Result of the Entity or referring to an Error if happened */
<E extends Entity> Result<E> log(UserLogActivityType activityType, E entity); <E extends Entity> Result<E> log(UserLogActivityType activityType, E entity);
/** Creates a user activity log entry for the current user. /** Creates a user activity log entry for the current user.
@ -86,7 +93,7 @@ public interface UserActivityLogDAO extends
* @param activityType the activity type * @param activityType the activity type
* @param entityType the EntityType * @param entityType the EntityType
* @param message the message * @param message the message
* @return Result of the Entity or referring to an Error id happened */ * @return Result of the Entity or referring to an Error if happened */
<T> Result<T> log(UserLogActivityType activityType, EntityType entityType, String entityId, String message, T data); <T> Result<T> log(UserLogActivityType activityType, EntityType entityType, String entityId, String message, T data);
/** Creates a user activity log entry. /** Creates a user activity log entry.
@ -95,7 +102,7 @@ public interface UserActivityLogDAO extends
* @param activityType the activity type * @param activityType the activity type
* @param entity the Entity * @param entity the Entity
* @param message an optional message * @param message an optional message
* @return Result of the Entity or referring to an Error id happened */ * @return Result of the Entity or referring to an Error if happened */
<E extends Entity> Result<E> log( <E extends Entity> Result<E> log(
SEBServerUser user, SEBServerUser user,
UserLogActivityType activityType, UserLogActivityType activityType,
@ -108,7 +115,7 @@ public interface UserActivityLogDAO extends
* @param activityType the activity type * @param activityType the activity type
* @param entityType the entity type * @param entityType the entity type
* @param entityId the entity id (primary key or UUID) * @param entityId the entity id (primary key or UUID)
* @return Result of the Entity or referring to an Error id happened */ * @return Result of the Entity or referring to an Error if happened */
default <E extends Entity> Result<E> log( default <E extends Entity> Result<E> log(
final SEBServerUser user, final SEBServerUser user,
final UserLogActivityType activityType, final UserLogActivityType activityType,

View file

@ -36,6 +36,7 @@ import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.api.JSONMapper; import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.user.UserAccount;
import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog; import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog;
import ch.ethz.seb.sebserver.gbl.model.user.UserLogActivityType; import ch.ethz.seb.sebserver.gbl.model.user.UserLogActivityType;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
@ -87,6 +88,24 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO {
return log(UserLogActivityType.CREATE, entity); return log(UserLogActivityType.CREATE, entity);
} }
@Override
@Transactional
public Result<UserAccount> logRegisterAccount(final UserAccount account) {
return Result.tryCatch(() -> {
this.userLogRecordMapper.insertSelective(new UserActivityLogRecord(
null,
account.getModelId(),
System.currentTimeMillis(),
UserLogActivityType.REGISTER.name(),
EntityType.USER.name(),
account.getModelId(),
toMessage(account)));
return account;
});
}
@Override @Override
@Transactional @Transactional
public <E extends Entity> Result<E> logSaveToHistory(final E entity) { public <E extends Entity> Result<E> logSaveToHistory(final E entity) {

View file

@ -20,7 +20,7 @@ import org.springframework.web.bind.annotation.RestController;
import ch.ethz.seb.sebserver.gbl.api.API; import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.authorization.Privilege; import ch.ethz.seb.sebserver.gbl.api.authorization.Privilege;
import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.EntityName;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.InstitutionDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.InstitutionDAO;
@ -60,11 +60,25 @@ public class InfoController {
.orElse(null); .orElse(null);
} }
@RequestMapping(
path = API.INFO_INST_PATH_SEGMENT,
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public Collection<EntityName> getInstitutionInfo() {
return this.institutionDAO
.all(null, true)
.getOrThrow()
.stream()
.filter(inst -> BooleanUtils.isTrue(inst.active))
.map(inst -> new EntityName(inst.getEntityKey(), inst.name))
.collect(Collectors.toList());
}
@RequestMapping( @RequestMapping(
path = API.INFO_INST_ENDPOINT, path = API.INFO_INST_ENDPOINT,
method = RequestMethod.GET, method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE) produces = MediaType.APPLICATION_JSON_VALUE)
public Collection<EntityKey> getInstitutionInfo(@PathVariable(required = false) final String urlSuffix) { public Collection<EntityName> getInstitutionInfo(@PathVariable final String urlSuffix) {
return this.institutionDAO return this.institutionDAO
.all(null, true) .all(null, true)
.getOrThrow() .getOrThrow()
@ -72,7 +86,7 @@ public class InfoController {
.filter(inst -> BooleanUtils.isTrue(inst.active) && .filter(inst -> BooleanUtils.isTrue(inst.active) &&
(inst.urlSuffix == null || (inst.urlSuffix == null ||
urlSuffix.equals(inst.urlSuffix))) urlSuffix.equals(inst.urlSuffix)))
.map(inst -> inst.getEntityKey()) .map(inst -> new EntityName(inst.getEntityKey(), inst.name))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }

View file

@ -9,16 +9,12 @@
package ch.ethz.seb.sebserver.webservice.weblayer.api; package ch.ethz.seb.sebserver.webservice.weblayer.api;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet;
import java.util.Locale;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTimeZone;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.MultiValueMap;
import org.springframework.validation.FieldError; import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
@ -26,21 +22,20 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import ch.ethz.seb.sebserver.WebSecurityConfig; import ch.ethz.seb.sebserver.WebSecurityConfig;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.API; import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.APIMessage; 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.APIMessageException;
import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
import ch.ethz.seb.sebserver.gbl.model.Domain.USER_ROLE;
import ch.ethz.seb.sebserver.gbl.model.user.PasswordChange; 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.UserInfo;
import ch.ethz.seb.sebserver.gbl.model.user.UserMod; import ch.ethz.seb.sebserver.gbl.model.user.UserMod;
import ch.ethz.seb.sebserver.gbl.model.user.UserRole; import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.webservice.servicelayer.client.ClientCredentialService;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.InstitutionDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.InstitutionDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService;
@WebServiceProfile @WebServiceProfile
@RestController @RestController
@ -50,73 +45,36 @@ public class RegisterUserController {
private final InstitutionDAO institutionDAO; private final InstitutionDAO institutionDAO;
private final UserActivityLogDAO userActivityLogDAO; private final UserActivityLogDAO userActivityLogDAO;
private final UserDAO userDAO; private final UserDAO userDAO;
private final ClientCredentialService clientCredentialService; private final BeanValidationService beanValidationService;
protected RegisterUserController( protected RegisterUserController(
final InstitutionDAO institutionDAO, final InstitutionDAO institutionDAO,
final UserActivityLogDAO userActivityLogDAO, final UserActivityLogDAO userActivityLogDAO,
final UserDAO userDAO, final UserDAO userDAO,
final ClientCredentialService clientCredentialService, final BeanValidationService beanValidationService,
@Qualifier(WebSecurityConfig.USER_PASSWORD_ENCODER_BEAN_NAME) final PasswordEncoder userPasswordEncoder) { @Qualifier(WebSecurityConfig.USER_PASSWORD_ENCODER_BEAN_NAME) final PasswordEncoder userPasswordEncoder) {
this.institutionDAO = institutionDAO; this.institutionDAO = institutionDAO;
this.userActivityLogDAO = userActivityLogDAO; this.userActivityLogDAO = userActivityLogDAO;
this.userDAO = userDAO; this.userDAO = userDAO;
this.clientCredentialService = clientCredentialService; this.beanValidationService = beanValidationService;
} }
@RequestMapping( @RequestMapping(
method = RequestMethod.POST, method = RequestMethod.POST,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE) produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public UserInfo registerNewUser( public UserInfo registerNewUser(@RequestParam final MultiValueMap<String, String> allRequestParams) {
@RequestParam(name = Domain.USER.ATTR_INSTITUTION_ID, required = true) final String institutionId,
@RequestParam(name = Domain.USER.ATTR_NAME, required = true) final String name, final POSTMapper postMap = new POSTMapper(allRequestParams)
@RequestParam(name = Domain.USER.ATTR_SURNAME, required = false) final String surname, .putIfAbsent(USER_ROLE.REFERENCE_NAME, UserRole.EXAM_SUPPORTER.name());
@RequestParam(name = Domain.USER.ATTR_USERNAME, required = true) final String username, final UserMod userMod = new UserMod(null, postMap);
@RequestParam(name = Domain.USER.ATTR_EMAIL, required = false) final String email,
@RequestParam( return this.beanValidationService.validateBean(userMod)
name = Domain.USER.ATTR_LANGUAGE, .map(userAccount -> {
required = false,
defaultValue = Constants.DEFAULT_LANG_CODE) final String lang,
@RequestParam(
name = Domain.USER.ATTR_TIMEZONE,
required = false,
defaultValue = Constants.DEFAULT_TIME_ZONE_CODE) final String timezone,
@RequestParam(name = PasswordChange.ATTR_NAME_NEW_PASSWORD, required = true) final String pwd,
@RequestParam(name = PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD, required = true) final String rpwd) {
final Collection<APIMessage> errors = new ArrayList<>(); final Collection<APIMessage> errors = new ArrayList<>();
if (!userAccount.newPasswordMatch()) {
// check institution info
Long instId = null;
if (StringUtils.isNotBlank(institutionId)) {
try {
instId = Long.parseLong(institutionId);
} catch (final Exception e) {
instId = this.institutionDAO
.all(null, true)
.getOrThrow()
.stream()
.filter(inst -> inst.urlSuffix != null && institutionId.equals(inst.urlSuffix))
.findFirst()
.map(inst -> inst.id)
.orElse(null);
}
}
if (instId == null) {
errors.add(APIMessage.fieldValidationError(
new FieldError(
"user",
Domain.USER.ATTR_INSTITUTION_ID,
"user:institutionId:notNull")));
}
// check password-match
final CharSequence rawPWD = this.clientCredentialService.decrypt(pwd);
final CharSequence rawRPWD = this.clientCredentialService.decrypt(rpwd);
if (!rawPWD.equals(rawRPWD)) {
errors.add(APIMessage.fieldValidationError( errors.add(APIMessage.fieldValidationError(
new FieldError( new FieldError(
"passwordChange", "passwordChange",
@ -128,27 +86,15 @@ public class RegisterUserController {
throw new APIMessageException(errors); throw new APIMessageException(errors);
} }
final UserMod user = new UserMod( return userAccount;
null,
instId,
name,
surname,
username,
rawPWD,
rawRPWD,
email,
Locale.forLanguageTag(lang),
DateTimeZone.forID(timezone),
new HashSet<>(Arrays.asList(UserRole.EXAM_SUPPORTER.name())));
return this.userDAO.createNew(user)
.flatMap(this.userActivityLogDAO::logCreate)
.map(u -> {
Utils.clear(rawPWD);
Utils.clear(rawRPWD);
return u;
}) })
.flatMap(this.userDAO::createNew)
.flatMap(account -> this.userDAO.setActive(account, true))
.flatMap(this.userActivityLogDAO::logRegisterAccount)
.flatMap(account -> this.userDAO.byModelId(account.getModelId()))
.getOrThrow(); .getOrThrow();
} }
} }

View file

@ -35,6 +35,7 @@ sebserver.overall.action.remove=Remove
sebserver.overall.action.select=Please Select sebserver.overall.action.select=Please Select
sebserver.overall.action.toggle-activity=Switch Activity sebserver.overall.action.toggle-activity=Switch Activity
sebserver.overall.types.activityType.REGISTER=Register new Account
sebserver.overall.types.activityType.CREATE=Create New sebserver.overall.types.activityType.CREATE=Create New
sebserver.overall.types.activityType.IMPORT=Import sebserver.overall.types.activityType.IMPORT=Import
sebserver.overall.types.activityType.EXPORT=Export sebserver.overall.types.activityType.EXPORT=Export
@ -111,7 +112,9 @@ sebserver.login.password.change=Information
sebserver.login.password.change.success=The password was successfully changed. Please sign in with your new password sebserver.login.password.change.success=The password was successfully changed. Please sign in with your new password
sebserver.login.register=Register sebserver.login.register=Register
sebserver.login.register.form.title=Register As New User sebserver.login.register.form.title=Create an Account
sebserver.login.register.do=Create Account
sebserver.login.register.success=New account successfully created.<br/> Please log in with your username and password.
################################ ################################
@ -391,6 +394,7 @@ sebserver.exam.configuration.action.noconfig.message=There is currently no SEB e
sebserver.exam.configuration.action.list.new=Add Configuration sebserver.exam.configuration.action.list.new=Add Configuration
sebserver.exam.configuration.action.list.modify=Edit Configuration sebserver.exam.configuration.action.list.modify=Edit Configuration
sebserver.exam.configuration.action.list.view=View Configuration
sebserver.exam.configuration.action.list.delete=Delete Configuration sebserver.exam.configuration.action.list.delete=Delete Configuration
sebserver.exam.configuration.action.save=Save Configuration sebserver.exam.configuration.action.save=Save Configuration
sebserver.exam.configuration.action.export-config=Export Configuration sebserver.exam.configuration.action.export-config=Export Configuration
@ -499,11 +503,11 @@ sebserver.examconfig.list.action.no.modify.privilege=No Access: An Exam Configur
sebserver.examconfig.action.list.new=Add Exam Configuration sebserver.examconfig.action.list.new=Add Exam Configuration
sebserver.examconfig.action.list.view=View Configuration sebserver.examconfig.action.list.view=View Configuration
sebserver.examconfig.action.list.modify=Edit Settings sebserver.examconfig.action.list.modify=Edit Settings
sebserver.examconfig.action.list.modify=View Settings
sebserver.examconfig.action.list.modify.properties=Edit Configuration sebserver.examconfig.action.list.modify.properties=Edit Configuration
sebserver.examconfig.action.view=View Configuration sebserver.examconfig.action.view=View Settings
sebserver.examconfig.action.modify=Edit Settings sebserver.examconfig.action.modify=Edit Settings
sebserver.examconfig.action.modify.properties=Edit Configuration sebserver.examconfig.action.modify.properties=Edit Configuration
sebserver.examconfig.action.view.properties=View Configuration
sebserver.examconfig.action.save=Save sebserver.examconfig.action.save=Save
sebserver.examconfig.action.saveToHistory=Save / Publish sebserver.examconfig.action.saveToHistory=Save / Publish
sebserver.examconfig.action.saveToHistory.success=Successfully saved in history sebserver.examconfig.action.saveToHistory.success=Successfully saved in history
@ -1101,6 +1105,7 @@ sebserver.monitoring.connection.list.column.examname=Exam
sebserver.monitoring.connection.list.column.vdiAddress=IP Address (VDI) sebserver.monitoring.connection.list.column.vdiAddress=IP Address (VDI)
sebserver.monitoring.exam.connection.emptySelection=Please select first a Connection from the list sebserver.monitoring.exam.connection.emptySelection=Please select first a Connection from the list
sebserver.monitoring.exam.connection.emptySelection.active=Please select first an active Connection from the list
sebserver.monitoring.exam.connection.title=SEB Client Connection sebserver.monitoring.exam.connection.title=SEB Client Connection
sebserver.monitoring.exam.connection.list.actions= sebserver.monitoring.exam.connection.list.actions=
sebserver.monitoring.exam.connection.action.view=View Details sebserver.monitoring.exam.connection.action.view=View Details

View file

@ -139,6 +139,8 @@ Composite {
background-color: transparent; background-color: transparent;
} }
Composite.bordered { Composite.bordered {
border: 2px; border: 2px;
} }
@ -196,6 +198,13 @@ Composite.login {
border-radius: 2px; border-radius: 2px;
} }
Composite.register {
background-color: #EAECEE;
margin: 20px 0 0 0;
padding: 15px 8px 8px 8px;
border: none;
}
Composite.login-back { Composite.login-back {
background-color: #EAECEE; background-color: #EAECEE;
margin: 0px 0 0 0; margin: 0px 0 0 0;