SEBSERV-27 #Insitution List and actions

This commit is contained in:
anhefti 2019-02-07 17:03:46 +01:00
parent 60bd32c2cb
commit 04d438923d
50 changed files with 1296 additions and 335 deletions

View file

@ -56,6 +56,11 @@ public class EntityName implements ModelIdAware, ModelNameAware {
return this.modelId;
}
@JsonIgnore
public EntityKey getEntityKey() {
return new EntityKey(getModelId(), getEntityType());
}
@Override
public int hashCode() {
final int prime = 31;

View file

@ -95,7 +95,7 @@ public final class LmsSetupTestResult {
}
public static final LmsSetupTestResult ofQuizRequestError(final String message) {
return new LmsSetupTestResult(false, null, null, message);
return new LmsSetupTestResult(false, Collections.emptyList(), null, message);
}
}

View file

@ -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
*
* @param other the other value to get if the computed value is null

View file

@ -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();
}
}
}

View file

@ -11,6 +11,9 @@ package ch.ethz.seb.sebserver.gui.service.page;
import org.eclipse.swt.widgets.Composite;
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;
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 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 PASSWORD = "PASSWORD";
@ -86,6 +92,8 @@ public interface PageContext {
* @return this PageContext instance (builder pattern) */
PageContext withAttr(String key, String value);
PageContext withSelection(ActivitySelection selection);
String getAttribute(String name);
String getAttribute(String name, String def);
@ -99,6 +107,8 @@ public interface PageContext {
* @param event the concrete PageEvent instance */
<T extends PageEvent> void publishPageEvent(T event);
Action createAction(ActionDefinition actionDefinition);
/** Apply a confirm dialog with a specified confirm message and a callback code
* block that will be executed on users OK selection.
*
@ -125,4 +135,6 @@ public interface PageContext {
<T> T logoutOnError(Throwable error);
void publishPageMessage(PageMessageException pme);
}

View file

@ -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);
}

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -13,10 +13,14 @@ import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory.IconButtonType;
public enum ActionDefinition {
INSTITUTION_NEW(
"actions.new.institution",
"sebserver.institution.action.new",
IconButtonType.NEW_ACTION),
INSTITUTION_MODIFY(
"sebserver.institution.action.modify",
IconButtonType.MODIFY_ACTION),
INSTITUTION_SAVE(
"actions.modify.institution",
IconButtonType.SAVE_ACTION),

View file

@ -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.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.event.ActionPublishEvent;
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.CustomVariant;
@Lazy
@Component
@ -42,18 +43,21 @@ public class ActionPane implements TemplateComposer {
}
@Override
public void compose(final PageContext composerCtx) {
public void compose(final PageContext pageContext) {
final Label label = this.widgetFactory.labelLocalized(
composerCtx.getParent(),
"h3",
pageContext.getParent(),
CustomVariant.TEXT_H2,
new LocTextKey("sebserver.actionpane.title"));
final GridData titleLayout = new GridData(SWT.FILL, SWT.TOP, true, false);
titleLayout.verticalIndent = 10;
titleLayout.horizontalIndent = 10;
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");
final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
actions.setLayoutData(gridData);
@ -69,7 +73,7 @@ public class ActionPane implements TemplateComposer {
actions.addListener(SWT.Selection, event -> {
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();
if (!treeItem.isDisposed()) {
@ -82,12 +86,16 @@ public class ActionPane implements TemplateComposer {
new ActionPublishEventListener() {
@Override
public void notify(final ActionPublishEvent event) {
final TreeItem actionItem = ActionPane.this.widgetFactory.treeItemLocalized(
actions,
event.actionDefinition.name);
actionItem.setImage(event.actionDefinition.icon.getImage(composerCtx.getParent().getDisplay()));
actionItem.setData(ACTION_EVENT_CALL_KEY,
new SafeActionExecution(composerCtx, event, event.run));
event.action.definition.name);
actionItem.setImage(event.action.definition.icon.getImage(
pageContext.getParent().getDisplay()));
actionItem.setData(ACTION_EVENT_CALL_KEY, event.action);
}
});

View file

@ -8,9 +8,47 @@
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 */
// 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 */
// static Runnable deleteInstitution(final PageContext composerCtx, final RestServices restServices,

View file

@ -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);
}
}
};
}
}

