SEBSERV-27 #Insitution List and actions
This commit is contained in:
parent
60bd32c2cb
commit
04d438923d
50 changed files with 1296 additions and 335 deletions
|
@ -56,6 +56,11 @@ public class EntityName implements ModelIdAware, ModelNameAware {
|
||||||
return this.modelId;
|
return this.modelId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
public EntityKey getEntityKey() {
|
||||||
|
return new EntityKey(getModelId(), getEntityType());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
final int prime = 31;
|
final int prime = 31;
|
||||||
|
|
|
@ -95,7 +95,7 @@ public final class LmsSetupTestResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final LmsSetupTestResult ofQuizRequestError(final String message) {
|
public static final LmsSetupTestResult ofQuizRequestError(final String message) {
|
||||||
return new LmsSetupTestResult(false, null, null, message);
|
return new LmsSetupTestResult(false, Collections.emptyList(), null, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,6 +88,12 @@ public final class Result<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void handleError(final Consumer<Throwable> errorHandler) {
|
||||||
|
if (this.error != null) {
|
||||||
|
errorHandler.accept(this.error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Use this to get the resulting value or (if null) to get a given other value
|
/** Use this to get the resulting value or (if null) to get a given other value
|
||||||
*
|
*
|
||||||
* @param other the other value to get if the computed value is null
|
* @param other the other value to get if the computed value is null
|
||||||
|
|
|
@ -1,28 +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.service;
|
|
||||||
|
|
||||||
import org.eclipse.swt.widgets.Composite;
|
|
||||||
import org.eclipse.swt.widgets.Control;
|
|
||||||
|
|
||||||
public final class RWTUtils {
|
|
||||||
|
|
||||||
public static final String TEXT_NAME_H2 = "h2";
|
|
||||||
|
|
||||||
public static void clearComposite(final Composite parent) {
|
|
||||||
if (parent == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (final Control control : parent.getChildren()) {
|
|
||||||
control.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -11,6 +11,9 @@ package ch.ethz.seb.sebserver.gui.service.page;
|
||||||
import org.eclipse.swt.widgets.Composite;
|
import org.eclipse.swt.widgets.Composite;
|
||||||
import org.eclipse.swt.widgets.Shell;
|
import org.eclipse.swt.widgets.Shell;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.page.action.Action;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition;
|
||||||
|
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.PageEvent;
|
||||||
|
|
||||||
public interface PageContext {
|
public interface PageContext {
|
||||||
|
@ -30,7 +33,10 @@ public interface PageContext {
|
||||||
|
|
||||||
public static final String ATTR_PAGE_TEMPLATE_COMPOSER_NAME = "ATTR_PAGE_TEMPLATE_COMPOSER_NAME";
|
public static final String ATTR_PAGE_TEMPLATE_COMPOSER_NAME = "ATTR_PAGE_TEMPLATE_COMPOSER_NAME";
|
||||||
|
|
||||||
public static final String INSTITUTION_ID = "INSTITUTION_ID";
|
public static final String ATTR_ENTITY_ID = "ENTITY_ID";
|
||||||
|
public static final String ATTR_PARENT_ENTITY_ID = "PARENT_ENTITY_ID";
|
||||||
|
public static final String ATTR_ENTITY_TYPE = "ENTITY_TYPE";
|
||||||
|
public static final String ATTR_PARENT_ENTITY_TYPE = "PARENT_ENTITY_TYPE";
|
||||||
|
|
||||||
// public static final String USER_NAME = "USER_NAME";
|
// public static final String USER_NAME = "USER_NAME";
|
||||||
// public static final String PASSWORD = "PASSWORD";
|
// public static final String PASSWORD = "PASSWORD";
|
||||||
|
@ -86,6 +92,8 @@ public interface PageContext {
|
||||||
* @return this PageContext instance (builder pattern) */
|
* @return this PageContext instance (builder pattern) */
|
||||||
PageContext withAttr(String key, String value);
|
PageContext withAttr(String key, String value);
|
||||||
|
|
||||||
|
PageContext withSelection(ActivitySelection selection);
|
||||||
|
|
||||||
String getAttribute(String name);
|
String getAttribute(String name);
|
||||||
|
|
||||||
String getAttribute(String name, String def);
|
String getAttribute(String name, String def);
|
||||||
|
@ -99,6 +107,8 @@ public interface PageContext {
|
||||||
* @param event the concrete PageEvent instance */
|
* @param event the concrete PageEvent instance */
|
||||||
<T extends PageEvent> void publishPageEvent(T event);
|
<T extends PageEvent> void publishPageEvent(T event);
|
||||||
|
|
||||||
|
Action createAction(ActionDefinition actionDefinition);
|
||||||
|
|
||||||
/** Apply a confirm dialog with a specified confirm message and a callback code
|
/** Apply a confirm dialog with a specified confirm message and a callback code
|
||||||
* block that will be executed on users OK selection.
|
* block that will be executed on users OK selection.
|
||||||
*
|
*
|
||||||
|
@ -125,4 +135,6 @@ public interface PageContext {
|
||||||
|
|
||||||
<T> T logoutOnError(Throwable error);
|
<T> T logoutOnError(Throwable error);
|
||||||
|
|
||||||
|
void publishPageMessage(PageMessageException pme);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +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;
|
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.event.PageEvent;
|
|
||||||
|
|
||||||
public interface PageEventListener<T extends PageEvent> {
|
|
||||||
|
|
||||||
String LISTENER_ATTRIBUTE_KEY = "PageEventListener";
|
|
||||||
|
|
||||||
boolean match(Class<? extends PageEvent> eventType);
|
|
||||||
|
|
||||||
default int priority() {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void notify(T event);
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* 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.page;
|
||||||
|
|
||||||
|
public class PageMessageException extends RuntimeException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -6967378384991469166L;
|
||||||
|
|
||||||
|
public PageMessageException(final String message, final Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PageMessageException(final String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
/*
|
||||||
|
* 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.page.action;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
|
||||||
|
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;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
||||||
|
|
||||||
|
public class Action implements Runnable {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(Action.class);
|
||||||
|
|
||||||
|
public final ActionDefinition definition;
|
||||||
|
String confirmationMessage;
|
||||||
|
String successMessage;
|
||||||
|
boolean updateOnSelection;
|
||||||
|
|
||||||
|
final RestService restService;
|
||||||
|
final PageContext pageContext;
|
||||||
|
Function<Action, Result<?>> exec;
|
||||||
|
Supplier<Set<String>> selectionSupplier;
|
||||||
|
|
||||||
|
public Action(
|
||||||
|
final ActionDefinition definition,
|
||||||
|
final PageContext pageContext,
|
||||||
|
final RestService restService) {
|
||||||
|
|
||||||
|
this.definition = definition;
|
||||||
|
this.pageContext = pageContext;
|
||||||
|
this.restService = restService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (StringUtils.isNotBlank(this.confirmationMessage)) {
|
||||||
|
this.pageContext.applyConfirmDialog(
|
||||||
|
this.confirmationMessage,
|
||||||
|
() -> exec());
|
||||||
|
} else {
|
||||||
|
exec();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void exec() {
|
||||||
|
try {
|
||||||
|
|
||||||
|
this.exec.apply(this)
|
||||||
|
.map(value -> {
|
||||||
|
this.pageContext.publishPageEvent(
|
||||||
|
new ActionEvent(this.definition, value));
|
||||||
|
return value;
|
||||||
|
})
|
||||||
|
.getOrThrow();
|
||||||
|
|
||||||
|
} catch (final PageMessageException pme) {
|
||||||
|
Action.this.pageContext.publishPageMessage(pme);
|
||||||
|
} catch (final Throwable t) {
|
||||||
|
log.error("Failed to execute action: {}", Action.this, t);
|
||||||
|
Action.this.pageContext.notifyError("action.error.unexpected.message", t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Action withExec(final Function<Action, Result<?>> exec) {
|
||||||
|
this.exec = exec;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Action withSelectionSupplier(final Supplier<Set<String>> selectionSupplier) {
|
||||||
|
this.selectionSupplier = selectionSupplier;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Action withConfirm(final String confirmationMessage) {
|
||||||
|
this.confirmationMessage = confirmationMessage;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Action withSuccess(final String successMessage) {
|
||||||
|
this.successMessage = successMessage;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Action withUpdateOnSelection() {
|
||||||
|
this.updateOnSelection = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PageContext publish() {
|
||||||
|
this.pageContext.publishPageEvent(new ActionPublishEvent(this));
|
||||||
|
return this.pageContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -13,10 +13,14 @@ import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory.IconButtonType;
|
||||||
public enum ActionDefinition {
|
public enum ActionDefinition {
|
||||||
|
|
||||||
INSTITUTION_NEW(
|
INSTITUTION_NEW(
|
||||||
"actions.new.institution",
|
"sebserver.institution.action.new",
|
||||||
IconButtonType.NEW_ACTION),
|
IconButtonType.NEW_ACTION),
|
||||||
|
|
||||||
INSTITUTION_MODIFY(
|
INSTITUTION_MODIFY(
|
||||||
|
"sebserver.institution.action.modify",
|
||||||
|
IconButtonType.MODIFY_ACTION),
|
||||||
|
|
||||||
|
INSTITUTION_SAVE(
|
||||||
"actions.modify.institution",
|
"actions.modify.institution",
|
||||||
IconButtonType.SAVE_ACTION),
|
IconButtonType.SAVE_ACTION),
|
||||||
|
|
||||||
|
|
|
@ -22,11 +22,12 @@ import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
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.PageEventListener;
|
|
||||||
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.page.event.ActionPublishEvent;
|
import ch.ethz.seb.sebserver.gui.service.page.event.ActionPublishEvent;
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.event.ActionPublishEventListener;
|
import ch.ethz.seb.sebserver.gui.service.page.event.ActionPublishEventListener;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.page.event.PageEventListener;
|
||||||
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory;
|
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory.CustomVariant;
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
@Component
|
@Component
|
||||||
|
@ -42,18 +43,21 @@ public class ActionPane implements TemplateComposer {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compose(final PageContext composerCtx) {
|
public void compose(final PageContext pageContext) {
|
||||||
|
|
||||||
final Label label = this.widgetFactory.labelLocalized(
|
final Label label = this.widgetFactory.labelLocalized(
|
||||||
composerCtx.getParent(),
|
pageContext.getParent(),
|
||||||
"h3",
|
CustomVariant.TEXT_H2,
|
||||||
new LocTextKey("sebserver.actionpane.title"));
|
new LocTextKey("sebserver.actionpane.title"));
|
||||||
|
|
||||||
final GridData titleLayout = new GridData(SWT.FILL, SWT.TOP, true, false);
|
final GridData titleLayout = new GridData(SWT.FILL, SWT.TOP, true, false);
|
||||||
titleLayout.verticalIndent = 10;
|
titleLayout.verticalIndent = 10;
|
||||||
titleLayout.horizontalIndent = 10;
|
titleLayout.horizontalIndent = 10;
|
||||||
label.setLayoutData(titleLayout);
|
label.setLayoutData(titleLayout);
|
||||||
|
|
||||||
final Tree actions = this.widgetFactory.treeLocalized(composerCtx.getParent(), SWT.SINGLE | SWT.FULL_SELECTION);
|
final Tree actions = this.widgetFactory.treeLocalized(
|
||||||
|
pageContext.getParent(),
|
||||||
|
SWT.SINGLE | SWT.FULL_SELECTION);
|
||||||
actions.setData(RWT.CUSTOM_VARIANT, "actions");
|
actions.setData(RWT.CUSTOM_VARIANT, "actions");
|
||||||
final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
|
final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
|
||||||
actions.setLayoutData(gridData);
|
actions.setLayoutData(gridData);
|
||||||
|
@ -69,7 +73,7 @@ public class ActionPane implements TemplateComposer {
|
||||||
actions.addListener(SWT.Selection, event -> {
|
actions.addListener(SWT.Selection, event -> {
|
||||||
final TreeItem treeItem = (TreeItem) event.item;
|
final TreeItem treeItem = (TreeItem) event.item;
|
||||||
|
|
||||||
final Runnable action = (Runnable) treeItem.getData(ACTION_EVENT_CALL_KEY);
|
final Action action = (Action) treeItem.getData(ACTION_EVENT_CALL_KEY);
|
||||||
action.run();
|
action.run();
|
||||||
|
|
||||||
if (!treeItem.isDisposed()) {
|
if (!treeItem.isDisposed()) {
|
||||||
|
@ -82,12 +86,16 @@ public class ActionPane implements TemplateComposer {
|
||||||
new ActionPublishEventListener() {
|
new ActionPublishEventListener() {
|
||||||
@Override
|
@Override
|
||||||
public void notify(final ActionPublishEvent event) {
|
public void notify(final ActionPublishEvent event) {
|
||||||
|
|
||||||
final TreeItem actionItem = ActionPane.this.widgetFactory.treeItemLocalized(
|
final TreeItem actionItem = ActionPane.this.widgetFactory.treeItemLocalized(
|
||||||
actions,
|
actions,
|
||||||
event.actionDefinition.name);
|
event.action.definition.name);
|
||||||
actionItem.setImage(event.actionDefinition.icon.getImage(composerCtx.getParent().getDisplay()));
|
|
||||||
actionItem.setData(ACTION_EVENT_CALL_KEY,
|
actionItem.setImage(event.action.definition.icon.getImage(
|
||||||
new SafeActionExecution(composerCtx, event, event.run));
|
pageContext.getParent().getDisplay()));
|
||||||
|
|
||||||
|
actionItem.setData(ACTION_EVENT_CALL_KEY, event.action);
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,47 @@
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.gui.service.page.action;
|
package ch.ethz.seb.sebserver.gui.service.page.action;
|
||||||
|
|
||||||
public interface InstitutionActions {
|
import static ch.ethz.seb.sebserver.gui.service.page.activity.ActivitySelection.Activity.INSTITUTION_NODE;
|
||||||
|
|
||||||
// /** Use this higher-order function to create a new Institution action Runnable.
|
import java.util.Collection;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.EntityType;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.page.PageMessageException;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.page.event.ActivitySelectionEvent;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.NewInstitution;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.table.EntityTable;
|
||||||
|
|
||||||
|
public final class InstitutionActions {
|
||||||
|
|
||||||
|
public static Result<?> newInstitution(final Action action) {
|
||||||
|
return action.restService
|
||||||
|
.getBuilder(NewInstitution.class)
|
||||||
|
.call();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Function<Action, Result<?>> editInstitution(final EntityTable<?> fromTable) {
|
||||||
|
return action -> {
|
||||||
|
final Collection<String> selection = fromTable.getSelection();
|
||||||
|
if (selection.isEmpty()) {
|
||||||
|
return Result.ofError(new PageMessageException("sebserver.institution.info.pleaseSelect"));
|
||||||
|
}
|
||||||
|
|
||||||
|
final EntityKey entityKey = new EntityKey(
|
||||||
|
selection.iterator().next(),
|
||||||
|
EntityType.INSTITUTION);
|
||||||
|
action.pageContext.publishPageEvent(new ActivitySelectionEvent(
|
||||||
|
INSTITUTION_NODE
|
||||||
|
.createSelection()
|
||||||
|
.withEntity(entityKey)));
|
||||||
|
|
||||||
|
return Result.of(entityKey);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// /** Use this higher-order function to create a new Institution action function.
|
||||||
// *
|
// *
|
||||||
// * @return */
|
// * @return */
|
||||||
// static Runnable newInstitution(final PageContext composerCtx, final RestServices restServices) {
|
// static Runnable newInstitution(final PageContext composerCtx, final RestServices restServices) {
|
||||||
|
@ -23,7 +61,7 @@ public interface InstitutionActions {
|
||||||
// };
|
// };
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// /** Use this higher-order function to create a delete Institution action Runnable.
|
// /** Use this higher-order function to create a delete Institution action function.
|
||||||
// *
|
// *
|
||||||
// * @return */
|
// * @return */
|
||||||
// static Runnable deleteInstitution(final PageContext composerCtx, final RestServices restServices,
|
// static Runnable deleteInstitution(final PageContext composerCtx, final RestServices restServices,
|
||||||
|
|
|
@ -1,68 +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.action;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
|
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.event.ActionPublishEvent;
|
|
||||||
|
|
||||||
public class SafeActionExecution implements Runnable {
|
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(SafeActionExecution.class);
|
|
||||||
|
|
||||||
private final PageContext pageContext;
|
|
||||||
private final ActionPublishEvent actionEvent;
|
|
||||||
private final Runnable actionExecution;
|
|
||||||
|
|
||||||
public SafeActionExecution(
|
|
||||||
final PageContext pageContext,
|
|
||||||
final ActionPublishEvent actionEvent,
|
|
||||||
final Runnable actionExecution) {
|
|
||||||
|
|
||||||
this.pageContext = pageContext;
|
|
||||||
this.actionEvent = actionEvent;
|
|
||||||
this.actionExecution = actionExecution;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
if (StringUtils.isNotBlank(this.actionEvent.confirmationMessage)) {
|
|
||||||
this.pageContext.applyConfirmDialog(
|
|
||||||
this.actionEvent.confirmationMessage,
|
|
||||||
createConfirmationCallback());
|
|
||||||
} else {
|
|
||||||
this.actionExecution.run();
|
|
||||||
}
|
|
||||||
} catch (final Throwable t) {
|
|
||||||
log.error("Failed to execute action: {}", this.actionEvent, t);
|
|
||||||
this.pageContext.notifyError("action.error.unexpected.message", t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Runnable createConfirmationCallback() {
|
|
||||||
return new Runnable() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
SafeActionExecution.this.actionExecution.run();
|
|
||||||
} catch (final Throwable t) {
|
|
||||||
log.error("Failed to execute action: {}", SafeActionExecution.this.actionEvent, t);
|
|
||||||
SafeActionExecution.this.pageContext.notifyError("action.error.unexpected.message", t);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -9,9 +9,7 @@
|
||||||
package ch.ethz.seb.sebserver.gui.service.page.activity;
|
package ch.ethz.seb.sebserver.gui.service.page.activity;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.eclipse.swt.SWT;
|
import org.eclipse.swt.SWT;
|
||||||
|
@ -28,18 +26,18 @@ import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
|
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
|
||||||
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.PageEventListener;
|
|
||||||
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.page.action.ActionDefinition;
|
import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition;
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.activity.ActivitySelection.Activity;
|
import ch.ethz.seb.sebserver.gui.service.page.activity.ActivitySelection.Activity;
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent;
|
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.ActionEventListener;
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.event.ActivitySelectionEvent;
|
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.page.impl.MainPageState;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitutionNames;
|
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder;
|
||||||
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory;
|
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory.CustomVariant;
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
@Component
|
@Component
|
||||||
|
@ -76,7 +74,7 @@ public class ActivitiesPane implements TemplateComposer {
|
||||||
|
|
||||||
final Label activities = this.widgetFactory.labelLocalized(
|
final Label activities = this.widgetFactory.labelLocalized(
|
||||||
pageContext.getParent(),
|
pageContext.getParent(),
|
||||||
"h3",
|
CustomVariant.TEXT_H2,
|
||||||
new LocTextKey("sebserver.activitiespane.title"));
|
new LocTextKey("sebserver.activitiespane.title"));
|
||||||
final GridData activitiesGridData = new GridData(SWT.FILL, SWT.TOP, true, false);
|
final GridData activitiesGridData = new GridData(SWT.FILL, SWT.TOP, true, false);
|
||||||
activitiesGridData.horizontalIndent = 20;
|
activitiesGridData.horizontalIndent = 20;
|
||||||
|
@ -88,10 +86,10 @@ public class ActivitiesPane implements TemplateComposer {
|
||||||
navigationGridData.horizontalIndent = 10;
|
navigationGridData.horizontalIndent = 10;
|
||||||
navigation.setLayoutData(navigationGridData);
|
navigation.setLayoutData(navigationGridData);
|
||||||
|
|
||||||
final List<EntityName> insitutionNames = this.restService
|
// final List<EntityName> insitutionNames = this.restService
|
||||||
.getBuilder(GetInstitutionNames.class)
|
// .getBuilder(GetInstitutionNames.class)
|
||||||
.call()
|
// .call()
|
||||||
.get(pageContext::notifyError, () -> Collections.emptyList());
|
// .get(pageContext::notifyError, () -> Collections.emptyList());
|
||||||
|
|
||||||
if (userInfo.hasRole(UserRole.SEB_SERVER_ADMIN)) {
|
if (userInfo.hasRole(UserRole.SEB_SERVER_ADMIN)) {
|
||||||
// institutions (list) as root
|
// institutions (list) as root
|
||||||
|
@ -100,12 +98,17 @@ public class ActivitiesPane implements TemplateComposer {
|
||||||
Activity.INSTITUTION_ROOT.title);
|
Activity.INSTITUTION_ROOT.title);
|
||||||
ActivitySelection.inject(institutions, Activity.INSTITUTION_ROOT.createSelection());
|
ActivitySelection.inject(institutions, Activity.INSTITUTION_ROOT.createSelection());
|
||||||
|
|
||||||
for (final EntityName inst : insitutionNames) {
|
// for (final EntityName inst : insitutionNames) {
|
||||||
createInstitutionItem(institutions, inst);
|
// createInstitutionItem(institutions, inst);
|
||||||
}
|
// }
|
||||||
} else {
|
} else {
|
||||||
final EntityName inst = insitutionNames.iterator().next();
|
// institution node as none root
|
||||||
createInstitutionItem(navigation, inst);
|
final TreeItem institutions = this.widgetFactory.treeItemLocalized(
|
||||||
|
navigation,
|
||||||
|
Activity.INSTITUTION_ROOT.title);
|
||||||
|
ActivitySelection.inject(institutions, Activity.INSTITUTION_NODE.createSelection());
|
||||||
|
// final EntityName inst = insitutionNames.iterator().next();
|
||||||
|
// createInstitutionItem(navigation, inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
// final TreeItem user = this.widgetFactory.treeItemLocalized(
|
// final TreeItem user = this.widgetFactory.treeItemLocalized(
|
||||||
|
@ -222,21 +225,25 @@ public class ActivitiesPane implements TemplateComposer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static TreeItem createInstitutionItem(final Tree parent, final EntityName idAndName) {
|
static TreeItem createInstitutionItem(final Tree parent, final EntityName entityName) {
|
||||||
final TreeItem institution = new TreeItem(parent, SWT.NONE);
|
final TreeItem institution = new TreeItem(parent, SWT.NONE);
|
||||||
createInstitutionItem(idAndName, institution);
|
createInstitutionItem(entityName, institution);
|
||||||
return institution;
|
return institution;
|
||||||
}
|
}
|
||||||
|
|
||||||
static TreeItem createInstitutionItem(final TreeItem parent, final EntityName idAndName) {
|
static TreeItem createInstitutionItem(final TreeItem parent, final EntityName entityName) {
|
||||||
final TreeItem institution = new TreeItem(parent, SWT.NONE);
|
final TreeItem institution = new TreeItem(parent, SWT.NONE);
|
||||||
createInstitutionItem(idAndName, institution);
|
createInstitutionItem(entityName, institution);
|
||||||
return institution;
|
return institution;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void createInstitutionItem(final EntityName idAndName, final TreeItem institution) {
|
static void createInstitutionItem(final EntityName entityName, final TreeItem institution) {
|
||||||
institution.setText(idAndName.name);
|
institution.setText(entityName.name);
|
||||||
ActivitySelection.inject(institution, Activity.INSTITUTION_NODE.createSelection(idAndName));
|
ActivitySelection.inject(
|
||||||
|
institution,
|
||||||
|
Activity.INSTITUTION_NODE
|
||||||
|
.createSelection()
|
||||||
|
.withEntity(entityName.getEntityKey()));
|
||||||
}
|
}
|
||||||
|
|
||||||
static final TreeItem findItemByActivity(
|
static final TreeItem findItemByActivity(
|
||||||
|
@ -250,7 +257,7 @@ public class ActivitiesPane implements TemplateComposer {
|
||||||
|
|
||||||
for (final TreeItem item : items) {
|
for (final TreeItem item : items) {
|
||||||
final ActivitySelection activitySelection = ActivitySelection.get(item);
|
final ActivitySelection activitySelection = ActivitySelection.get(item);
|
||||||
final String id = activitySelection.getObjectIdentifier();
|
final String id = activitySelection.getEntityId();
|
||||||
if (activitySelection != null && activitySelection.activity == activity &&
|
if (activitySelection != null && activitySelection.activity == activity &&
|
||||||
(id == null || (objectId != null && objectId.equals(id)))) {
|
(id == null || (objectId != null && objectId.equals(id)))) {
|
||||||
return item;
|
return item;
|
||||||
|
|
|
@ -8,11 +8,14 @@
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.gui.service.page.activity;
|
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 java.util.function.Consumer;
|
||||||
|
|
||||||
import org.eclipse.swt.widgets.TreeItem;
|
import org.eclipse.swt.widgets.TreeItem;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.EntityName;
|
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||||
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.AttributeKeys;
|
import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys;
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
|
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
|
||||||
|
@ -30,7 +33,7 @@ public class ActivitySelection {
|
||||||
};
|
};
|
||||||
|
|
||||||
public enum Activity {
|
public enum Activity {
|
||||||
NONE(TODOTemplate.class, TODOTemplate.class, (String) null),
|
NONE(TODOTemplate.class, TODOTemplate.class),
|
||||||
INSTITUTION_ROOT(
|
INSTITUTION_ROOT(
|
||||||
InstitutionList.class,
|
InstitutionList.class,
|
||||||
ActionPane.class,
|
ActionPane.class,
|
||||||
|
@ -38,7 +41,7 @@ public class ActivitySelection {
|
||||||
INSTITUTION_NODE(
|
INSTITUTION_NODE(
|
||||||
TODOTemplate.class,
|
TODOTemplate.class,
|
||||||
ActionPane.class,
|
ActionPane.class,
|
||||||
AttributeKeys.INSTITUTION_ID),
|
new LocTextKey("sebserver.activities.inst")),
|
||||||
//
|
//
|
||||||
// USERS(UserAccountsForm.class, ActionPane.class),
|
// USERS(UserAccountsForm.class, ActionPane.class),
|
||||||
//
|
//
|
||||||
|
@ -55,7 +58,7 @@ public class ActivitySelection {
|
||||||
public final LocTextKey title;
|
public final LocTextKey title;
|
||||||
public final Class<? extends TemplateComposer> contentPaneComposer;
|
public final Class<? extends TemplateComposer> contentPaneComposer;
|
||||||
public final Class<? extends TemplateComposer> actionPaneComposer;
|
public final Class<? extends TemplateComposer> actionPaneComposer;
|
||||||
public final String objectIdentifierAttribute;
|
//public final String modelIdAttribute;
|
||||||
|
|
||||||
private Activity(
|
private Activity(
|
||||||
final Class<? extends TemplateComposer> objectPaneComposer,
|
final Class<? extends TemplateComposer> objectPaneComposer,
|
||||||
|
@ -65,43 +68,54 @@ public class ActivitySelection {
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.contentPaneComposer = objectPaneComposer;
|
this.contentPaneComposer = objectPaneComposer;
|
||||||
this.actionPaneComposer = selectionPaneComposer;
|
this.actionPaneComposer = selectionPaneComposer;
|
||||||
this.objectIdentifierAttribute = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Activity(
|
private Activity(
|
||||||
final Class<? extends TemplateComposer> objectPaneComposer,
|
final Class<? extends TemplateComposer> objectPaneComposer,
|
||||||
final Class<? extends TemplateComposer> selectionPaneComposer,
|
final Class<? extends TemplateComposer> selectionPaneComposer) {
|
||||||
final String objectIdentifierAttribute) {
|
|
||||||
|
|
||||||
this.title = null;
|
this.title = null;
|
||||||
this.contentPaneComposer = objectPaneComposer;
|
this.contentPaneComposer = objectPaneComposer;
|
||||||
this.actionPaneComposer = selectionPaneComposer;
|
this.actionPaneComposer = selectionPaneComposer;
|
||||||
this.objectIdentifierAttribute = objectIdentifierAttribute;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final ActivitySelection createSelection() {
|
public final ActivitySelection createSelection() {
|
||||||
return new ActivitySelection(this);
|
return new ActivitySelection(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final ActivitySelection createSelection(final EntityName entityName) {
|
|
||||||
return new ActivitySelection(this, entityName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String ATTR_ACTIVITY_SELECTION = "ACTIVITY_SELECTION";
|
private static final String ATTR_ACTIVITY_SELECTION = "ACTIVITY_SELECTION";
|
||||||
|
|
||||||
public final Activity activity;
|
public final Activity activity;
|
||||||
public final EntityName entityName;
|
final Map<String, String> attributes;
|
||||||
Consumer<TreeItem> expandFunction = EMPTY_FUNCTION;
|
Consumer<TreeItem> expandFunction = EMPTY_FUNCTION;
|
||||||
|
|
||||||
ActivitySelection(final Activity activity) {
|
ActivitySelection(final Activity activity) {
|
||||||
this(activity, null);
|
this.activity = activity;
|
||||||
|
this.attributes = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
ActivitySelection(final Activity activity, final EntityName entityName) {
|
public ActivitySelection withEntity(final EntityKey entityKey) {
|
||||||
this.activity = activity;
|
if (entityKey != null) {
|
||||||
this.entityName = entityName;
|
this.attributes.put(AttributeKeys.ATTR_ENTITY_ID, entityKey.modelId);
|
||||||
this.expandFunction = EMPTY_FUNCTION;
|
this.attributes.put(AttributeKeys.ATTR_ENTITY_TYPE, entityKey.entityType.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActivitySelection withParentEntity(final EntityKey parentEntityKey) {
|
||||||
|
if (parentEntityKey != null) {
|
||||||
|
this.attributes.put(AttributeKeys.ATTR_PARENT_ENTITY_ID, parentEntityKey.modelId);
|
||||||
|
this.attributes.put(AttributeKeys.ATTR_PARENT_ENTITY_TYPE, parentEntityKey.entityType.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getAttributes() {
|
||||||
|
return Collections.unmodifiableMap(this.attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ActivitySelection withExpandFunction(final Consumer<TreeItem> expandFunction) {
|
public ActivitySelection withExpandFunction(final Consumer<TreeItem> expandFunction) {
|
||||||
|
@ -112,44 +126,12 @@ public class ActivitySelection {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getObjectIdentifier() {
|
|
||||||
if (this.entityName == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.entityName.modelId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void processExpand(final TreeItem item) {
|
public void processExpand(final TreeItem item) {
|
||||||
this.expandFunction.accept(item);
|
this.expandFunction.accept(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public String getEntityId() {
|
||||||
public int hashCode() {
|
return this.attributes.get(AttributeKeys.ATTR_ENTITY_ID);
|
||||||
final int prime = 31;
|
|
||||||
int result = 1;
|
|
||||||
result = prime * result + ((this.activity == null) ? 0 : this.activity.hashCode());
|
|
||||||
result = prime * result + ((this.entityName == null) ? 0 : this.entityName.hashCode());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(final Object obj) {
|
|
||||||
if (this == obj)
|
|
||||||
return true;
|
|
||||||
if (obj == null)
|
|
||||||
return false;
|
|
||||||
if (getClass() != obj.getClass())
|
|
||||||
return false;
|
|
||||||
final ActivitySelection other = (ActivitySelection) obj;
|
|
||||||
if (this.activity != other.activity)
|
|
||||||
return false;
|
|
||||||
if (this.entityName == null) {
|
|
||||||
if (other.entityName != null)
|
|
||||||
return false;
|
|
||||||
} else if (!this.entityName.equals(other.entityName))
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ActivitySelection get(final TreeItem item) {
|
public static ActivitySelection get(final TreeItem item) {
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
/*
|
||||||
|
* 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.page.content;
|
||||||
|
|
||||||
|
public class Institution {
|
||||||
|
|
||||||
|
}
|
|
@ -16,13 +16,17 @@ import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.institution.Institution;
|
||||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||||
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.TemplateComposer;
|
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.page.action.InstitutionActions;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitutions;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitutions;
|
||||||
import ch.ethz.seb.sebserver.gui.service.table.ColumnDefinition;
|
import ch.ethz.seb.sebserver.gui.service.table.ColumnDefinition;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.table.EntityTable;
|
||||||
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory;
|
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory;
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
|
@ -49,37 +53,39 @@ public class InstitutionList implements TemplateComposer {
|
||||||
content.setLayout(contentLayout);
|
content.setLayout(contentLayout);
|
||||||
content.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
|
content.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
|
||||||
|
|
||||||
|
// title
|
||||||
this.widgetFactory.labelLocalizedTitle(
|
this.widgetFactory.labelLocalizedTitle(
|
||||||
content,
|
content,
|
||||||
new LocTextKey("sebserver.institution.list.title"));
|
new LocTextKey("sebserver.institution.list.title"));
|
||||||
|
|
||||||
this.widgetFactory.entityTableBuilder(this.restService.getRestCall(GetInstitutions.class))
|
// table
|
||||||
.withPaging(3)
|
final EntityTable<Institution> table =
|
||||||
.withColumn(new ColumnDefinition<>(
|
this.widgetFactory.entityTableBuilder(this.restService.getRestCall(GetInstitutions.class))
|
||||||
Domain.INSTITUTION.ATTR_NAME,
|
.withPaging(3)
|
||||||
new LocTextKey("sebserver.institution.list.column.name"),
|
.withColumn(new ColumnDefinition<>(
|
||||||
null,
|
Domain.INSTITUTION.ATTR_NAME,
|
||||||
0,
|
new LocTextKey("sebserver.institution.list.column.name"),
|
||||||
entity -> entity.name,
|
entity -> entity.name,
|
||||||
null,
|
true))
|
||||||
true))
|
.withColumn(new ColumnDefinition<>(
|
||||||
.withColumn(new ColumnDefinition<>(
|
Domain.INSTITUTION.ATTR_URL_SUFFIX,
|
||||||
Domain.INSTITUTION.ATTR_URL_SUFFIX,
|
new LocTextKey("sebserver.institution.list.column.urlSuffix"),
|
||||||
new LocTextKey("sebserver.institution.list.column.urlSuffix"),
|
entity -> entity.urlSuffix,
|
||||||
null,
|
true))
|
||||||
0,
|
.withColumn(new ColumnDefinition<>(
|
||||||
entity -> entity.urlSuffix,
|
Domain.INSTITUTION.ATTR_ACTIVE,
|
||||||
null,
|
new LocTextKey("sebserver.institution.list.column.active"),
|
||||||
true))
|
entity -> entity.active,
|
||||||
.withColumn(new ColumnDefinition<>(
|
true))
|
||||||
Domain.INSTITUTION.ATTR_ACTIVE,
|
.compose(content);
|
||||||
new LocTextKey("sebserver.institution.list.column.active"),
|
|
||||||
null,
|
// propagate content actions to action-pane
|
||||||
0,
|
pageContext.createAction(ActionDefinition.INSTITUTION_NEW)
|
||||||
entity -> entity.active,
|
.withExec(InstitutionActions::newInstitution)
|
||||||
null,
|
.publish()
|
||||||
true))
|
.createAction(ActionDefinition.INSTITUTION_MODIFY)
|
||||||
.compose(content);
|
.withExec(InstitutionActions.editInstitution(table))
|
||||||
|
.publish();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,8 @@ package ch.ethz.seb.sebserver.gui.service.page.event;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition;
|
import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition;
|
||||||
|
|
||||||
|
/** 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 class ActionEvent implements PageEvent {
|
||||||
|
|
||||||
public final ActionDefinition actionDefinition;
|
public final ActionDefinition actionDefinition;
|
||||||
|
|
|
@ -13,7 +13,6 @@ import java.util.function.Predicate;
|
||||||
|
|
||||||
import org.eclipse.swt.widgets.Widget;
|
import org.eclipse.swt.widgets.Widget;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.PageEventListener;
|
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition;
|
import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition;
|
||||||
|
|
||||||
public interface ActionEventListener extends PageEventListener<ActionEvent> {
|
public interface ActionEventListener extends PageEventListener<ActionEvent> {
|
||||||
|
|
|
@ -8,46 +8,16 @@
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.gui.service.page.event;
|
package ch.ethz.seb.sebserver.gui.service.page.event;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition;
|
import ch.ethz.seb.sebserver.gui.service.page.action.Action;
|
||||||
|
|
||||||
|
/** This action is used to publish an Action to the Action-Pane for a specified context.
|
||||||
|
* The ActionPane is listening to this events and render specified actions on notify */
|
||||||
public class ActionPublishEvent implements PageEvent {
|
public class ActionPublishEvent implements PageEvent {
|
||||||
|
|
||||||
public final ActionDefinition actionDefinition;
|
public final Action action;
|
||||||
public final Runnable run;
|
|
||||||
public final String confirmationMessage;
|
|
||||||
public final String successMessage;
|
|
||||||
|
|
||||||
public ActionPublishEvent(
|
public ActionPublishEvent(final Action action) {
|
||||||
final ActionDefinition actionDefinition,
|
this.action = action;
|
||||||
final Runnable run) {
|
|
||||||
|
|
||||||
this(actionDefinition, run, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ActionPublishEvent(
|
|
||||||
final ActionDefinition actionDefinition,
|
|
||||||
final Runnable run,
|
|
||||||
final String confirmationMessage) {
|
|
||||||
|
|
||||||
this(actionDefinition, run, confirmationMessage, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ActionPublishEvent(
|
|
||||||
final ActionDefinition actionDefinition,
|
|
||||||
final Runnable run,
|
|
||||||
final String confirmationMessage,
|
|
||||||
final String successMessage) {
|
|
||||||
|
|
||||||
this.actionDefinition = actionDefinition;
|
|
||||||
this.run = run;
|
|
||||||
this.confirmationMessage = confirmationMessage;
|
|
||||||
this.successMessage = successMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "ActionPublishEvent [actionDefinition=" + this.actionDefinition + ", confirmationMessage="
|
|
||||||
+ this.confirmationMessage + ", successMessage=" + this.successMessage + "]";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,6 @@
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.gui.service.page.event;
|
package ch.ethz.seb.sebserver.gui.service.page.event;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.PageEventListener;
|
|
||||||
|
|
||||||
public interface ActionPublishEventListener extends PageEventListener<ActionPublishEvent> {
|
public interface ActionPublishEventListener extends PageEventListener<ActionPublishEvent> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -8,8 +8,6 @@
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.gui.service.page.event;
|
package ch.ethz.seb.sebserver.gui.service.page.event;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.PageEventListener;
|
|
||||||
|
|
||||||
public interface ActivitySelectionListener extends PageEventListener<ActivitySelectionEvent> {
|
public interface ActivitySelectionListener extends PageEventListener<ActivitySelectionEvent> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -8,8 +8,6 @@
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.gui.service.page.event;
|
package ch.ethz.seb.sebserver.gui.service.page.event;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.PageEventListener;
|
|
||||||
|
|
||||||
public interface LogoutEventListener extends PageEventListener<LogoutEvent> {
|
public interface LogoutEventListener extends PageEventListener<LogoutEvent> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -8,6 +8,11 @@
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.gui.service.page.event;
|
package ch.ethz.seb.sebserver.gui.service.page.event;
|
||||||
|
|
||||||
|
/** This is just a marker interface for all page events.
|
||||||
|
* Page events can be published to the actual page tree by using PageContext.publishPageEvent
|
||||||
|
*
|
||||||
|
* Potentially every component on the actual page tree can listen to an certain page event
|
||||||
|
* by adding a specified listener within the components setData functionality.
|
||||||
|
* see PageListener for more information */
|
||||||
public interface PageEvent {
|
public interface PageEvent {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/** Defines a listener for PageEvent.
|
||||||
|
*
|
||||||
|
* Potentially every component on the actual page tree can listen to an certain page event
|
||||||
|
* by adding a specified listener within the components setData functionality.
|
||||||
|
*
|
||||||
|
* @param <T> the type of the PageEvent to listen to */
|
||||||
|
public interface PageEventListener<T extends PageEvent> {
|
||||||
|
|
||||||
|
/** The key name used to register PageEventListener instances within the
|
||||||
|
* setData functionality of a component */
|
||||||
|
String LISTENER_ATTRIBUTE_KEY = "PageEventListener";
|
||||||
|
|
||||||
|
/** Used to check a concrete listener is interested in a specified type of PageEvent.
|
||||||
|
*
|
||||||
|
* @param eventType the PageEvent type
|
||||||
|
* @return whether the listener is interested in being notified by the event or not */
|
||||||
|
boolean match(Class<? extends PageEvent> eventType);
|
||||||
|
|
||||||
|
/** The listeners priority.
|
||||||
|
* Use this if a dedicated order or sequence of listener notification is needed.
|
||||||
|
* Default priority is 1
|
||||||
|
*
|
||||||
|
* @return the priority of the listener that defines in witch sequence listeners of the same
|
||||||
|
* type get notified on a PageEvent propagation process on the current page-tree */
|
||||||
|
default int priority() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void notify(T event);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,234 @@
|
||||||
|
/*
|
||||||
|
* 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.form;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import org.eclipse.rap.rwt.RWT;
|
||||||
|
import org.eclipse.swt.widgets.Combo;
|
||||||
|
import org.eclipse.swt.widgets.Control;
|
||||||
|
import org.eclipse.swt.widgets.Label;
|
||||||
|
import org.eclipse.swt.widgets.Text;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.FormBinding;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.widget.SingleSelection;
|
||||||
|
|
||||||
|
public final class Form implements FormBinding {
|
||||||
|
|
||||||
|
private final JSONMapper jsonMapper;
|
||||||
|
private final ObjectNode objectRoot;
|
||||||
|
|
||||||
|
private final Map<String, 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<>();
|
||||||
|
|
||||||
|
Form(final JSONMapper jsonMapper) {
|
||||||
|
this.jsonMapper = jsonMapper;
|
||||||
|
this.objectRoot = this.jsonMapper.createObjectNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFormAsJson() {
|
||||||
|
try {
|
||||||
|
flush();
|
||||||
|
return this.jsonMapper.writeValueAsString(this.objectRoot);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new RuntimeException("Unexpected error while trying to create json form Form post: ", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue(final String name) {
|
||||||
|
final FormFieldAccessor<?> formFieldAccessor = this.formFields.get(name);
|
||||||
|
if (formFieldAccessor != null) {
|
||||||
|
return formFieldAccessor.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putStatic(final String name, final String value) {
|
||||||
|
this.objectRoot.put(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addToGroup(final String groupName, final String fieldName) {
|
||||||
|
if (this.formFields.containsKey(fieldName)) {
|
||||||
|
this.groups.computeIfAbsent(groupName, k -> new HashSet<>())
|
||||||
|
.add(fieldName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Form putField(final String name, final Label label, final Label field) {
|
||||||
|
this.formFields.put(name, createAccessor(label, field));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Form putField(final String name, final Label label, final Text field) {
|
||||||
|
this.formFields.put(name, createAccessor(label, field));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putField(final String name, final Label label, final Combo field) {
|
||||||
|
if (field instanceof SingleSelection) {
|
||||||
|
this.formFields.put(name, createAccessor(label, (SingleSelection) field));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putSubForm(final String name, final Form form) {
|
||||||
|
this.subForms.put(name, form);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Form getSubForm(final String name) {
|
||||||
|
return this.subForms.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addSubForm(final String arrayName, final Form form) {
|
||||||
|
final List<Form> array = this.subLists.computeIfAbsent(arrayName, k -> new ArrayList<>());
|
||||||
|
array.add(form);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Form getSubForm(final String arrayName, final int index) {
|
||||||
|
final List<Form> array = this.subLists.get(arrayName);
|
||||||
|
if (array == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void allVisible() {
|
||||||
|
process(
|
||||||
|
name -> true,
|
||||||
|
ffa -> ffa.setVisible(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVisible(final boolean visible, final String group) {
|
||||||
|
if (!this.groups.containsKey(group)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Set<String> namesSet = this.groups.get(group);
|
||||||
|
process(
|
||||||
|
name -> namesSet.contains(name),
|
||||||
|
ffa -> ffa.setVisible(visible));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void process(
|
||||||
|
final Predicate<String> nameFilter,
|
||||||
|
final Consumer<FormFieldAccessor<?>> processor) {
|
||||||
|
|
||||||
|
this.formFields.entrySet()
|
||||||
|
.stream()
|
||||||
|
.filter(entity -> nameFilter.test(entity.getKey()))
|
||||||
|
.map(entity -> entity.getValue())
|
||||||
|
.forEach(processor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void flush() {
|
||||||
|
for (final Map.Entry<String, FormFieldAccessor<?>> entry : this.formFields.entrySet()) {
|
||||||
|
final FormFieldAccessor<?> accessor = entry.getValue();
|
||||||
|
if (accessor.control.isVisible()) {
|
||||||
|
this.objectRoot.put(entry.getKey(), accessor.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final Map.Entry<String, Form> entry : this.subForms.entrySet()) {
|
||||||
|
final Form subForm = entry.getValue();
|
||||||
|
subForm.flush();
|
||||||
|
final ObjectNode objectNode = this.jsonMapper.createObjectNode();
|
||||||
|
this.objectRoot.set(entry.getKey(), objectNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final Map.Entry<String, List<Form>> entry : this.subLists.entrySet()) {
|
||||||
|
final List<Form> value = entry.getValue();
|
||||||
|
final ArrayNode arrayNode = this.jsonMapper.createArrayNode();
|
||||||
|
final int index = 0;
|
||||||
|
for (final Form arrayForm : value) {
|
||||||
|
arrayForm.flush();
|
||||||
|
arrayNode.insert(index, arrayForm.objectRoot);
|
||||||
|
}
|
||||||
|
this.objectRoot.set(entry.getKey(), arrayNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
private FormFieldAccessor<?> createAccessor(final Label label, final Label field) {
|
||||||
|
return new FormFieldAccessor<>(label, field) {
|
||||||
|
@Override public String getValue() { return field.getText(); }
|
||||||
|
@Override public void setValue(final String value) { field.setText(value); }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
private FormFieldAccessor<Text> createAccessor(final Label label, final Text text) {
|
||||||
|
return new FormFieldAccessor<>(label, text) {
|
||||||
|
@Override public String getValue() { return text.getText(); }
|
||||||
|
@Override public void setValue(final String value) { text.setText(value); }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
private FormFieldAccessor<SingleSelection> createAccessor(
|
||||||
|
final Label label,
|
||||||
|
final SingleSelection singleSelection) {
|
||||||
|
|
||||||
|
return new FormFieldAccessor<>(label, singleSelection) {
|
||||||
|
@Override public String getValue() { return singleSelection.getSelectionValue(); }
|
||||||
|
@Override public void setValue(final String value) { singleSelection.select(value); }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
public static abstract class FormFieldAccessor<T extends Control> {
|
||||||
|
|
||||||
|
public final Label label;
|
||||||
|
public final T control;
|
||||||
|
private boolean hasError;
|
||||||
|
|
||||||
|
public FormFieldAccessor(final Label label, final T control) {
|
||||||
|
this.label = label;
|
||||||
|
this.control = control;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract String getValue();
|
||||||
|
|
||||||
|
public abstract void setValue(String value);
|
||||||
|
|
||||||
|
public void setVisible(final boolean visible) {
|
||||||
|
this.label.setVisible(visible);
|
||||||
|
this.control.setVisible(visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setError(final String errorTooltip) {
|
||||||
|
if (!this.hasError) {
|
||||||
|
this.control.setData(RWT.CUSTOM_VARIANT, "error");
|
||||||
|
//this.control.setBackground(new Color(this.control.getDisplay(), 255, 0, 0, 50));
|
||||||
|
this.control.setToolTipText(errorTooltip);
|
||||||
|
this.hasError = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetError() {
|
||||||
|
if (this.hasError) {
|
||||||
|
this.control.setData(RWT.CUSTOM_VARIANT, null);
|
||||||
|
//this.control.setBackground(new Color(this.control.getDisplay(), 0, 0, 0, 0));
|
||||||
|
this.control.setToolTipText(null);
|
||||||
|
this.hasError = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,188 @@
|
||||||
|
/*
|
||||||
|
* 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.form;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.eclipse.swt.SWT;
|
||||||
|
import org.eclipse.swt.layout.GridData;
|
||||||
|
import org.eclipse.swt.layout.GridLayout;
|
||||||
|
import org.eclipse.swt.widgets.Combo;
|
||||||
|
import org.eclipse.swt.widgets.Composite;
|
||||||
|
import org.eclipse.swt.widgets.Label;
|
||||||
|
import org.eclipse.swt.widgets.TabItem;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.util.Tuple;
|
||||||
|
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.remote.webservice.api.RestCall;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory;
|
||||||
|
|
||||||
|
public class FormBuilder {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(FormBuilder.class);
|
||||||
|
|
||||||
|
private final WidgetFactory widgetFactory;
|
||||||
|
private final PolyglotPageService polyglotPageService;
|
||||||
|
public final PageContext pageContext;
|
||||||
|
public final Composite formParent;
|
||||||
|
public final Form form;
|
||||||
|
|
||||||
|
private boolean readonly = false;
|
||||||
|
|
||||||
|
FormBuilder(
|
||||||
|
final JSONMapper jsonMapper,
|
||||||
|
final WidgetFactory widgetFactory,
|
||||||
|
final PolyglotPageService polyglotPageService,
|
||||||
|
final PageContext pageContext,
|
||||||
|
final int rows) {
|
||||||
|
|
||||||
|
this.widgetFactory = widgetFactory;
|
||||||
|
this.polyglotPageService = polyglotPageService;
|
||||||
|
this.pageContext = pageContext;
|
||||||
|
this.form = new Form(jsonMapper);
|
||||||
|
|
||||||
|
this.formParent = new Composite(pageContext.getParent(), SWT.NONE);
|
||||||
|
final GridLayout layout = new GridLayout(rows, true);
|
||||||
|
layout.horizontalSpacing = 10;
|
||||||
|
layout.verticalSpacing = 10;
|
||||||
|
layout.marginLeft = 10;
|
||||||
|
layout.marginTop = 10;
|
||||||
|
this.formParent.setLayout(layout);
|
||||||
|
this.formParent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public FormBuilder readonly(final boolean readonly) {
|
||||||
|
this.readonly = readonly;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FormBuilder setVisible(final boolean visible, final String group) {
|
||||||
|
this.form.setVisible(visible, group);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FormBuilder setControl(final TabItem instTab) {
|
||||||
|
instTab.setControl(this.formParent);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FormBuilder addEmptyCell() {
|
||||||
|
return addEmptyCell(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FormBuilder addEmptyCell(final int span) {
|
||||||
|
this.widgetFactory.formEmpty(this.formParent, span, 1);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FormBuilder putStaticValue(final String name, final String value) {
|
||||||
|
try {
|
||||||
|
this.form.putStatic(name, value);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
log.error("Failed to put static field value to json object: ", e);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FormBuilder addTextField(
|
||||||
|
final String name,
|
||||||
|
final String label,
|
||||||
|
final String value) {
|
||||||
|
|
||||||
|
return addTextField(name, label, value, 1, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FormBuilder addTextField(
|
||||||
|
final String name,
|
||||||
|
final String label,
|
||||||
|
final String value,
|
||||||
|
final int span) {
|
||||||
|
|
||||||
|
return addTextField(name, label, value, span, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FormBuilder addTextField(
|
||||||
|
final String name,
|
||||||
|
final String label,
|
||||||
|
final String value,
|
||||||
|
final int span,
|
||||||
|
final String group) {
|
||||||
|
|
||||||
|
final Label lab = this.widgetFactory.formLabelLocalized(this.formParent, label);
|
||||||
|
if (this.readonly) {
|
||||||
|
this.form.putField(name, lab, this.widgetFactory.formValueLabel(this.formParent, value, span));
|
||||||
|
} else {
|
||||||
|
this.form.putField(name, lab, this.widgetFactory.formTextInput(this.formParent, value, span, 1));
|
||||||
|
}
|
||||||
|
if (StringUtils.isNoneBlank(group)) {
|
||||||
|
this.form.addToGroup(group, name);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FormBuilder addSingleSelection(
|
||||||
|
final String name,
|
||||||
|
final String label,
|
||||||
|
final String value,
|
||||||
|
final List<Tuple<String>> items,
|
||||||
|
final Consumer<Form> selectionListener) {
|
||||||
|
|
||||||
|
return addSingleSelection(name, label, value, items, selectionListener, 1, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FormBuilder addSingleSelection(
|
||||||
|
final String name,
|
||||||
|
final String label,
|
||||||
|
final String value,
|
||||||
|
final List<Tuple<String>> items,
|
||||||
|
final Consumer<Form> selectionListener,
|
||||||
|
final int span) {
|
||||||
|
|
||||||
|
return addSingleSelection(name, label, value, items, selectionListener, span, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FormBuilder addSingleSelection(
|
||||||
|
final String name,
|
||||||
|
final String label,
|
||||||
|
final String value,
|
||||||
|
final List<Tuple<String>> items,
|
||||||
|
final Consumer<Form> selectionListener,
|
||||||
|
final int span,
|
||||||
|
final String group) {
|
||||||
|
|
||||||
|
final Label lab = this.widgetFactory.formLabelLocalized(this.formParent, label);
|
||||||
|
if (this.readonly) {
|
||||||
|
this.form.putField(name, lab, this.widgetFactory.formValueLabel(this.formParent, value, 2));
|
||||||
|
} else {
|
||||||
|
final Combo selection =
|
||||||
|
this.widgetFactory.formSingleSelectionLocalized(this.formParent, value, items, span, 1);
|
||||||
|
this.form.putField(name, lab, selection);
|
||||||
|
if (selectionListener != null) {
|
||||||
|
selection.addListener(SWT.Selection, e -> {
|
||||||
|
selectionListener.accept(this.form);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (StringUtils.isNoneBlank(group)) {
|
||||||
|
this.form.addToGroup(group, name);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> FormHandle<T> buildFor(final RestCall<T> post) {
|
||||||
|
return new FormHandle<>(this.pageContext, this.form, post, this.polyglotPageService.getI18nSupport());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
* 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.form;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
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.PageContext;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.page.action.ActionDefinition;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.page.form.Form.FormFieldAccessor;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.page.validation.FieldValidationError;
|
||||||
|
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> {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(FormHandle.class);
|
||||||
|
|
||||||
|
public static final String FIELD_VALIDATION_LOCTEXT_PREFIX = "org.sebserver.form.validation.fieldError.";
|
||||||
|
|
||||||
|
private final PageContext pageContext;
|
||||||
|
private final Form form;
|
||||||
|
private final RestCall<T> post;
|
||||||
|
private final I18nSupport i18nSupport;
|
||||||
|
|
||||||
|
FormHandle(
|
||||||
|
final PageContext pageContext,
|
||||||
|
final Form form,
|
||||||
|
final RestCall<T> post,
|
||||||
|
final I18nSupport i18nSupport) {
|
||||||
|
|
||||||
|
this.pageContext = pageContext;
|
||||||
|
this.form = form;
|
||||||
|
this.post = post;
|
||||||
|
this.i18nSupport = i18nSupport;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doAPIPost(final ActionDefinition action) {
|
||||||
|
this.form.process(
|
||||||
|
name -> true,
|
||||||
|
fieldAccessor -> fieldAccessor.resetError());
|
||||||
|
|
||||||
|
this.post
|
||||||
|
.newBuilder()
|
||||||
|
.withFormBinding(this.form)
|
||||||
|
.call()
|
||||||
|
.map(result -> {
|
||||||
|
this.pageContext.publishPageEvent(new ActionEvent(action, result));
|
||||||
|
return result;
|
||||||
|
}).onErrorDo(error -> {
|
||||||
|
if (error instanceof RestCallError) {
|
||||||
|
((RestCallError) error)
|
||||||
|
.getErrorMessages()
|
||||||
|
.stream()
|
||||||
|
.map(FieldValidationError::new)
|
||||||
|
.forEach(fve -> this.form.process(
|
||||||
|
name -> name.equals(fve.fieldName),
|
||||||
|
fieldAccessor -> showValidationError(fieldAccessor, fve)));
|
||||||
|
} else {
|
||||||
|
log.error("Unexpected error while trying to post form: ", error);
|
||||||
|
this.pageContext.notifyError(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private final void showValidationError(
|
||||||
|
final FormFieldAccessor<?> fieldAccessor,
|
||||||
|
final FieldValidationError valError) {
|
||||||
|
|
||||||
|
fieldAccessor.setError(this.i18nSupport.getText(new LocTextKey(
|
||||||
|
FIELD_VALIDATION_LOCTEXT_PREFIX + valError.errorType,
|
||||||
|
(Object[]) valError.attributes)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public FormHandle<T> process(final Consumer<Form> consumer) {
|
||||||
|
consumer.accept(this.form);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* 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.form;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
|
||||||
|
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.widget.WidgetFactory;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Component
|
||||||
|
public class PageFormService {
|
||||||
|
|
||||||
|
private final JSONMapper jsonMapper;
|
||||||
|
private final WidgetFactory widgetFactory;
|
||||||
|
private final PolyglotPageService polyglotPageService;
|
||||||
|
|
||||||
|
public PageFormService(
|
||||||
|
final JSONMapper jsonMapper,
|
||||||
|
final WidgetFactory widgetFactory,
|
||||||
|
final PolyglotPageService polyglotPageService) {
|
||||||
|
|
||||||
|
this.jsonMapper = jsonMapper;
|
||||||
|
this.widgetFactory = widgetFactory;
|
||||||
|
this.polyglotPageService = polyglotPageService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FormBuilder getBuilder(final PageContext pageContext, final int rows) {
|
||||||
|
return new FormBuilder(
|
||||||
|
this.jsonMapper,
|
||||||
|
this.widgetFactory,
|
||||||
|
this.polyglotPageService,
|
||||||
|
pageContext,
|
||||||
|
rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WidgetFactory getWidgetFactory() {
|
||||||
|
return this.widgetFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PolyglotPageService getPolyglotPageService() {
|
||||||
|
return this.polyglotPageService;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -20,13 +20,14 @@ import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||||
import ch.ethz.seb.sebserver.gui.service.RWTUtils;
|
|
||||||
import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
|
import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.ComposerService;
|
import ch.ethz.seb.sebserver.gui.service.page.ComposerService;
|
||||||
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.PageDefinition;
|
import ch.ethz.seb.sebserver.gui.service.page.PageDefinition;
|
||||||
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.RestService;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory;
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
@Service
|
@Service
|
||||||
|
@ -40,17 +41,20 @@ public class ComposerServiceImpl implements ComposerService {
|
||||||
private final Class<? extends PageDefinition> mainPageType = DefaultMainPage.class;
|
private final Class<? extends PageDefinition> mainPageType = DefaultMainPage.class;
|
||||||
|
|
||||||
final AuthorizationContextHolder authorizationContextHolder;
|
final AuthorizationContextHolder authorizationContextHolder;
|
||||||
|
private final RestService restService;
|
||||||
private final I18nSupport i18nSupport;
|
private final I18nSupport i18nSupport;
|
||||||
private final Map<String, TemplateComposer> composer;
|
private final Map<String, TemplateComposer> composer;
|
||||||
private final Map<String, PageDefinition> pages;
|
private final Map<String, PageDefinition> pages;
|
||||||
|
|
||||||
public ComposerServiceImpl(
|
public ComposerServiceImpl(
|
||||||
final AuthorizationContextHolder authorizationContextHolder,
|
final AuthorizationContextHolder authorizationContextHolder,
|
||||||
|
final RestService restService,
|
||||||
final I18nSupport i18nSupport,
|
final I18nSupport i18nSupport,
|
||||||
final Collection<TemplateComposer> composer,
|
final Collection<TemplateComposer> composer,
|
||||||
final Collection<PageDefinition> pageDefinitions) {
|
final Collection<PageDefinition> pageDefinitions) {
|
||||||
|
|
||||||
this.authorizationContextHolder = authorizationContextHolder;
|
this.authorizationContextHolder = authorizationContextHolder;
|
||||||
|
this.restService = restService;
|
||||||
this.i18nSupport = i18nSupport;
|
this.i18nSupport = i18nSupport;
|
||||||
this.composer = composer
|
this.composer = composer
|
||||||
.stream()
|
.stream()
|
||||||
|
@ -108,7 +112,7 @@ public class ComposerServiceImpl implements ComposerService {
|
||||||
|
|
||||||
if (composer.validate(pageContext)) {
|
if (composer.validate(pageContext)) {
|
||||||
|
|
||||||
RWTUtils.clearComposite(pageContext.getParent());
|
WidgetFactory.clearComposite(pageContext.getParent());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
composer.compose(pageContext);
|
composer.compose(pageContext);
|
||||||
|
@ -169,8 +173,7 @@ public class ComposerServiceImpl implements ComposerService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private PageContext createPageContext(final Composite root) {
|
private PageContext createPageContext(final Composite root) {
|
||||||
return new PageContextImpl(
|
return new PageContextImpl(this.restService, this.i18nSupport, this, root, root, null);
|
||||||
this.i18nSupport, this, root, root, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys;
|
||||||
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.auth.AuthorizationContextHolder;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder;
|
||||||
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory;
|
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory.CustomVariant;
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
@Component
|
@Component
|
||||||
|
@ -243,11 +244,17 @@ public class DefaultPageLayout implements TemplateComposer {
|
||||||
rowLayout.marginRight = 20;
|
rowLayout.marginRight = 20;
|
||||||
footerRight.setLayout(rowLayout);
|
footerRight.setLayout(rowLayout);
|
||||||
|
|
||||||
this.widgetFactory.labelLocalized(footerLeft, "footer", new LocTextKey("sebserver.overall.imprint"));
|
this.widgetFactory.labelLocalized(
|
||||||
this.widgetFactory.labelLocalized(footerLeft, "footer", new LocTextKey("sebserver.overall.about"));
|
footerLeft,
|
||||||
|
CustomVariant.FOOTER,
|
||||||
|
new LocTextKey("sebserver.overall.imprint"));
|
||||||
|
this.widgetFactory.labelLocalized(
|
||||||
|
footerLeft,
|
||||||
|
CustomVariant.FOOTER,
|
||||||
|
new LocTextKey("sebserver.overall.about"));
|
||||||
this.widgetFactory.labelLocalized(
|
this.widgetFactory.labelLocalized(
|
||||||
footerRight,
|
footerRight,
|
||||||
"footer",
|
CustomVariant.FOOTER,
|
||||||
new LocTextKey("sebserver.overall.version", this.sebServerVersion));
|
new LocTextKey("sebserver.overall.version", this.sebServerVersion));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,8 +29,13 @@ import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.ComposerService;
|
import ch.ethz.seb.sebserver.gui.service.page.ComposerService;
|
||||||
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.PageDefinition;
|
import ch.ethz.seb.sebserver.gui.service.page.PageDefinition;
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.PageEventListener;
|
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.action.ActionDefinition;
|
||||||
|
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.PageEvent;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.page.event.PageEventListener;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
||||||
import ch.ethz.seb.sebserver.gui.service.widget.Message;
|
import ch.ethz.seb.sebserver.gui.service.widget.Message;
|
||||||
|
|
||||||
public class PageContextImpl implements PageContext {
|
public class PageContextImpl implements PageContext {
|
||||||
|
@ -39,6 +44,7 @@ public class PageContextImpl implements PageContext {
|
||||||
|
|
||||||
private static final ListenerComparator LIST_COMPARATOR = new ListenerComparator();
|
private static final ListenerComparator LIST_COMPARATOR = new ListenerComparator();
|
||||||
|
|
||||||
|
private final RestService restService;
|
||||||
private final I18nSupport i18nSupport;
|
private final I18nSupport i18nSupport;
|
||||||
private final ComposerService composerService;
|
private final ComposerService composerService;
|
||||||
private final Composite root;
|
private final Composite root;
|
||||||
|
@ -46,12 +52,14 @@ public class PageContextImpl implements PageContext {
|
||||||
private final Map<String, String> attributes;
|
private final Map<String, String> attributes;
|
||||||
|
|
||||||
PageContextImpl(
|
PageContextImpl(
|
||||||
|
final RestService restService,
|
||||||
final I18nSupport i18nSupport,
|
final I18nSupport i18nSupport,
|
||||||
final ComposerService composerService,
|
final ComposerService composerService,
|
||||||
final Composite root,
|
final Composite root,
|
||||||
final Composite parent,
|
final Composite parent,
|
||||||
final Map<String, String> attributes) {
|
final Map<String, String> attributes) {
|
||||||
|
|
||||||
|
this.restService = restService;
|
||||||
this.i18nSupport = i18nSupport;
|
this.i18nSupport = i18nSupport;
|
||||||
this.composerService = composerService;
|
this.composerService = composerService;
|
||||||
this.root = root;
|
this.root = root;
|
||||||
|
@ -86,6 +94,7 @@ public class PageContextImpl implements PageContext {
|
||||||
@Override
|
@Override
|
||||||
public PageContext copyOf(final Composite parent) {
|
public PageContext copyOf(final Composite parent) {
|
||||||
return new PageContextImpl(
|
return new PageContextImpl(
|
||||||
|
this.restService,
|
||||||
this.i18nSupport,
|
this.i18nSupport,
|
||||||
this.composerService,
|
this.composerService,
|
||||||
this.root,
|
this.root,
|
||||||
|
@ -99,6 +108,7 @@ public class PageContextImpl implements PageContext {
|
||||||
attrs.putAll(this.attributes);
|
attrs.putAll(this.attributes);
|
||||||
attrs.putAll(((PageContextImpl) otherContext).attributes);
|
attrs.putAll(((PageContextImpl) otherContext).attributes);
|
||||||
return new PageContextImpl(
|
return new PageContextImpl(
|
||||||
|
this.restService,
|
||||||
this.i18nSupport,
|
this.i18nSupport,
|
||||||
this.composerService,
|
this.composerService,
|
||||||
this.root,
|
this.root,
|
||||||
|
@ -112,10 +122,31 @@ public class PageContextImpl implements PageContext {
|
||||||
attrs.putAll(this.attributes);
|
attrs.putAll(this.attributes);
|
||||||
attrs.put(key, value);
|
attrs.put(key, value);
|
||||||
return new PageContextImpl(
|
return new PageContextImpl(
|
||||||
|
this.restService,
|
||||||
this.i18nSupport,
|
this.i18nSupport,
|
||||||
this.composerService,
|
this.composerService,
|
||||||
this.root,
|
this.root,
|
||||||
this.parent, attrs);
|
this.parent,
|
||||||
|
attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PageContext withSelection(final ActivitySelection selection) {
|
||||||
|
if (selection == null) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<String, String> attrs = new HashMap<>();
|
||||||
|
attrs.putAll(this.attributes);
|
||||||
|
attrs.putAll(selection.getAttributes());
|
||||||
|
|
||||||
|
return new PageContextImpl(
|
||||||
|
this.restService,
|
||||||
|
this.i18nSupport,
|
||||||
|
this.composerService,
|
||||||
|
this.root,
|
||||||
|
this.parent,
|
||||||
|
attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -160,6 +191,11 @@ public class PageContextImpl implements PageContext {
|
||||||
.forEach(listener -> listener.notify(event));
|
.forEach(listener -> listener.notify(event));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Action createAction(final ActionDefinition actionDefinition) {
|
||||||
|
return new Action(actionDefinition, this, this.restService);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public void applyConfirmDialog(final String confirmMessage, final Runnable onOK) {
|
public void applyConfirmDialog(final String confirmMessage, final Runnable onOK) {
|
||||||
|
@ -212,6 +248,17 @@ public class PageContextImpl implements PageContext {
|
||||||
forwardToPage(this.composerService.loginPage(), pageContext);
|
forwardToPage(this.composerService.loginPage(), pageContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void publishPageMessage(final PageMessageException pme) {
|
||||||
|
final MessageBox messageBox = new Message(
|
||||||
|
getShell(),
|
||||||
|
this.i18nSupport.getText("sebserver.page.message"),
|
||||||
|
this.i18nSupport.getText(pme.getMessage()),
|
||||||
|
SWT.NONE);
|
||||||
|
messageBox.setMarkupEnabled(true);
|
||||||
|
messageBox.open(null);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void notifyError(final String errorMessage, final Throwable error) {
|
public void notifyError(final String errorMessage, final Throwable error) {
|
||||||
if (error instanceof APIMessageError) {
|
if (error instanceof APIMessageError) {
|
||||||
|
|
|
@ -60,8 +60,8 @@ public class SEBLogin implements TemplateComposer {
|
||||||
if (pageContext.hasAttribute((AttributeKeys.LGOUT_SUCCESS))) {
|
if (pageContext.hasAttribute((AttributeKeys.LGOUT_SUCCESS))) {
|
||||||
final MessageBox logoutSuccess = new Message(
|
final MessageBox logoutSuccess = new Message(
|
||||||
pageContext.getShell(),
|
pageContext.getShell(),
|
||||||
this.i18nSupport.getText("org.sebserver.logout"),
|
this.i18nSupport.getText("sebserver.logout"),
|
||||||
this.i18nSupport.getText("org.sebserver.logout.success.message"),
|
this.i18nSupport.getText("sebserver.logout.success.message"),
|
||||||
SWT.ICON_INFORMATION);
|
SWT.ICON_INFORMATION);
|
||||||
logoutSuccess.open(null);
|
logoutSuccess.open(null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,11 +23,11 @@ import org.springframework.stereotype.Component;
|
||||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||||
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.PageEventListener;
|
|
||||||
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.page.activity.ActivitiesPane;
|
import ch.ethz.seb.sebserver.gui.service.page.activity.ActivitiesPane;
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.event.ActivitySelectionEvent;
|
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.ActivitySelectionListener;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.page.event.PageEventListener;
|
||||||
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory;
|
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory;
|
||||||
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory.IconButtonType;
|
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory.IconButtonType;
|
||||||
|
|
||||||
|
@ -128,9 +128,9 @@ public class SEBMainPage implements TemplateComposer {
|
||||||
public void notify(final ActivitySelectionEvent event) {
|
public void notify(final ActivitySelectionEvent event) {
|
||||||
pageContext.composerService().compose(
|
pageContext.composerService().compose(
|
||||||
event.selection.activity.contentPaneComposer,
|
event.selection.activity.contentPaneComposer,
|
||||||
pageContext.copyOf(contentObjects).withAttr(
|
pageContext
|
||||||
event.selection.activity.objectIdentifierAttribute,
|
.copyOf(contentObjects)
|
||||||
event.selection.getObjectIdentifier()));
|
.withSelection(event.selection));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -150,9 +150,9 @@ public class SEBMainPage implements TemplateComposer {
|
||||||
public void notify(final ActivitySelectionEvent event) {
|
public void notify(final ActivitySelectionEvent event) {
|
||||||
pageContext.composerService().compose(
|
pageContext.composerService().compose(
|
||||||
event.selection.activity.actionPaneComposer,
|
event.selection.activity.actionPaneComposer,
|
||||||
pageContext.copyOf(actionPane).withAttr(
|
pageContext
|
||||||
event.selection.activity.objectIdentifierAttribute,
|
.copyOf(actionPane)
|
||||||
event.selection.getObjectIdentifier()));
|
.withSelection(event.selection));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* 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.validation;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
|
||||||
|
|
||||||
|
public class FieldValidationError {
|
||||||
|
|
||||||
|
public final String messageCode;
|
||||||
|
public final String domainName;
|
||||||
|
public final String fieldName;
|
||||||
|
public final String errorType;
|
||||||
|
public final String[] attributes;
|
||||||
|
|
||||||
|
public FieldValidationError(final APIMessage apiMessage) {
|
||||||
|
this(
|
||||||
|
apiMessage.messageCode,
|
||||||
|
apiMessage.attributes.toArray(new String[apiMessage.attributes.size()]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public FieldValidationError(
|
||||||
|
final String messageCode,
|
||||||
|
final String[] attributes) {
|
||||||
|
|
||||||
|
this.messageCode = messageCode;
|
||||||
|
this.attributes = attributes;
|
||||||
|
|
||||||
|
this.domainName = (attributes != null && attributes.length > 0) ? attributes[0] : null;
|
||||||
|
this.fieldName = (attributes != null && attributes.length > 1) ? attributes[1] : null;
|
||||||
|
this.errorType = (attributes != null && attributes.length > 2) ? attributes[2] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
public interface FormBinding {
|
||||||
|
|
||||||
|
String getFormAsJson();
|
||||||
|
|
||||||
|
}
|
|
@ -66,6 +66,9 @@ public abstract class RestCall<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Result<T> exchange(final RestCallBuilder builder) {
|
protected Result<T> exchange(final RestCallBuilder builder) {
|
||||||
|
|
||||||
|
log.debug("Call webservice API on {} for {}", this.path, builder);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final ResponseEntity<String> responseEntity = RestCall.this.restService
|
final ResponseEntity<String> responseEntity = RestCall.this.restService
|
||||||
.getWebserviceAPIRestTemplate()
|
.getWebserviceAPIRestTemplate()
|
||||||
|
@ -90,6 +93,11 @@ public abstract class RestCall<T> {
|
||||||
responseEntity.getBody(),
|
responseEntity.getBody(),
|
||||||
new TypeReference<List<APIMessage>>() {
|
new TypeReference<List<APIMessage>>() {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
log.debug(
|
||||||
|
"Webservice answered with well defined error- or validation-failure-response: ",
|
||||||
|
restCallError);
|
||||||
|
|
||||||
return Result.ofError(restCallError);
|
return Result.ofError(restCallError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +110,7 @@ public abstract class RestCall<T> {
|
||||||
new TypeReference<List<APIMessage>>() {
|
new TypeReference<List<APIMessage>>() {
|
||||||
}));
|
}));
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
log.error("Unable to handle rest call error: ", e);
|
log.error("Unexpected error-response while webservice API call for: {}", builder, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result.ofError(restCallError);
|
return Result.ofError(restCallError);
|
||||||
|
@ -180,6 +188,11 @@ public abstract class RestCall<T> {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RestCallBuilder withFormBinding(final FormBinding formBinding) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public RestCallBuilder onlyActive(final boolean active) {
|
public RestCallBuilder onlyActive(final boolean active) {
|
||||||
this.queryParams.put(Entity.FILTER_ATTR_ACTIVE, Arrays.asList(String.valueOf(active)));
|
this.queryParams.put(Entity.FILTER_ATTR_ACTIVE, Arrays.asList(String.valueOf(active)));
|
||||||
return this;
|
return this;
|
||||||
|
@ -204,6 +217,13 @@ public abstract class RestCall<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "RestCallBuilder [httpHeaders=" + this.httpHeaders + ", body=" + this.body + ", queryParams="
|
||||||
|
+ this.queryParams
|
||||||
|
+ ", uriVariables=" + this.uriVariables + "]";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,4 +37,8 @@ public class RestCallError extends RuntimeException implements APIMessageError {
|
||||||
return !this.errors.isEmpty();
|
return !this.errors.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "RestCallError [errors=" + this.errors + "]";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,6 @@ import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
@ -29,8 +27,6 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.WebserviceURISer
|
||||||
@GuiProfile
|
@GuiProfile
|
||||||
public class RestService {
|
public class RestService {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(RestService.class);
|
|
||||||
|
|
||||||
private final AuthorizationContextHolder authorizationContextHolder;
|
private final AuthorizationContextHolder authorizationContextHolder;
|
||||||
private final WebserviceURIService webserviceURIBuilderSupplier;
|
private final WebserviceURIService webserviceURIBuilderSupplier;
|
||||||
private final Map<String, RestCall<?>> calls;
|
private final Map<String, RestCall<?>> calls;
|
||||||
|
|
|
@ -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.institution;
|
||||||
|
|
||||||
|
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.SEBServerRestEndpoints;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.institution.Institution;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Component
|
||||||
|
@GuiProfile
|
||||||
|
public class GetInstitution extends RestCall<Institution> {
|
||||||
|
|
||||||
|
protected GetInstitution() {
|
||||||
|
super(
|
||||||
|
new TypeReference<Institution>() {
|
||||||
|
},
|
||||||
|
HttpMethod.GET,
|
||||||
|
MediaType.APPLICATION_FORM_URLENCODED,
|
||||||
|
SEBServerRestEndpoints.ENDPOINT_INSTITUTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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.institution;
|
||||||
|
|
||||||
|
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.SEBServerRestEndpoints;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.institution.Institution;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Component
|
||||||
|
@GuiProfile
|
||||||
|
public class NewInstitution extends RestCall<Institution> {
|
||||||
|
|
||||||
|
protected NewInstitution() {
|
||||||
|
super(
|
||||||
|
new TypeReference<Institution>() {
|
||||||
|
},
|
||||||
|
HttpMethod.POST,
|
||||||
|
MediaType.APPLICATION_FORM_URLENCODED,
|
||||||
|
SEBServerRestEndpoints.ENDPOINT_INSTITUTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.gui.service.remote.webservice.auth;
|
package ch.ethz.seb.sebserver.gui.service.remote.webservice.auth;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -25,9 +26,11 @@ import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||||
|
import org.springframework.http.client.ClientHttpResponse;
|
||||||
import org.springframework.security.access.AccessDeniedException;
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext;
|
import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext;
|
||||||
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
|
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
|
||||||
|
import org.springframework.security.oauth2.client.http.OAuth2ErrorHandler;
|
||||||
import org.springframework.security.oauth2.client.resource.OAuth2AccessDeniedException;
|
import org.springframework.security.oauth2.client.resource.OAuth2AccessDeniedException;
|
||||||
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
|
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
|
||||||
import org.springframework.security.oauth2.client.token.AccessTokenRequest;
|
import org.springframework.security.oauth2.client.token.AccessTokenRequest;
|
||||||
|
@ -162,6 +165,14 @@ public class OAuth2AuthorizationContextHolder implements AuthorizationContextHol
|
||||||
|
|
||||||
this.restTemplate = new DisposableOAuth2RestTemplate(this.resource);
|
this.restTemplate = new DisposableOAuth2RestTemplate(this.resource);
|
||||||
this.restTemplate.setRequestFactory(clientHttpRequestFactory);
|
this.restTemplate.setRequestFactory(clientHttpRequestFactory);
|
||||||
|
this.restTemplate.setErrorHandler(new OAuth2ErrorHandler(this.resource) {
|
||||||
|
@Override
|
||||||
|
public boolean hasError(final ClientHttpResponse response) throws IOException {
|
||||||
|
final HttpStatus statusCode = HttpStatus.resolve(response.getRawStatusCode());
|
||||||
|
return (statusCode != null && statusCode.series() == HttpStatus.Series.SERVER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
this.revokeTokenURI = webserviceURIService.getOAuthRevokeTokenURI();
|
this.revokeTokenURI = webserviceURIService.getOAuthRevokeTokenURI();
|
||||||
this.currentUserURI = webserviceURIService.getCurrentUserRequestURI();
|
this.currentUserURI = webserviceURIService.getCurrentUserRequestURI();
|
||||||
|
|
|
@ -40,6 +40,15 @@ public final class ColumnDefinition<ROW extends Entity> {
|
||||||
this(columnName, displayName, null, widthPercent, null, null, false);
|
this(columnName, displayName, null, widthPercent, null, null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ColumnDefinition(
|
||||||
|
final String columnName,
|
||||||
|
final LocTextKey displayName,
|
||||||
|
final Function<ROW, Object> valueSupplier,
|
||||||
|
final boolean sortable) {
|
||||||
|
|
||||||
|
this(columnName, displayName, null, -1, valueSupplier, null, sortable);
|
||||||
|
}
|
||||||
|
|
||||||
public ColumnDefinition(
|
public ColumnDefinition(
|
||||||
final String columnName,
|
final String columnName,
|
||||||
final LocTextKey displayName,
|
final LocTextKey displayName,
|
||||||
|
|
|
@ -8,7 +8,11 @@
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.gui.service.table;
|
package ch.ethz.seb.sebserver.gui.service.table;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.eclipse.swt.SWT;
|
import org.eclipse.swt.SWT;
|
||||||
import org.eclipse.swt.layout.GridData;
|
import org.eclipse.swt.layout.GridData;
|
||||||
|
@ -46,8 +50,6 @@ public class EntityTable<ROW extends Entity> extends Composite {
|
||||||
private final Table table;
|
private final Table table;
|
||||||
private final TableNavigator navigator;
|
private final TableNavigator navigator;
|
||||||
|
|
||||||
private final boolean selectableRows;
|
|
||||||
|
|
||||||
private int pageNumber = 1;
|
private int pageNumber = 1;
|
||||||
private int pageSize;
|
private int pageSize;
|
||||||
private String sortColumn = null;
|
private String sortColumn = null;
|
||||||
|
@ -56,31 +58,34 @@ public class EntityTable<ROW extends Entity> extends Composite {
|
||||||
private boolean columnsWithSameWidth = true;
|
private boolean columnsWithSameWidth = true;
|
||||||
|
|
||||||
EntityTable(
|
EntityTable(
|
||||||
|
final int type,
|
||||||
final Composite parent,
|
final Composite parent,
|
||||||
final RestCall<Page<ROW>> restCall,
|
final RestCall<Page<ROW>> restCall,
|
||||||
final WidgetFactory widgetFactory,
|
final WidgetFactory widgetFactory,
|
||||||
final List<ColumnDefinition<ROW>> columns,
|
final List<ColumnDefinition<ROW>> columns,
|
||||||
final List<TableRowAction> actions,
|
final List<TableRowAction> actions,
|
||||||
final int pageSize,
|
final int pageSize,
|
||||||
final boolean withFilter,
|
final boolean withFilter) {
|
||||||
final boolean selectableRows) {
|
|
||||||
|
|
||||||
super(parent, SWT.NONE);
|
super(parent, type);
|
||||||
this.widgetFactory = widgetFactory;
|
this.widgetFactory = widgetFactory;
|
||||||
this.restCall = restCall;
|
this.restCall = restCall;
|
||||||
this.columns = Utils.immutableListOf(columns);
|
this.columns = Utils.immutableListOf(columns);
|
||||||
this.actions = Utils.immutableListOf(actions);
|
this.actions = Utils.immutableListOf(actions);
|
||||||
|
|
||||||
super.setLayout(new GridLayout());
|
super.setLayout(new GridLayout());
|
||||||
super.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
|
GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false);
|
||||||
|
|
||||||
|
gridData.heightHint = (pageSize + 1) * 40;
|
||||||
|
super.setLayoutData(gridData);
|
||||||
|
|
||||||
this.pageSize = pageSize;
|
this.pageSize = pageSize;
|
||||||
this.filter = (withFilter) ? new TableFilter<>(this) : null;
|
this.filter = (withFilter) ? new TableFilter<>(this) : null;
|
||||||
this.selectableRows = selectableRows;
|
|
||||||
|
|
||||||
this.table = widgetFactory.tableLocalized(this);
|
this.table = widgetFactory.tableLocalized(this);
|
||||||
this.table.setLayout(new GridLayout(columns.size(), true));
|
this.table.setLayout(new GridLayout(columns.size(), true));
|
||||||
final GridData gridData = new GridData(SWT.FILL, SWT.CENTER, true, false);
|
gridData = new GridData(SWT.FILL, SWT.CENTER, true, false);
|
||||||
|
gridData.heightHint = (pageSize + 1) * 25;
|
||||||
this.table.setLayoutData(gridData);
|
this.table.setLayoutData(gridData);
|
||||||
this.table.addListener(SWT.Resize, this::adaptColumnWidth);
|
this.table.addListener(SWT.Resize, this::adaptColumnWidth);
|
||||||
|
|
||||||
|
@ -148,6 +153,18 @@ public class EntityTable<ROW extends Entity> extends Composite {
|
||||||
this.sortOrder);
|
this.sortOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Collection<String> getSelection() {
|
||||||
|
final TableItem[] selection = this.table.getSelection();
|
||||||
|
if (selection == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Arrays.asList(selection)
|
||||||
|
.stream()
|
||||||
|
.map(this::getRowDataId)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
private void createTableColumns() {
|
private void createTableColumns() {
|
||||||
for (final ColumnDefinition<ROW> column : this.columns) {
|
for (final ColumnDefinition<ROW> column : this.columns) {
|
||||||
final TableColumn tableColumn = this.widgetFactory.tableColumnLocalized(
|
final TableColumn tableColumn = this.widgetFactory.tableColumnLocalized(
|
||||||
|
@ -204,9 +221,6 @@ public class EntityTable<ROW extends Entity> extends Composite {
|
||||||
final TableItem item = new TableItem(this.table, SWT.NONE);
|
final TableItem item = new TableItem(this.table, SWT.NONE);
|
||||||
item.setData(TABLE_ROW_DATA, row);
|
item.setData(TABLE_ROW_DATA, row);
|
||||||
int index = 0;
|
int index = 0;
|
||||||
if (this.selectableRows) {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
for (final ColumnDefinition<ROW> column : this.columns) {
|
for (final ColumnDefinition<ROW> column : this.columns) {
|
||||||
final Object value = column.valueSupplier.apply(row);
|
final Object value = column.valueSupplier.apply(row);
|
||||||
if (value instanceof Boolean) {
|
if (value instanceof Boolean) {
|
||||||
|
@ -247,4 +261,13 @@ public class EntityTable<ROW extends Entity> extends Composite {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private ROW getRowData(final TableItem item) {
|
||||||
|
return (ROW) item.getData(TABLE_ROW_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getRowDataId(final TableItem item) {
|
||||||
|
return getRowData(item).getModelId();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.gui.service.table;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.eclipse.swt.SWT;
|
||||||
import org.eclipse.swt.widgets.Composite;
|
import org.eclipse.swt.widgets.Composite;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.Entity;
|
import ch.ethz.seb.sebserver.gbl.model.Entity;
|
||||||
|
@ -41,7 +42,7 @@ public class TableBuilder<ROW extends Entity> {
|
||||||
final List<TableRowAction> actions = new ArrayList<>();
|
final List<TableRowAction> actions = new ArrayList<>();
|
||||||
|
|
||||||
private int pageSize = -1;
|
private int pageSize = -1;
|
||||||
private boolean selectableRows = false;
|
private int type = SWT.NONE;
|
||||||
|
|
||||||
public TableBuilder(
|
public TableBuilder(
|
||||||
final WidgetFactory widgetFactory,
|
final WidgetFactory widgetFactory,
|
||||||
|
@ -61,13 +62,13 @@ public class TableBuilder<ROW extends Entity> {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TableBuilder<ROW> withSelectableRows() {
|
public TableBuilder<ROW> withAction(final TableRowAction action) {
|
||||||
this.selectableRows = true;
|
this.actions.add(action);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TableBuilder<ROW> withAction(final TableRowAction action) {
|
public TableBuilder<ROW> withMultiselection() {
|
||||||
this.actions.add(action);
|
this.type |= SWT.MULTI;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,14 +80,14 @@ public class TableBuilder<ROW extends Entity> {
|
||||||
.isPresent();
|
.isPresent();
|
||||||
|
|
||||||
return new EntityTable<>(
|
return new EntityTable<>(
|
||||||
|
this.type,
|
||||||
parent,
|
parent,
|
||||||
this.restCall,
|
this.restCall,
|
||||||
this.widgetFactory,
|
this.widgetFactory,
|
||||||
this.columns,
|
this.columns,
|
||||||
this.actions,
|
this.actions,
|
||||||
this.pageSize,
|
this.pageSize,
|
||||||
withFilter,
|
withFilter);
|
||||||
this.selectableRows);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ import org.eclipse.swt.widgets.Composite;
|
||||||
import org.eclipse.swt.widgets.Label;
|
import org.eclipse.swt.widgets.Label;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.Page;
|
import ch.ethz.seb.sebserver.gbl.model.Page;
|
||||||
import ch.ethz.seb.sebserver.gui.service.RWTUtils;
|
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory;
|
||||||
|
|
||||||
public class TableNavigator extends Composite {
|
public class TableNavigator extends Composite {
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ public class TableNavigator extends Composite {
|
||||||
|
|
||||||
public Page<?> update(final Page<?> pageData) {
|
public Page<?> update(final Page<?> pageData) {
|
||||||
// clear all
|
// clear all
|
||||||
RWTUtils.clearComposite(this);
|
WidgetFactory.clearComposite(this);
|
||||||
|
|
||||||
final int pageNumber = pageData.getPageNumber();
|
final int pageNumber = pageData.getPageNumber();
|
||||||
final int numberOfPages = pageData.getNumberOfPages();
|
final int numberOfPages = pageData.getNumberOfPages();
|
||||||
|
|
|
@ -25,6 +25,7 @@ import org.eclipse.swt.layout.GridData;
|
||||||
import org.eclipse.swt.widgets.Button;
|
import org.eclipse.swt.widgets.Button;
|
||||||
import org.eclipse.swt.widgets.Combo;
|
import org.eclipse.swt.widgets.Combo;
|
||||||
import org.eclipse.swt.widgets.Composite;
|
import org.eclipse.swt.widgets.Composite;
|
||||||
|
import org.eclipse.swt.widgets.Control;
|
||||||
import org.eclipse.swt.widgets.Label;
|
import org.eclipse.swt.widgets.Label;
|
||||||
import org.eclipse.swt.widgets.Listener;
|
import org.eclipse.swt.widgets.Listener;
|
||||||
import org.eclipse.swt.widgets.Table;
|
import org.eclipse.swt.widgets.Table;
|
||||||
|
@ -42,7 +43,6 @@ import ch.ethz.seb.sebserver.gbl.model.Entity;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.Page;
|
import ch.ethz.seb.sebserver.gbl.model.Page;
|
||||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Tuple;
|
import ch.ethz.seb.sebserver.gbl.util.Tuple;
|
||||||
import ch.ethz.seb.sebserver.gui.service.RWTUtils;
|
|
||||||
import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
|
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.i18n.LocTextKey;
|
||||||
import ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService;
|
import ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService;
|
||||||
|
@ -57,9 +57,27 @@ public class WidgetFactory {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(WidgetFactory.class);
|
private static final Logger log = LoggerFactory.getLogger(WidgetFactory.class);
|
||||||
|
|
||||||
|
public enum CustomVariant {
|
||||||
|
TEXT_H1("h1"),
|
||||||
|
TEXT_H2("h2"),
|
||||||
|
TEXT_H3("h3"),
|
||||||
|
TEXT_ACTION("action"),
|
||||||
|
|
||||||
|
FOOTER("footer"),
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
public final String key;
|
||||||
|
|
||||||
|
private CustomVariant(final String key) {
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public enum IconButtonType {
|
public enum IconButtonType {
|
||||||
MAXIMIZE("maximize.png"),
|
MAXIMIZE("maximize.png"),
|
||||||
MINIMIZE("minimize.png"),
|
MINIMIZE("minimize.png"),
|
||||||
|
MODIFY_ACTION("editAction.png"),
|
||||||
SAVE_ACTION("saveAction.png"),
|
SAVE_ACTION("saveAction.png"),
|
||||||
NEW_ACTION("newAction.png"),
|
NEW_ACTION("newAction.png"),
|
||||||
DELETE_ACTION("deleteAction.png"),
|
DELETE_ACTION("deleteAction.png"),
|
||||||
|
@ -108,10 +126,10 @@ public class WidgetFactory {
|
||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Button buttonLocalized(final Composite parent, final String style, final String locTextKey) {
|
public Button buttonLocalized(final Composite parent, final CustomVariant variant, final String locTextKey) {
|
||||||
final Button button = new Button(parent, SWT.NONE);
|
final Button button = new Button(parent, SWT.NONE);
|
||||||
this.injectI18n(button, new LocTextKey(locTextKey));
|
this.injectI18n(button, new LocTextKey(locTextKey));
|
||||||
button.setData(RWT.CUSTOM_VARIANT, style);
|
button.setData(RWT.CUSTOM_VARIANT, variant.key);
|
||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,10 +151,10 @@ public class WidgetFactory {
|
||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Label labelLocalized(final Composite parent, final String style, final LocTextKey locTextKey) {
|
public Label labelLocalized(final Composite parent, final CustomVariant variant, final LocTextKey locTextKey) {
|
||||||
final Label label = new Label(parent, SWT.NONE);
|
final Label label = new Label(parent, SWT.NONE);
|
||||||
this.injectI18n(label, locTextKey);
|
this.injectI18n(label, locTextKey);
|
||||||
label.setData(RWT.CUSTOM_VARIANT, style);
|
label.setData(RWT.CUSTOM_VARIANT, variant.key);
|
||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,24 +170,24 @@ public class WidgetFactory {
|
||||||
|
|
||||||
public Label labelLocalized(
|
public Label labelLocalized(
|
||||||
final Composite parent,
|
final Composite parent,
|
||||||
final String style,
|
final CustomVariant variant,
|
||||||
final LocTextKey locTextKey,
|
final LocTextKey locTextKey,
|
||||||
final LocTextKey locToolTextKey) {
|
final LocTextKey locToolTextKey) {
|
||||||
|
|
||||||
final Label label = new Label(parent, SWT.NONE);
|
final Label label = new Label(parent, SWT.NONE);
|
||||||
this.injectI18n(label, locTextKey, locToolTextKey);
|
this.injectI18n(label, locTextKey, locToolTextKey);
|
||||||
label.setData(RWT.CUSTOM_VARIANT, style);
|
label.setData(RWT.CUSTOM_VARIANT, variant.key);
|
||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Label labelLocalizedTitle(final Composite content, final LocTextKey locTextKey) {
|
public Label labelLocalizedTitle(final Composite content, final LocTextKey locTextKey) {
|
||||||
final Label labelLocalized = labelLocalized(content, RWTUtils.TEXT_NAME_H2, locTextKey);
|
final Label labelLocalized = labelLocalized(content, CustomVariant.TEXT_H1, locTextKey);
|
||||||
labelLocalized.setLayoutData(new GridData(SWT.TOP, SWT.LEFT, true, false));
|
labelLocalized.setLayoutData(new GridData(SWT.TOP, SWT.LEFT, true, false));
|
||||||
return labelLocalized;
|
return labelLocalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Tree treeLocalized(final Composite parent, final int style) {
|
public Tree treeLocalized(final Composite parent, final int style) {
|
||||||
final Tree tree = new Tree(parent, SWT.SINGLE | SWT.FULL_SELECTION);
|
final Tree tree = new Tree(parent, style);
|
||||||
this.injectI18n(tree);
|
this.injectI18n(tree);
|
||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
|
@ -381,6 +399,16 @@ public class WidgetFactory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void clearComposite(final Composite parent) {
|
||||||
|
if (parent == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final Control control : parent.getChildren()) {
|
||||||
|
control.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static Consumer<Tree> treeFunction(final I18nSupport i18nSupport) {
|
private static Consumer<Tree> treeFunction(final I18nSupport i18nSupport) {
|
||||||
return tree -> updateLocale(tree.getItems(), i18nSupport);
|
return tree -> updateLocale(tree.getItems(), i18nSupport);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@ sebserver.login.pwd=Password
|
||||||
sebserver.login.login=Sign In
|
sebserver.login.login=Sign In
|
||||||
sebserver.login.failed.title=Login Failed
|
sebserver.login.failed.title=Login Failed
|
||||||
sebserver.login.failed.message=Access Denied: Wrong username or password
|
sebserver.login.failed.message=Access Denied: Wrong username or password
|
||||||
|
sebserver.logout=Sign out
|
||||||
|
sebserver.logout.success.message=You have been successfully signed out.
|
||||||
|
|
||||||
################################
|
################################
|
||||||
# Main Page
|
# Main Page
|
||||||
|
@ -29,6 +31,7 @@ sebserver.actionpane.title=Actions
|
||||||
sebserver.activities.inst=Institution
|
sebserver.activities.inst=Institution
|
||||||
|
|
||||||
sebserver.error.unexpected=Unexpected Error
|
sebserver.error.unexpected=Unexpected Error
|
||||||
|
sebserver.page.message=Information
|
||||||
|
|
||||||
|
|
||||||
################################
|
################################
|
||||||
|
@ -39,3 +42,13 @@ sebserver.institution.list.title=Institutions
|
||||||
sebserver.institution.list.column.name=Name
|
sebserver.institution.list.column.name=Name
|
||||||
sebserver.institution.list.column.urlSuffix=URL Suffix
|
sebserver.institution.list.column.urlSuffix=URL Suffix
|
||||||
sebserver.institution.list.column.active=Active
|
sebserver.institution.list.column.active=Active
|
||||||
|
sebserver.institution.action.new=New Institution
|
||||||
|
sebserver.institution.action.modify=Edit Institution
|
||||||
|
sebserver.institution.action.delete=Delete Institution
|
||||||
|
sebserver.institution.info.pleaseSelect=Please Select an Institution from the Table first.
|
||||||
|
|
||||||
|
################################
|
||||||
|
# Form validation
|
||||||
|
################################
|
||||||
|
|
||||||
|
sebserver.form.validation.fieldError.size=The size must be between {3} and {4}
|
BIN
src/main/resources/static/images/delete.png
Normal file
BIN
src/main/resources/static/images/delete.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 186 B |
BIN
src/main/resources/static/images/editAction.png
Normal file
BIN
src/main/resources/static/images/editAction.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 165 B |
Binary file not shown.
Before Width: | Height: | Size: 165 B After Width: | Height: | Size: 121 B |
Loading…
Reference in a new issue