View file

@ -9,9 +9,7 @@
package ch.ethz.seb.sebserver.gui.service.page.activity;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
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.gui.service.i18n.LocTextKey;
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.action.ActionDefinition;
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.ActionEventListener;
import ch.ethz.seb.sebserver.gui.service.page.event.ActivitySelectionEvent;
import ch.ethz.seb.sebserver.gui.service.page.event.PageEventListener;
import ch.ethz.seb.sebserver.gui.service.page.impl.MainPageState;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitutionNames;
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.CustomVariant;
@Lazy
@Component
@ -76,7 +74,7 @@ public class ActivitiesPane implements TemplateComposer {
final Label activities = this.widgetFactory.labelLocalized(
pageContext.getParent(),
"h3",
CustomVariant.TEXT_H2,
new LocTextKey("sebserver.activitiespane.title"));
final GridData activitiesGridData = new GridData(SWT.FILL, SWT.TOP, true, false);
activitiesGridData.horizontalIndent = 20;
@ -88,10 +86,10 @@ public class ActivitiesPane implements TemplateComposer {
navigationGridData.horizontalIndent = 10;
navigation.setLayoutData(navigationGridData);
final List<EntityName> insitutionNames = this.restService
.getBuilder(GetInstitutionNames.class)
.call()
.get(pageContext::notifyError, () -> Collections.emptyList());
// final List<EntityName> insitutionNames = this.restService
// .getBuilder(GetInstitutionNames.class)
// .call()
// .get(pageContext::notifyError, () -> Collections.emptyList());
if (userInfo.hasRole(UserRole.SEB_SERVER_ADMIN)) {
// institutions (list) as root
@ -100,12 +98,17 @@ public class ActivitiesPane implements TemplateComposer {
Activity.INSTITUTION_ROOT.title);
ActivitySelection.inject(institutions, Activity.INSTITUTION_ROOT.createSelection());
for (final EntityName inst : insitutionNames) {
createInstitutionItem(institutions, inst);
}
// for (final EntityName inst : insitutionNames) {
// createInstitutionItem(institutions, inst);
// }
} else {
final EntityName inst = insitutionNames.iterator().next();
createInstitutionItem(navigation, inst);
// institution node as none root
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(
@ -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);
createInstitutionItem(idAndName, institution);
createInstitutionItem(entityName, 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);
createInstitutionItem(idAndName, institution);
createInstitutionItem(entityName, institution);
return institution;
}
static void createInstitutionItem(final EntityName idAndName, final TreeItem institution) {
institution.setText(idAndName.name);
ActivitySelection.inject(institution, Activity.INSTITUTION_NODE.createSelection(idAndName));
static void createInstitutionItem(final EntityName entityName, final TreeItem institution) {
institution.setText(entityName.name);
ActivitySelection.inject(
institution,
Activity.INSTITUTION_NODE
.createSelection()
.withEntity(entityName.getEntityKey()));
}
static final TreeItem findItemByActivity(
@ -250,7 +257,7 @@ public class ActivitiesPane implements TemplateComposer {
for (final TreeItem item : items) {
final ActivitySelection activitySelection = ActivitySelection.get(item);
final String id = activitySelection.getObjectIdentifier();
final String id = activitySelection.getEntityId();
if (activitySelection != null && activitySelection.activity == activity &&
(id == null || (objectId != null && objectId.equals(id)))) {
return item;

View file

@ -8,11 +8,14 @@
package ch.ethz.seb.sebserver.gui.service.page.activity;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import org.eclipse.swt.widgets.TreeItem;
import ch.ethz.seb.sebserver.gbl.model.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.page.PageContext.AttributeKeys;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
@ -30,7 +33,7 @@ public class ActivitySelection {
};
public enum Activity {
NONE(TODOTemplate.class, TODOTemplate.class, (String) null),
NONE(TODOTemplate.class, TODOTemplate.class),
INSTITUTION_ROOT(
InstitutionList.class,
ActionPane.class,
@ -38,7 +41,7 @@ public class ActivitySelection {
INSTITUTION_NODE(
TODOTemplate.class,
ActionPane.class,
AttributeKeys.INSTITUTION_ID),
new LocTextKey("sebserver.activities.inst")),
//
// USERS(UserAccountsForm.class, ActionPane.class),
//
@ -55,7 +58,7 @@ public class ActivitySelection {
public final LocTextKey title;
public final Class<? extends TemplateComposer> contentPaneComposer;
public final Class<? extends TemplateComposer> actionPaneComposer;
public final String objectIdentifierAttribute;
//public final String modelIdAttribute;
private Activity(
final Class<? extends TemplateComposer> objectPaneComposer,
@ -65,43 +68,54 @@ public class ActivitySelection {
this.title = title;
this.contentPaneComposer = objectPaneComposer;
this.actionPaneComposer = selectionPaneComposer;
this.objectIdentifierAttribute = null;
}
private Activity(
final Class<? extends TemplateComposer> objectPaneComposer,
final Class<? extends TemplateComposer> selectionPaneComposer,
final String objectIdentifierAttribute) {
final Class<? extends TemplateComposer> selectionPaneComposer) {
this.title = null;
this.contentPaneComposer = objectPaneComposer;
this.actionPaneComposer = selectionPaneComposer;
this.objectIdentifierAttribute = objectIdentifierAttribute;
}
public final ActivitySelection createSelection() {
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";
public final Activity activity;
public final EntityName entityName;
final Map<String, String> attributes;
Consumer<TreeItem> expandFunction = EMPTY_FUNCTION;
ActivitySelection(final Activity activity) {
this(activity, null);
this.activity = activity;
this.attributes = new HashMap<>();
}
ActivitySelection(final Activity activity, final EntityName entityName) {
this.activity = activity;
this.entityName = entityName;
this.expandFunction = EMPTY_FUNCTION;
public ActivitySelection withEntity(final EntityKey entityKey) {
if (entityKey != null) {
this.attributes.put(AttributeKeys.ATTR_ENTITY_ID, entityKey.modelId);
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) {
@ -112,44 +126,12 @@ public class ActivitySelection {
return this;
}
public String getObjectIdentifier() {
if (this.entityName == null) {
return null;
}
return this.entityName.modelId;
}
public void processExpand(final TreeItem item) {
this.expandFunction.accept(item);
}
@Override
public int hashCode() {
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 String getEntityId() {
return this.attributes.get(AttributeKeys.ATTR_ENTITY_ID);
}
public static ActivitySelection get(final TreeItem item) {

View file

@ -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 {
}

View file

@ -16,13 +16,17 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
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.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.page.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.institution.GetInstitutions;
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;
@Lazy
@ -49,37 +53,39 @@ public class InstitutionList implements TemplateComposer {
content.setLayout(contentLayout);
content.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
// title
this.widgetFactory.labelLocalizedTitle(
content,
new LocTextKey("sebserver.institution.list.title"));
this.widgetFactory.entityTableBuilder(this.restService.getRestCall(GetInstitutions.class))
.withPaging(3)
.withColumn(new ColumnDefinition<>(
Domain.INSTITUTION.ATTR_NAME,
new LocTextKey("sebserver.institution.list.column.name"),
null,
0,
entity -> entity.name,
null,
true))
.withColumn(new ColumnDefinition<>(
Domain.INSTITUTION.ATTR_URL_SUFFIX,
new LocTextKey("sebserver.institution.list.column.urlSuffix"),
null,
0,
entity -> entity.urlSuffix,
null,
true))
.withColumn(new ColumnDefinition<>(
Domain.INSTITUTION.ATTR_ACTIVE,
new LocTextKey("sebserver.institution.list.column.active"),
null,
0,
entity -> entity.active,
null,
true))
.compose(content);
// table
final EntityTable<Institution> table =
this.widgetFactory.entityTableBuilder(this.restService.getRestCall(GetInstitutions.class))
.withPaging(3)
.withColumn(new ColumnDefinition<>(
Domain.INSTITUTION.ATTR_NAME,
new LocTextKey("sebserver.institution.list.column.name"),
entity -> entity.name,
true))
.withColumn(new ColumnDefinition<>(
Domain.INSTITUTION.ATTR_URL_SUFFIX,
new LocTextKey("sebserver.institution.list.column.urlSuffix"),
entity -> entity.urlSuffix,
true))
.withColumn(new ColumnDefinition<>(
Domain.INSTITUTION.ATTR_ACTIVE,
new LocTextKey("sebserver.institution.list.column.active"),
entity -> entity.active,
true))
.compose(content);
// propagate content actions to action-pane
pageContext.createAction(ActionDefinition.INSTITUTION_NEW)
.withExec(InstitutionActions::newInstitution)
.publish()
.createAction(ActionDefinition.INSTITUTION_MODIFY)
.withExec(InstitutionActions.editInstitution(table))
.publish();
}

View file

@ -10,6 +10,8 @@ package ch.ethz.seb.sebserver.gui.service.page.event;
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 ActionDefinition actionDefinition;

View file

@ -13,7 +13,6 @@ import java.util.function.Predicate;
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;
public interface ActionEventListener extends PageEventListener<ActionEvent> {

View file

@ -8,46 +8,16 @@
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 final ActionDefinition actionDefinition;
public final Runnable run;
public final String confirmationMessage;
public final String successMessage;
public final Action action;
public ActionPublishEvent(
final ActionDefinition actionDefinition,
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 + "]";
public ActionPublishEvent(final Action action) {
this.action = action;
}
}

View file

@ -8,8 +8,6 @@
package ch.ethz.seb.sebserver.gui.service.page.event;
import ch.ethz.seb.sebserver.gui.service.page.PageEventListener;
public interface ActionPublishEventListener extends PageEventListener<ActionPublishEvent> {
@Override

View file

@ -8,8 +8,6 @@
package ch.ethz.seb.sebserver.gui.service.page.event;
import ch.ethz.seb.sebserver.gui.service.page.PageEventListener;
public interface ActivitySelectionListener extends PageEventListener<ActivitySelectionEvent> {
@Override

View file

@ -8,8 +8,6 @@
package ch.ethz.seb.sebserver.gui.service.page.event;
import ch.ethz.seb.sebserver.gui.service.page.PageEventListener;
public interface LogoutEventListener extends PageEventListener<LogoutEvent> {
@Override

View file

@ -8,6 +8,11 @@
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 {
}

View file

@ -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);
}

View file

@ -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;
}
}
}
}

View file

@ -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());
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -20,13 +20,14 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
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.page.ComposerService;
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.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.widget.WidgetFactory;
@Lazy
@Service
@ -40,17 +41,20 @@ public class ComposerServiceImpl implements ComposerService {
private final Class<? extends PageDefinition> mainPageType = DefaultMainPage.class;
final AuthorizationContextHolder authorizationContextHolder;
private final RestService restService;
private final I18nSupport i18nSupport;
private final Map<String, TemplateComposer> composer;
private final Map<String, PageDefinition> pages;
public ComposerServiceImpl(
final AuthorizationContextHolder authorizationContextHolder,
final RestService restService,
final I18nSupport i18nSupport,
final Collection<TemplateComposer> composer,
final Collection<PageDefinition> pageDefinitions) {
this.authorizationContextHolder = authorizationContextHolder;
this.restService = restService;
this.i18nSupport = i18nSupport;
this.composer = composer
.stream()
@ -108,7 +112,7 @@ public class ComposerServiceImpl implements ComposerService {
if (composer.validate(pageContext)) {
RWTUtils.clearComposite(pageContext.getParent());
WidgetFactory.clearComposite(pageContext.getParent());
try {
composer.compose(pageContext);
@ -169,8 +173,7 @@ public class ComposerServiceImpl implements ComposerService {
}
private PageContext createPageContext(final Composite root) {
return new PageContextImpl(
this.i18nSupport, this, root, root, null);
return new PageContextImpl(this.restService, this.i18nSupport, this, root, root, null);
}
}

View file

@ -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.remote.webservice.auth.AuthorizationContextHolder;
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory;
import ch.ethz.seb.sebserver.gui.service.widget.WidgetFactory.CustomVariant;
@Lazy
@Component
@ -243,11 +244,17 @@ public class DefaultPageLayout implements TemplateComposer {
rowLayout.marginRight = 20;
footerRight.setLayout(rowLayout);
this.widgetFactory.labelLocalized(footerLeft, "footer", new LocTextKey("sebserver.overall.imprint"));
this.widgetFactory.labelLocalized(footerLeft, "footer", new LocTextKey("sebserver.overall.about"));
this.widgetFactory.labelLocalized(
footerLeft,
CustomVariant.FOOTER,
new LocTextKey("sebserver.overall.imprint"));
this.widgetFactory.labelLocalized(
footerLeft,
CustomVariant.FOOTER,
new LocTextKey("sebserver.overall.about"));
this.widgetFactory.labelLocalized(
footerRight,
"footer",
CustomVariant.FOOTER,
new LocTextKey("sebserver.overall.version", this.sebServerVersion));
}

View file

@ -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.PageContext;
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.PageEventListener;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.widget.Message;
public class PageContextImpl implements PageContext {
@ -39,6 +44,7 @@ public class PageContextImpl implements PageContext {
private static final ListenerComparator LIST_COMPARATOR = new ListenerComparator();
private final RestService restService;
private final I18nSupport i18nSupport;
private final ComposerService composerService;
private final Composite root;
@ -46,12 +52,14 @@ public class PageContextImpl implements PageContext {
private final Map<String, String> attributes;
PageContextImpl(
final RestService restService,
final I18nSupport i18nSupport,
final ComposerService composerService,
final Composite root,
final Composite parent,
final Map<String, String> attributes) {
this.restService = restService;
this.i18nSupport = i18nSupport;
this.composerService = composerService;
this.root = root;
@ -86,6 +94,7 @@ public class PageContextImpl implements PageContext {
@Override
public PageContext copyOf(final Composite parent) {
return new PageContextImpl(
this.restService,
this.i18nSupport,
this.composerService,
this.root,
@ -99,6 +108,7 @@ public class PageContextImpl implements PageContext {
attrs.putAll(this.attributes);
attrs.putAll(((PageContextImpl) otherContext).attributes);
return new PageContextImpl(
this.restService,
this.i18nSupport,
this.composerService,
this.root,
@ -112,10 +122,31 @@ public class PageContextImpl implements PageContext {
attrs.putAll(this.attributes);
attrs.put(key, value);
return new PageContextImpl(
this.restService,
this.i18nSupport,
this.composerService,
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
@ -160,6 +191,11 @@ public class PageContextImpl implements PageContext {
.forEach(listener -> listener.notify(event));
}
@Override
public Action createAction(final ActionDefinition actionDefinition) {
return new Action(actionDefinition, this, this.restService);
}
@Override
@SuppressWarnings("serial")
public void applyConfirmDialog(final String confirmMessage, final Runnable onOK) {
@ -212,6 +248,17 @@ public class PageContextImpl implements 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
public void notifyError(final String errorMessage, final Throwable error) {
if (error instanceof APIMessageError) {

View file

@ -60,8 +60,8 @@ public class SEBLogin implements TemplateComposer {
if (pageContext.hasAttribute((AttributeKeys.LGOUT_SUCCESS))) {
final MessageBox logoutSuccess = new Message(
pageContext.getShell(),
this.i18nSupport.getText("org.sebserver.logout"),
this.i18nSupport.getText("org.sebserver.logout.success.message"),
this.i18nSupport.getText("sebserver.logout"),
this.i18nSupport.getText("sebserver.logout.success.message"),
SWT.ICON_INFORMATION);
logoutSuccess.open(null);
}

View file

@ -23,11 +23,11 @@ import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
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.PageEventListener;
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.event.ActivitySelectionEvent;
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.IconButtonType;
@ -128,9 +128,9 @@ public class SEBMainPage implements TemplateComposer {
public void notify(final ActivitySelectionEvent event) {
pageContext.composerService().compose(
event.selection.activity.contentPaneComposer,
pageContext.copyOf(contentObjects).withAttr(
event.selection.activity.objectIdentifierAttribute,
event.selection.getObjectIdentifier()));
pageContext
.copyOf(contentObjects)
.withSelection(event.selection));
}
});
@ -150,9 +150,9 @@ public class SEBMainPage implements TemplateComposer {
public void notify(final ActivitySelectionEvent event) {
pageContext.composerService().compose(
event.selection.activity.actionPaneComposer,
pageContext.copyOf(actionPane).withAttr(
event.selection.activity.objectIdentifierAttribute,
event.selection.getObjectIdentifier()));
pageContext
.copyOf(actionPane)
.withSelection(event.selection));
}
});

View file

@ -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;
}
}

View file

@ -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();
}

View file

@ -66,6 +66,9 @@ public abstract class RestCall<T> {
}
protected Result<T> exchange(final RestCallBuilder builder) {
log.debug("Call webservice API on {} for {}", this.path, builder);
try {
final ResponseEntity<String> responseEntity = RestCall.this.restService
.getWebserviceAPIRestTemplate()
@ -90,6 +93,11 @@ public abstract class RestCall<T> {
responseEntity.getBody(),
new TypeReference<List<APIMessage>>() {
}));
log.debug(
"Webservice answered with well defined error- or validation-failure-response: ",
restCallError);
return Result.ofError(restCallError);
}
@ -102,7 +110,7 @@ public abstract class RestCall<T> {
new TypeReference<List<APIMessage>>() {
}));
} 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);
@ -180,6 +188,11 @@ public abstract class RestCall<T> {
return this;
}
public RestCallBuilder withFormBinding(final FormBinding formBinding) {
// TODO Auto-generated method stub
return this;
}
public RestCallBuilder onlyActive(final boolean active) {
this.queryParams.put(Entity.FILTER_ATTR_ACTIVE, Arrays.asList(String.valueOf(active)));
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 + "]";
}
}
}

View file

@ -37,4 +37,8 @@ public class RestCallError extends RuntimeException implements APIMessageError {
return !this.errors.isEmpty();
}
@Override
public String toString() {
return "RestCallError [errors=" + this.errors + "]";
}
}

View file

@ -12,8 +12,6 @@ import java.util.Collection;
import java.util.Map;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@ -29,8 +27,6 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.WebserviceURISer
@GuiProfile
public class RestService {
private static final Logger log = LoggerFactory.getLogger(RestService.class);
private final AuthorizationContextHolder authorizationContextHolder;
private final WebserviceURIService webserviceURIBuilderSupplier;
private final Map<String, RestCall<?>> calls;

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui.service.remote.webservice.api.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);
}
}

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui.service.remote.webservice.api.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);
}
}

View file

@ -8,6 +8,7 @@
package ch.ethz.seb.sebserver.gui.service.remote.webservice.auth;
import java.io.IOException;
import java.net.URI;
import java.util.Arrays;
import java.util.Collections;
@ -25,9 +26,11 @@ import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext;
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.OAuth2ProtectedResourceDetails;
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.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.currentUserURI = webserviceURIService.getCurrentUserRequestURI();

View file

@ -40,6 +40,15 @@ public final class ColumnDefinition<ROW extends Entity> {
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(
final String columnName,
final LocTextKey displayName,

View file

@ -8,7 +8,11 @@
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.stream.Collectors;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
@ -46,8 +50,6 @@ public class EntityTable<ROW extends Entity> extends Composite {
private final Table table;
private final TableNavigator navigator;
private final boolean selectableRows;
private int pageNumber = 1;
private int pageSize;
private String sortColumn = null;
@ -56,31 +58,34 @@ public class EntityTable<ROW extends Entity> extends Composite {
private boolean columnsWithSameWidth = true;
EntityTable(
final int type,
final Composite parent,
final RestCall<Page<ROW>> restCall,
final WidgetFactory widgetFactory,
final List<ColumnDefinition<ROW>> columns,
final List<TableRowAction> actions,
final int pageSize,
final boolean withFilter,
final boolean selectableRows) {
final boolean withFilter) {
super(parent, SWT.NONE);
super(parent, type);
this.widgetFactory = widgetFactory;
this.restCall = restCall;
this.columns = Utils.immutableListOf(columns);
this.actions = Utils.immutableListOf(actions);
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.filter = (withFilter) ? new TableFilter<>(this) : null;
this.selectableRows = selectableRows;
this.table = widgetFactory.tableLocalized(this);
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.addListener(SWT.Resize, this::adaptColumnWidth);
@ -148,6 +153,18 @@ public class EntityTable<ROW extends Entity> extends Composite {
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() {
for (final ColumnDefinition<ROW> column : this.columns) {
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);
item.setData(TABLE_ROW_DATA, row);
int index = 0;
if (this.selectableRows) {
// TODO
}
for (final ColumnDefinition<ROW> column : this.columns) {
final Object value = column.valueSupplier.apply(row);
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();
}
}

View file

@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.gui.service.table;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import ch.ethz.seb.sebserver.gbl.model.Entity;
@ -41,7 +42,7 @@ public class TableBuilder<ROW extends Entity> {
final List<TableRowAction> actions = new ArrayList<>();
private int pageSize = -1;
private boolean selectableRows = false;
private int type = SWT.NONE;
public TableBuilder(
final WidgetFactory widgetFactory,
@ -61,13 +62,13 @@ public class TableBuilder<ROW extends Entity> {
return this;
}
public TableBuilder<ROW> withSelectableRows() {
this.selectableRows = true;
public TableBuilder<ROW> withAction(final TableRowAction action) {
this.actions.add(action);
return this;
}
public TableBuilder<ROW> withAction(final TableRowAction action) {
this.actions.add(action);
public TableBuilder<ROW> withMultiselection() {
this.type |= SWT.MULTI;
return this;
}
@ -79,14 +80,14 @@ public class TableBuilder<ROW extends Entity> {
.isPresent();
return new EntityTable<>(
this.type,
parent,
this.restCall,
this.widgetFactory,
this.columns,
this.actions,
this.pageSize,
withFilter,
this.selectableRows);
withFilter);
}
}

View file

@ -17,7 +17,7 @@ import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
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 {
@ -39,7 +39,7 @@ public class TableNavigator extends Composite {
public Page<?> update(final Page<?> pageData) {
// clear all
RWTUtils.clearComposite(this);
WidgetFactory.clearComposite(this);
final int pageNumber = pageData.getPageNumber();
final int numberOfPages = pageData.getNumberOfPages();

View file

@ -25,6 +25,7 @@ import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
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.profile.GuiProfile;
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.LocTextKey;
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);
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 {
MAXIMIZE("maximize.png"),
MINIMIZE("minimize.png"),
MODIFY_ACTION("editAction.png"),
SAVE_ACTION("saveAction.png"),
NEW_ACTION("newAction.png"),
DELETE_ACTION("deleteAction.png"),
@ -108,10 +126,10 @@ public class WidgetFactory {
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);
this.injectI18n(button, new LocTextKey(locTextKey));
button.setData(RWT.CUSTOM_VARIANT, style);
button.setData(RWT.CUSTOM_VARIANT, variant.key);
return button;
}
@ -133,10 +151,10 @@ public class WidgetFactory {
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);
this.injectI18n(label, locTextKey);
label.setData(RWT.CUSTOM_VARIANT, style);
label.setData(RWT.CUSTOM_VARIANT, variant.key);
return label;
}
@ -152,24 +170,24 @@ public class WidgetFactory {
public Label labelLocalized(
final Composite parent,
final String style,
final CustomVariant variant,
final LocTextKey locTextKey,
final LocTextKey locToolTextKey) {
final Label label = new Label(parent, SWT.NONE);
this.injectI18n(label, locTextKey, locToolTextKey);
label.setData(RWT.CUSTOM_VARIANT, style);
label.setData(RWT.CUSTOM_VARIANT, variant.key);
return label;
}
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));
return labelLocalized;
}
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);
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) {
return tree -> updateLocale(tree.getItems(), i18nSupport);
}

View file

@ -16,6 +16,8 @@ sebserver.login.pwd=Password
sebserver.login.login=Sign In
sebserver.login.failed.title=Login Failed
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
@ -29,6 +31,7 @@ sebserver.actionpane.title=Actions
sebserver.activities.inst=Institution
sebserver.error.unexpected=Unexpected Error
sebserver.page.message=Information
################################
@ -38,4 +41,14 @@ sebserver.error.unexpected=Unexpected Error
sebserver.institution.list.title=Institutions
sebserver.institution.list.column.name=Name
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}

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

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