SEBSERV-25 finished table

This commit is contained in:
anhefti 2019-03-05 11:08:50 +01:00
parent 9bfe3fb2c8
commit e799a0214f
25 changed files with 723 additions and 347 deletions

View file

@ -12,6 +12,7 @@ import java.util.Collection;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import ch.ethz.seb.sebserver.gbl.util.Utils;
@ -67,6 +68,11 @@ public final class Page<T> {
return this.content;
}
@JsonIgnore
public boolean isEmpty() {
return this.content == null || this.content.isEmpty();
}
@Override
public String toString() {
return "Page [numberOfPages=" + this.numberOfPages + ", pageNumber=" + this.pageNumber + ", pageSize="

View file

@ -57,6 +57,7 @@ public class InstitutionList implements TemplateComposer {
// table
final EntityTable<Institution> table =
this.widgetFactory.entityTableBuilder(this.restService.getRestCall(GetInstitutions.class))
.withEmptyMessage(new LocTextKey("sebserver.institution.list.empty"))
.withPaging(3)
.withColumn(new ColumnDefinition<>(
Domain.INSTITUTION.ATTR_NAME,
@ -88,11 +89,11 @@ public class InstitutionList implements TemplateComposer {
.createAction(ActionDefinition.INSTITUTION_VIEW_FROM_LIST)
.withSelect(table::getSelection, Action.applySingleSelection("sebserver.institution.info.pleaseSelect"))
.publish()
.publishIf(() -> table.hasAnyContent())
.createAction(ActionDefinition.INSTITUTION_MODIFY_FROM_LIST)
.withSelect(table::getSelection, Action.applySingleSelection("sebserver.institution.info.pleaseSelect"))
.publishIf(instGrant::m);
.publishIf(() -> instGrant.m() && table.hasAnyContent());
;
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui.content;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.form.PageFormService;
import ch.ethz.seb.sebserver.gui.service.ResourceService;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
@Lazy
@Component
@GuiProfile
public class LmsSetupForm implements TemplateComposer {
private final PageFormService pageFormService;
private final ResourceService resourceService;
protected LmsSetupForm(
final PageFormService pageFormService,
final ResourceService resourceService) {
this.pageFormService = pageFormService;
this.resourceService = resourceService;
}
@Override
public void compose(final PageContext pageContext) {
// TODO Auto-generated method stub
}
}

View file

@ -0,0 +1,126 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui.content;
import java.util.function.Function;
import org.eclipse.swt.widgets.Composite;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.ResourceService;
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.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.GetLmsSetups;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
import ch.ethz.seb.sebserver.gui.table.ColumnDefinition;
import ch.ethz.seb.sebserver.gui.table.ColumnDefinition.TableFilterAttribute;
import ch.ethz.seb.sebserver.gui.table.EntityTable;
import ch.ethz.seb.sebserver.gui.table.TableFilter.CriteriaType;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@Lazy
@Component
@GuiProfile
public class LmsSetupList implements TemplateComposer {
private final WidgetFactory widgetFactory;
private final ResourceService resourceService;
private final int pageSize;
protected LmsSetupList(
final WidgetFactory widgetFactory,
final ResourceService resourceService,
@Value("${sebserver.gui.list.page.size}") final Integer pageSize) {
this.widgetFactory = widgetFactory;
this.resourceService = resourceService;
this.pageSize = (pageSize != null) ? pageSize : 20;
}
@Override
public void compose(final PageContext pageContext) {
final CurrentUser currentUser = this.resourceService.getCurrentUser();
final RestService restService = this.resourceService.getRestService();
final I18nSupport i18nSupport = this.widgetFactory.getI18nSupport();
// content page layout with title
final Composite content = this.widgetFactory.defaultPageLayout(
pageContext.getParent(),
new LocTextKey("sebserver.lmssetup.list.title"));
final boolean isSEBAdmin = currentUser.get().hasRole(UserRole.SEB_SERVER_ADMIN);
// table
final EntityTable<LmsSetup> table =
this.widgetFactory.entityTableBuilder(restService.getRestCall(GetLmsSetups.class))
.withEmptyMessage(new LocTextKey("sebserver.lmssetup.list.empty"))
.withPaging(this.pageSize)
.withColumnIf(() -> isSEBAdmin,
new ColumnDefinition<>(
Domain.LMS_SETUP.ATTR_INSTITUTION_ID,
new LocTextKey("sebserver.lmssetup.list.column.institution"),
lmsSetupInstitutionNameFunction(this.resourceService),
new TableFilterAttribute(
CriteriaType.SINGLE_SELECTION,
Domain.USER.ATTR_INSTITUTION_ID,
this.resourceService::institutionResource),
false))
.withColumn(new ColumnDefinition<>(
Domain.LMS_SETUP.ATTR_NAME,
new LocTextKey("sebserver.lmssetup.list.column.name"),
entity -> entity.name,
(isSEBAdmin)
? new TableFilterAttribute(CriteriaType.TEXT, Domain.LMS_SETUP.ATTR_NAME)
: null,
true))
.withColumn(new ColumnDefinition<>(
Domain.LMS_SETUP.ATTR_LMS_TYPE,
new LocTextKey("sebserver.lmssetup.list.column.type"),
this::lmsSetupTypeName,
(isSEBAdmin)
? new TableFilterAttribute(
CriteriaType.SINGLE_SELECTION,
Domain.LMS_SETUP.ATTR_LMS_TYPE,
this.resourceService::lmsTypeResources)
: null,
false, true))
.withColumn(new ColumnDefinition<>(
Domain.LMS_SETUP.ATTR_ACTIVE,
new LocTextKey("sebserver.lmssetup.list.column.active"),
entity -> entity.active,
true))
.compose(content);
}
private String lmsSetupTypeName(final LmsSetup lmsSetup) {
if (lmsSetup.lmsType == null) {
return Constants.EMPTY_NOTE;
}
return this.resourceService.getI18nSupport()
.getText("sebserver.lmssetup.type." + lmsSetup.lmsType.name());
}
private static Function<LmsSetup, String> lmsSetupInstitutionNameFunction(final ResourceService resourceService) {
final Function<String, String> institutionNameFunction = resourceService.getInstitutionNameFunction();
return lmsSetup -> institutionNameFunction.apply(String.valueOf(lmsSetup.institutionId));
}
}

View file

@ -33,6 +33,7 @@ import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
import ch.ethz.seb.sebserver.gui.form.FormBuilder;
import ch.ethz.seb.sebserver.gui.form.FormHandle;
import ch.ethz.seb.sebserver.gui.form.PageFormService;
import ch.ethz.seb.sebserver.gui.service.ResourceService;
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.PageUtils;
@ -55,24 +56,24 @@ public class UserAccountForm implements TemplateComposer {
private static final Logger log = LoggerFactory.getLogger(UserAccountForm.class);
private final PageFormService pageFormService;
private final RestService restService;
private final CurrentUser currentUser;
private final ResourceService resourceService;
protected UserAccountForm(
final PageFormService pageFormService,
final RestService restService,
final CurrentUser currentUser) {
final ResourceService resourceService) {
this.pageFormService = pageFormService;
this.restService = restService;
this.currentUser = currentUser;
this.resourceService = resourceService;
}
@Override
public void compose(final PageContext pageContext) {
final UserInfo user = this.currentUser.get();
final CurrentUser currentUser = this.resourceService.getCurrentUser();
final RestService restService = this.resourceService.getRestService();
final WidgetFactory widgetFactory = this.pageFormService.getWidgetFactory();
final UserInfo user = currentUser.get();
final EntityKey entityKey = pageContext.getEntityKey();
final EntityKey parentEntityKey = pageContext.getParentEntityKey();
final BooleanSupplier isNew = () -> entityKey == null;
@ -86,7 +87,7 @@ public class UserAccountForm implements TemplateComposer {
(parentEntityKey != null)
? Long.valueOf(parentEntityKey.modelId)
: user.institutionId)
: this.restService
: restService
.getBuilder(GetUserAccount.class)
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
.call()
@ -101,11 +102,11 @@ public class UserAccountForm implements TemplateComposer {
}
final boolean ownAccount = user.uuid.equals(userAccount.getModelId());
final EntityGrantCheck userGrantCheck = this.currentUser.entityGrantCheck(userAccount);
final EntityGrantCheck userGrantCheck = currentUser.entityGrantCheck(userAccount);
final boolean writeGrant = userGrantCheck.w();
final boolean modifyGrant = userGrantCheck.m();
// modifying an UserAccount is not possible if the root institution is inactive
final boolean istitutionActive = this.restService.getBuilder(GetInstitution.class)
final boolean istitutionActive = restService.getBuilder(GetInstitution.class)
.withURIVariable(API.PARAM_MODEL_ID, String.valueOf(userAccount.getInstitutionId()))
.call()
.map(inst -> inst.active)
@ -142,7 +143,7 @@ public class UserAccountForm implements TemplateComposer {
Domain.USER.ATTR_INSTITUTION_ID,
"sebserver.useraccount.form.institution",
String.valueOf(userAccount.getInstitutionId()),
() -> PageUtils.getInstitutionSelectionResource(this.restService))
() -> this.resourceService.institutionResource())
.withCondition(isSEBAdmin)
.readonlyIf(isNotNew))
.addField(FormBuilder.text(
@ -161,17 +162,17 @@ public class UserAccountForm implements TemplateComposer {
Domain.USER.ATTR_LANGUAGE,
"sebserver.useraccount.form.language",
userAccount.getLanguage().getLanguage(),
widgetFactory.getI18nSupport().localizedLanguageResources()))
this.resourceService::languageResources))
.addField(FormBuilder.singleSelection(
Domain.USER.ATTR_TIMEZONE,
"sebserver.useraccount.form.timezone",
userAccount.getTimeZone().getID(),
widgetFactory.getI18nSupport().localizedTimeZoneResources()))
this.resourceService::timeZoneResources))
.addField(FormBuilder.multiSelection(
USER_ROLE.REFERENCE_NAME,
"sebserver.useraccount.form.roles",
StringUtils.join(userAccount.getRoles(), Constants.LIST_SEPARATOR_CHAR),
widgetFactory.getI18nSupport().localizedUserRoleResources())
this.resourceService::userRoleResources)
.withCondition(() -> modifyGrant)
.visibleIf(writeGrant))
.addField(FormBuilder.text(
@ -185,8 +186,8 @@ public class UserAccountForm implements TemplateComposer {
.asPasswordField()
.withCondition(isNew))
.buildFor((entityKey == null)
? this.restService.getRestCall(NewUserAccount.class)
: this.restService.getRestCall(SaveUserAccount.class));
? restService.getRestCall(NewUserAccount.class)
: restService.getRestCall(SaveUserAccount.class));
// propagate content actions to action-pane
@ -204,19 +205,19 @@ public class UserAccountForm implements TemplateComposer {
.publishIf(() -> modifyGrant && readonly && istitutionActive && userAccount.isActive())
.createAction(ActionDefinition.USER_ACCOUNT_DEACTIVATE)
.withExec(Action.activation(this.restService, false))
.withConfirm(PageUtils.confirmDeactivation(userAccount, this.restService))
.withExec(Action.activation(restService, false))
.withConfirm(PageUtils.confirmDeactivation(userAccount, restService))
.publishIf(() -> writeGrant && readonly && istitutionActive && userAccount.isActive())
.createAction(ActionDefinition.USER_ACCOUNT_ACTIVATE)
.withExec(Action.activation(this.restService, true))
.withExec(Action.activation(restService, true))
.publishIf(() -> writeGrant && readonly && istitutionActive && !userAccount.isActive())
.createAction(ActionDefinition.USER_ACCOUNT_SAVE)
.withExec(action -> {
final Action postChanges = formHandle.postChanges(action);
if (ownAccount) {
this.currentUser.refresh();
currentUser.refresh();
pageContext.forwardToMainPage();
}
return postChanges;

View file

@ -8,16 +8,22 @@
package ch.ethz.seb.sebserver.gui.content;
import java.util.function.BooleanSupplier;
import java.util.function.Function;
import org.eclipse.swt.widgets.Composite;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
import ch.ethz.seb.sebserver.gui.service.ResourceService;
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;
@ -38,33 +44,45 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
public class UserAccountList implements TemplateComposer {
private final WidgetFactory widgetFactory;
private final RestService restService;
private final CurrentUser currentUser;
private final ResourceService resourceService;
private final int pageSize;
protected UserAccountList(
final WidgetFactory widgetFactory,
final RestService restService,
final CurrentUser currentUser,
final ResourceService resourceService,
@Value("${sebserver.gui.list.page.size}") final Integer pageSize) {
this.widgetFactory = widgetFactory;
this.restService = restService;
this.currentUser = currentUser;
this.resourceService = resourceService;
this.pageSize = (pageSize != null) ? pageSize : 20;
}
@Override
public void compose(final PageContext pageContext) {
final CurrentUser currentUser = this.resourceService.getCurrentUser();
final RestService restService = this.resourceService.getRestService();
// content page layout with title
final Composite content = this.widgetFactory.defaultPageLayout(
pageContext.getParent(),
new LocTextKey("sebserver.useraccount.list.title"));
final BooleanSupplier isSEBAdmin = () -> currentUser.get().hasRole(UserRole.SEB_SERVER_ADMIN);
// table
final EntityTable<UserInfo> table =
this.widgetFactory.entityTableBuilder(this.restService.getRestCall(GetUserAccounts.class))
this.widgetFactory.entityTableBuilder(restService.getRestCall(GetUserAccounts.class))
.withEmptyMessage(new LocTextKey("sebserver.useraccount.list.empty"))
.withPaging(this.pageSize)
.withColumnIf(isSEBAdmin,
new ColumnDefinition<>(
Domain.USER.ATTR_INSTITUTION_ID,
new LocTextKey("sebserver.useraccount.list.column.institution"),
userInstitutionNameFunction(this.resourceService),
new TableFilterAttribute(
CriteriaType.SINGLE_SELECTION,
Domain.USER.ATTR_INSTITUTION_ID,
this.resourceService::institutionResource),
false))
.withColumn(new ColumnDefinition<>(
Domain.USER.ATTR_NAME,
new LocTextKey("sebserver.useraccount.list.column.name"),
@ -87,7 +105,10 @@ public class UserAccountList implements TemplateComposer {
Domain.USER.ATTR_LANGUAGE,
new LocTextKey("sebserver.useraccount.list.column.language"),
this::getLocaleDisplayText,
new TableFilterAttribute(CriteriaType.COUNTRY_SELECTION, Domain.USER.ATTR_LANGUAGE),
new TableFilterAttribute(
CriteriaType.SINGLE_SELECTION,
Domain.USER.ATTR_LANGUAGE,
this.resourceService::languageResources),
true, true))
.withColumn(new ColumnDefinition<>(
Domain.USER.ATTR_ACTIVE,
@ -97,25 +118,30 @@ public class UserAccountList implements TemplateComposer {
.compose(content);
// propagate content actions to action-pane
final GrantCheck userGrant = this.currentUser.grantCheck(EntityType.USER);
final GrantCheck userGrant = currentUser.grantCheck(EntityType.USER);
pageContext.clearEntityKeys()
.createAction(ActionDefinition.USER_ACCOUNT_NEW)
.publishIf(userGrant::w)
.createAction(ActionDefinition.USER_ACCOUNT_VIEW)
.createAction(ActionDefinition.USER_ACCOUNT_VIEW_FROM_LIST)
.withSelect(table::getSelection, Action.applySingleSelection("sebserver.useraccount.info.pleaseSelect"))
.publish()
.publishIf(() -> table.hasAnyContent())
.createAction(ActionDefinition.USER_ACCOUNT_MODIFY_FROM_LIST)
.withSelect(table::getSelection, Action.applySingleSelection("sebserver.useraccount.info.pleaseSelect"))
.publishIf(userGrant::m);
.publishIf(() -> userGrant.m() && table.hasAnyContent());
}
private String getLocaleDisplayText(final UserInfo userInfo) {
return (userInfo.language != null)
? userInfo.language.getDisplayLanguage(this.widgetFactory.getI18nSupport().getCurrentLocale())
: null;
: Constants.EMPTY_NOTE;
}
private static Function<UserInfo, String> userInstitutionNameFunction(final ResourceService resourceService) {
final Function<String, String> institutionNameFunction = resourceService.getInstitutionNameFunction();
return userInfo -> institutionNameFunction.apply(String.valueOf(userInfo.institutionId));
}
}

View file

@ -10,6 +10,8 @@ package ch.ethz.seb.sebserver.gui.content.action;
import ch.ethz.seb.sebserver.gui.content.InstitutionForm;
import ch.ethz.seb.sebserver.gui.content.InstitutionList;
import ch.ethz.seb.sebserver.gui.content.LmsSetupForm;
import ch.ethz.seb.sebserver.gui.content.LmsSetupList;
import ch.ethz.seb.sebserver.gui.content.UserAccountChangePasswordForm;
import ch.ethz.seb.sebserver.gui.content.UserAccountForm;
import ch.ethz.seb.sebserver.gui.content.UserAccountList;
@ -17,6 +19,8 @@ import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon;
/** Enumeration of static action data for each action within the SEB Server GUI */
// TODO add category to allow easy positioning of actions later
public enum ActionDefinition {
INSTITUTION_VIEW_LIST(
@ -36,43 +40,36 @@ public enum ActionDefinition {
ImageIcon.SHOW,
InstitutionForm.class,
INSTITUTION_VIEW_LIST),
INSTITUTION_MODIFY_FROM_LIST(
new LocTextKey("sebserver.institution.action.list.modify"),
ImageIcon.EDIT,
InstitutionForm.class,
INSTITUTION_VIEW_LIST, false),
INSTITUTION_MODIFY(
new LocTextKey("sebserver.institution.action.modify"),
ImageIcon.EDIT,
InstitutionForm.class,
INSTITUTION_VIEW_LIST, false),
INSTITUTION_CANCEL_MODIFY(
new LocTextKey("sebserver.overall.action.modify.cancel"),
ImageIcon.CANCEL,
InstitutionForm.class,
INSTITUTION_VIEW_LIST),
INSTITUTION_SAVE(
new LocTextKey("sebserver.institution.action.save"),
ImageIcon.SAVE,
InstitutionForm.class,
INSTITUTION_VIEW_LIST),
INSTITUTION_ACTIVATE(
new LocTextKey("sebserver.institution.action.activate"),
ImageIcon.INACTIVE,
InstitutionForm.class,
INSTITUTION_VIEW_LIST),
INSTITUTION_DEACTIVATE(
new LocTextKey("sebserver.institution.action.deactivate"),
ImageIcon.ACTIVE,
InstitutionForm.class,
INSTITUTION_VIEW_LIST),
INSTITUTION_DELETE(
new LocTextKey("sebserver.institution.action.modify"),
ImageIcon.DELETE,
@ -91,55 +88,46 @@ public enum ActionDefinition {
ImageIcon.NEW,
UserAccountForm.class,
USER_ACCOUNT_VIEW_LIST, false),
USER_ACCOUNT_VIEW(
USER_ACCOUNT_VIEW_FROM_LIST(
new LocTextKey("sebserver.useraccount.action.view"),
ImageIcon.SHOW,
UserAccountForm.class,
USER_ACCOUNT_VIEW_LIST),
USER_ACCOUNT_MODIFY_FROM_LIST(
new LocTextKey("sebserver.useraccount.action.list.modify"),
ImageIcon.EDIT,
UserAccountForm.class,
USER_ACCOUNT_VIEW_LIST, false),
USER_ACCOUNT_MODIFY(
new LocTextKey("sebserver.useraccount.action.modify"),
ImageIcon.EDIT,
UserAccountForm.class,
USER_ACCOUNT_VIEW_LIST, false),
USER_ACCOUNT_CANCEL_MODIFY(
new LocTextKey("sebserver.overall.action.modify.cancel"),
ImageIcon.CANCEL,
UserAccountForm.class,
USER_ACCOUNT_VIEW_LIST),
USER_ACCOUNT_SAVE(
new LocTextKey("sebserver.useraccount.action.save"),
ImageIcon.SAVE,
UserAccountForm.class,
USER_ACCOUNT_VIEW_LIST),
USER_ACCOUNT_ACTIVATE(
new LocTextKey("sebserver.useraccount.action.activate"),
ImageIcon.INACTIVE,
UserAccountForm.class,
USER_ACCOUNT_VIEW_LIST),
USER_ACCOUNT_DEACTIVATE(
new LocTextKey("sebserver.useraccount.action.deactivate"),
ImageIcon.ACTIVE,
UserAccountForm.class,
USER_ACCOUNT_VIEW_LIST),
USER_ACCOUNT_DELETE(
new LocTextKey("sebserver.useraccount.action.modify"),
ImageIcon.DELETE,
UserAccountList.class,
USER_ACCOUNT_VIEW_LIST),
USER_ACCOUNT_CHANGE_PASSOWRD(
new LocTextKey("sebserver.useraccount.action.change.password"),
ImageIcon.EDIT,
@ -151,13 +139,21 @@ public enum ActionDefinition {
UserAccountForm.class,
USER_ACCOUNT_VIEW_LIST),
;
LMS_SETUP_VIEW_LIST(
new LocTextKey("sebserver.lmssetup.list.title"),
LmsSetupList.class),
LMS_SETUP_VIEW_FORM(
new LocTextKey("sebserver.useraccount.action.form"),
LmsSetupForm.class,
USER_ACCOUNT_VIEW_LIST),
;
public final LocTextKey title;
public final ImageIcon icon;
public final Class<? extends TemplateComposer> contentPaneComposer;
public final Class<? extends TemplateComposer> actionPaneComposer;
public final ActionDefinition activityAlias;
public final String category;
public final boolean readonly;
private ActionDefinition(
@ -169,6 +165,7 @@ public enum ActionDefinition {
this.contentPaneComposer = contentPaneComposer;
this.actionPaneComposer = ActionPane.class;
this.activityAlias = null;
this.category = null;
this.readonly = true;
}
@ -182,6 +179,7 @@ public enum ActionDefinition {
this.contentPaneComposer = contentPaneComposer;
this.actionPaneComposer = ActionPane.class;
this.activityAlias = activityAlias;
this.category = null;
this.readonly = true;
}
@ -196,6 +194,7 @@ public enum ActionDefinition {
this.contentPaneComposer = contentPaneComposer;
this.actionPaneComposer = ActionPane.class;
this.activityAlias = activityAlias;
this.category = null;
this.readonly = true;
}
@ -211,6 +210,7 @@ public enum ActionDefinition {
this.contentPaneComposer = contentPaneComposer;
this.actionPaneComposer = ActionPane.class;
this.activityAlias = activityAlias;
this.category = null;
this.readonly = readonly;
}

View file

@ -1,61 +0,0 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui.content.action;
public final class UserAccountActions {
// public static Action newUserAccount(final Action action) {
// return goToUserAccount(action, null, true);
// }
//
// public static Action viewUserAccountFromList(final Action action) {
// return fromSelection(action, false);
// }
//
// public static Action editUserAccountFromList(final Action action) {
// return fromSelection(action, true);
// }
//
// public static Action editUserAccount(final Action action) {
// return goToUserAccount(action, null, true);
// }
//
// public static Action cancelEditUserAccount(final Action action) {
// if (action.pageContext().getEntityKey() == null) {
// final Action toList = action.pageContext().createAction(ActionDefinition.USER_ACCOUNT_VIEW_LIST);
// action.pageContext().publishPageEvent(new ActionEvent(toList, false));
// return toList;
// } else {
// return goToUserAccount(action, null, false);
// }
// }
//
// private static Action fromSelection(final Action action, final boolean edit) {
// final Collection<String> selection = action.getSelectionSupplier().get();
// if (selection.isEmpty()) {
// throw new PageMessageException("sebserver.useraccount.info.pleaseSelect");
// }
//
// return goToUserAccount(action, selection.iterator().next(), edit);
// }
//
// private static Action goToUserAccount(
// final Action action,
// final String modelId,
// final boolean edit) {
//
// action.withAttribute(AttributeKeys.READ_ONLY, String.valueOf(!edit));
// if (modelId != null) {
// action.withEntity(new EntityKey(modelId, EntityType.USER));
// }
//
// return action;
// }
}

View file

@ -117,6 +117,14 @@ public class ActivitiesPane implements TemplateComposer {
.withAttribute(AttributeKeys.READ_ONLY, "true"));
}
// LMS Setup
final TreeItem userAccounts = this.widgetFactory.treeItemLocalized(
navigation,
ActionDefinition.LMS_SETUP_VIEW_LIST.title);
injectActivitySelection(
userAccounts,
pageContext.createAction(ActionDefinition.LMS_SETUP_VIEW_LIST));
navigation.addListener(SWT.Selection, event -> handleSelection(pageContext, event));
navigation.setData(
PageEventListener.LISTENER_ATTRIBUTE_KEY,

View file

@ -0,0 +1,144 @@
/*
* 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 java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTimeZone;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType;
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gbl.util.Tuple;
import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
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.CurrentUser;
@Lazy
@Service
@GuiProfile
public class ResourceService {
private final I18nSupport i18nSupport;
private final RestService restService;
private final CurrentUser currentUser;
protected ResourceService(
final I18nSupport i18nSupport,
final RestService restService,
final CurrentUser currentUser) {
this.i18nSupport = i18nSupport;
this.restService = restService;
this.currentUser = currentUser;
}
public I18nSupport getI18nSupport() {
return this.i18nSupport;
}
public RestService getRestService() {
return this.restService;
}
public CurrentUser getCurrentUser() {
return this.currentUser;
}
public Supplier<List<Tuple<String>>> localizedResourceSupplier(final List<Tuple<String>> source) {
return () -> source.stream()
.map(tuple -> new Tuple<>(tuple._1, this.i18nSupport.getText(tuple._2)))
.collect(Collectors.toList());
}
public List<Tuple<String>> lmsTypeResources() {
return Arrays.asList(LmsType.values())
.stream()
.map(lmsType -> new Tuple<>(
lmsType.name(),
this.i18nSupport.getText("sebserver.lmssetup.type." + lmsType.name())))
.collect(Collectors.toList());
}
public List<Tuple<String>> userRoleResources() {
return UserRole.publicRolesForUser(this.currentUser.get())
.stream()
.map(ur -> new Tuple<>(
ur.name(),
this.i18nSupport.getText("sebserver.useraccount.role." + ur.name())))
.collect(Collectors.toList());
}
public List<Tuple<String>> institutionResource() {
return this.restService.getBuilder(GetInstitutionNames.class)
.withQueryParam(Domain.INSTITUTION.ATTR_ACTIVE, "true")
.call()
.getOr(Collections.emptyList())
.stream()
.map(entityName -> new Tuple<>(entityName.modelId, entityName.name))
.collect(Collectors.toList());
}
public Function<String, String> getInstitutionNameFunction() {
final Map<String, String> idNameMap = this.restService.getBuilder(GetInstitutionNames.class)
.withQueryParam(Domain.INSTITUTION.ATTR_ACTIVE, "true")
.call()
.getOr(Collections.emptyList())
.stream()
.collect(Collectors.toMap(e -> e.modelId, e -> e.name));
return id -> {
if (!idNameMap.containsKey(id)) {
return Constants.EMPTY_NOTE;
}
return idNameMap.get(id);
};
}
/** Get a list of language key/name tuples for all supported languages in the
* language of the current users locale.
*
* @param i18nSupport I18nSupport to get the actual current users locale
* @return list of language key/name tuples for all supported languages in the language of the current users
* locale */
public List<Tuple<String>> languageResources() {
final Locale currentLocale = this.i18nSupport.getCurrentLocale();
return this.i18nSupport.supportedLanguages()
.stream()
.map(locale -> new Tuple<>(locale.toLanguageTag(), locale.getDisplayLanguage(currentLocale)))
.filter(tuple -> StringUtils.isNoneBlank(tuple._2))
.sorted((t1, t2) -> t1._2.compareTo(t2._2))
.collect(Collectors.toList());
}
public List<Tuple<String>> timeZoneResources() {
final Locale currentLocale = this.i18nSupport.getCurrentLocale();
return DateTimeZone
.getAvailableIDs()
.stream()
.map(id -> new Tuple<>(id, DateTimeZone.forID(id).getName(0, currentLocale) + " (" + id + ")"))
.sorted((t1, t2) -> t1._2.compareTo(t2._2))
.collect(Collectors.toList());
}
}

View file

@ -9,16 +9,9 @@
package ch.ethz.seb.sebserver.gui.service.i18n;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import ch.ethz.seb.sebserver.gbl.util.Tuple;
public interface I18nSupport {
@ -79,46 +72,4 @@ public interface I18nSupport {
* @return the text in current language parsed from localized text */
String getText(String key, Locale locale, String def, Object... args);
default Supplier<List<Tuple<String>>> localizedResourceSupplier(final List<Tuple<String>> source) {
return () -> source.stream()
.map(tuple -> new Tuple<>(tuple._1, getText(tuple._2)))
.collect(Collectors.toList());
}
default Supplier<List<Tuple<String>>> localizedLanguageResources() {
return () -> getLanguageResources(this);
}
default Supplier<List<Tuple<String>>> localizedTimeZoneResources() {
return () -> getTimeZoneResources(this);
}
Supplier<List<Tuple<String>>> localizedUserRoleResources();
/** Get a list of language key/name tuples for all supported languages in the
* language of the current users locale.
*
* @param i18nSupport I18nSupport to get the actual current users locale
* @return list of language key/name tuples for all supported languages in the language of the current users
* locale */
static List<Tuple<String>> getLanguageResources(final I18nSupport i18nSupport) {
final Locale currentLocale = i18nSupport.getCurrentLocale();
return i18nSupport.supportedLanguages()
.stream()
.map(locale -> new Tuple<>(locale.toLanguageTag(), locale.getDisplayLanguage(currentLocale)))
.filter(tuple -> StringUtils.isNoneBlank(tuple._2))
.sorted((t1, t2) -> t1._2.compareTo(t2._2))
.collect(Collectors.toList());
}
static List<Tuple<String>> getTimeZoneResources(final I18nSupport i18nSupport) {
final Locale currentLocale = i18nSupport.getCurrentLocale();
return DateTimeZone
.getAvailableIDs()
.stream()
.map(id -> new Tuple<>(id, DateTimeZone.forID(id).getName(0, currentLocale) + " (" + id + ")"))
.sorted((t1, t2) -> t1._2.compareTo(t2._2))
.collect(Collectors.toList());
}
}

View file

@ -10,10 +10,7 @@ package ch.ethz.seb.sebserver.gui.service.i18n.impl;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.eclipse.rap.rwt.RWT;
import org.joda.time.DateTime;
@ -26,8 +23,6 @@ import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
import ch.ethz.seb.sebserver.gbl.util.Tuple;
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.remote.webservice.auth.CurrentUser;
@ -133,13 +128,4 @@ public class I18nSupportImpl implements I18nSupport {
return this.messageSource.getMessage(key, args, def, locale);
}
@Override
public Supplier<List<Tuple<String>>> localizedUserRoleResources() {
final List<Tuple<String>> roles = UserRole.publicRolesForUser(this.currentUser.get())
.stream()
.map(ur -> new Tuple<>(ur.name(), "sebserver.useraccount.role." + ur.name()))
.collect(Collectors.toList());
return localizedResourceSupplier(roles);
}
}

View file

@ -8,30 +8,36 @@
package ch.ethz.seb.sebserver.gui.service.page;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.util.Tuple;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitutionDependency;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitutionNames;
public final class PageUtils {
private static final Logger log = LoggerFactory.getLogger(PageUtils.class);
public static void clearComposite(final Composite parent) {
if (parent == null) {
return;
}
for (final Control control : parent.getChildren()) {
control.dispose();
}
}
public static final Supplier<LocTextKey> confirmDeactivation(
final Entity entity,
final RestService restService) {
@ -56,14 +62,4 @@ public final class PageUtils {
};
}
public static List<Tuple<String>> getInstitutionSelectionResource(final RestService restService) {
return restService.getBuilder(GetInstitutionNames.class)
.withQueryParam(Domain.INSTITUTION.ATTR_ACTIVE, "true")
.call()
.getOr(Collections.emptyList())
.stream()
.map(entityName -> new Tuple<>(entityName.modelId, entityName.name))
.collect(Collectors.toList());
}
}

View file

@ -24,9 +24,9 @@ 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.PageUtils;
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.widget.WidgetFactory;
@Lazy
@Service
@ -113,7 +113,7 @@ public class ComposerServiceImpl implements ComposerService {
if (composer.validate(pageContext)) {
WidgetFactory.clearComposite(pageContext.getParent());
PageUtils.clearComposite(pageContext.getParent());
try {
composer.compose(pageContext);

View file

@ -1,26 +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.remote.webservice.api;
import ch.ethz.seb.sebserver.gbl.util.Result;
public class BuildErrorCall<T> extends RestCall<T> {
private final Throwable error;
protected BuildErrorCall(final Throwable error) {
super(null, null, null, null);
this.error = error;
}
@Override
protected Result<T> exchange(final RestCallBuilder builder) {
return Result.ofError(this.error);
}
}

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.lmssetup;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.type.TypeReference;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class GetLmsSetup extends RestCall<LmsSetup> {
protected GetLmsSetup() {
super(
new TypeReference<LmsSetup>() {
},
HttpMethod.GET,
MediaType.APPLICATION_FORM_URLENCODED,
API.LMS_SETUP_ENDPOINT + API.MODEL_ID_VAR_PATH_SEGMENT);
}
}

View file

@ -0,0 +1,38 @@
/*
* 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.lmssetup;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.type.TypeReference;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.model.Page;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class GetLmsSetups extends RestCall<Page<LmsSetup>> {
protected GetLmsSetups() {
super(
new TypeReference<Page<LmsSetup>>() {
},
HttpMethod.GET,
MediaType.APPLICATION_FORM_URLENCODED,
API.LMS_SETUP_ENDPOINT);
}
}

View file

@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.gui.table;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.util.Tuple;
@ -22,7 +23,7 @@ public final class ColumnDefinition<ROW extends Entity> {
final LocTextKey displayName;
final LocTextKey tooltip;
final int widthPercent;
final Function<ROW, Object> valueSupplier;
final Function<ROW, ?> valueSupplier;
final boolean sortable;
final TableFilterAttribute filterAttribute;
final boolean localized;
@ -51,7 +52,7 @@ public final class ColumnDefinition<ROW extends Entity> {
public ColumnDefinition(
final String columnName,
final LocTextKey displayName,
final Function<ROW, Object> valueSupplier,
final Function<ROW, ?> valueSupplier,
final boolean sortable) {
this(columnName, displayName, null, -1, valueSupplier, null, sortable, false);
@ -60,7 +61,7 @@ public final class ColumnDefinition<ROW extends Entity> {
public ColumnDefinition(
final String columnName,
final LocTextKey displayName,
final Function<ROW, Object> valueSupplier,
final Function<ROW, ?> valueSupplier,
final boolean sortable,
final boolean localized) {
@ -70,7 +71,7 @@ public final class ColumnDefinition<ROW extends Entity> {
public ColumnDefinition(
final String columnName,
final LocTextKey displayName,
final Function<ROW, Object> valueSupplier,
final Function<ROW, ?> valueSupplier,
final TableFilterAttribute tableFilterAttribute,
final boolean sortable) {
@ -80,7 +81,7 @@ public final class ColumnDefinition<ROW extends Entity> {
public ColumnDefinition(
final String columnName,
final LocTextKey displayName,
final Function<ROW, Object> valueSupplier,
final Function<ROW, ?> valueSupplier,
final TableFilterAttribute tableFilterAttribute,
final boolean sortable,
final boolean localized) {
@ -93,7 +94,7 @@ public final class ColumnDefinition<ROW extends Entity> {
final LocTextKey displayName,
final LocTextKey tooltip,
final int widthPercent,
final Function<ROW, Object> valueSupplier,
final Function<ROW, ?> valueSupplier,
final TableFilterAttribute filterAttribute,
final boolean sortable,
final boolean localized) {
@ -113,28 +114,30 @@ public final class ColumnDefinition<ROW extends Entity> {
public final CriteriaType type;
public final String columnName;
public final String initValue;
public final List<Tuple<String>> selectionResource;
public final Supplier<List<Tuple<String>>> resourceSupplier;
public TableFilterAttribute(final CriteriaType type, final String columnName) {
this(type, columnName, "", null);
}
public TableFilterAttribute(
final CriteriaType type,
final String columnName) {
final String columnName,
final Supplier<List<Tuple<String>>> resourceSupplier) {
this.type = type;
this.columnName = columnName;
this.initValue = "";
this.selectionResource = null;
this(type, columnName, "", resourceSupplier);
}
public TableFilterAttribute(
final CriteriaType type,
final String columnName,
final String initValue,
final List<Tuple<String>> selectionResource) {
final Supplier<List<Tuple<String>>> resourceSupplier) {
this.type = type;
this.columnName = columnName;
this.initValue = initValue;
this.selectionResource = selectionResource;
this.resourceSupplier = resourceSupplier;
}
}

View file

@ -35,6 +35,7 @@ import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.Page;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon;
@ -52,6 +53,7 @@ public class EntityTable<ROW extends Entity> {
final List<ColumnDefinition<ROW>> columns;
final List<TableRowAction> actions;
final LocTextKey emptyMessage;
final Composite composite;
private final TableFilter<ROW> filter;
@ -71,39 +73,46 @@ public class EntityTable<ROW extends Entity> {
final WidgetFactory widgetFactory,
final List<ColumnDefinition<ROW>> columns,
final List<TableRowAction> actions,
final int pageSize) {
final int pageSize,
final LocTextKey emptyMessage) {
this.composite = new Composite(parent, type);
this.widgetFactory = widgetFactory;
this.restCall = restCall;
this.columns = Utils.immutableListOf(columns);
this.actions = Utils.immutableListOf(actions);
this.emptyMessage = emptyMessage;
this.composite.setLayout(new GridLayout());
final GridLayout layout = new GridLayout();
layout.horizontalSpacing = 0;
layout.verticalSpacing = 0;
this.composite.setLayout(layout);
GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, true);
gridData.heightHint = (pageSize + 1) * 40;
gridData.heightHint = (pageSize + 2) * 40;
this.composite.setLayoutData(gridData);
// TODO just for debugging, remove when tested
// this.composite.setBackground(new Color(parent.getDisplay(), new RGB(0, 200, 0)));
this.pageSize = pageSize;
this.filter = columns
.stream()
.map(column -> column.filterAttribute)
.filter(Objects::nonNull)
.findFirst()
.isPresent() ? new TableFilter<>(this) : null;
this.filter =
columns
.stream()
.map(column -> column.filterAttribute)
.filter(Objects::nonNull)
.findFirst()
.isPresent() ? new TableFilter<>(this) : null;
this.table = widgetFactory.tableLocalized(this.composite);
final GridLayout gridLayout = new GridLayout(columns.size(), true);
this.table.setLayout(gridLayout);
gridData = new GridData(SWT.FILL, SWT.CENTER, true, false);
gridData.heightHint = (pageSize + 1) * 27;
gridData = new GridData(SWT.FILL, SWT.TOP, true, false);
this.table.setLayoutData(gridData);
this.table.addListener(SWT.Resize, this::adaptColumnWidth);
@SuppressWarnings("unchecked")
final Consumer<Table> locFunction = (Consumer<Table>) this.table.getData(POLYGLOT_WIDGET_FUNCTION_KEY);
final Consumer<Table> newLocFunction = t -> {
updateValues();
updateValues(this);
locFunction.accept(t);
};
this.table.setData(POLYGLOT_WIDGET_FUNCTION_KEY, newLocFunction);
@ -121,6 +130,10 @@ public class EntityTable<ROW extends Entity> {
this.sortOrder);
}
public boolean hasAnyContent() {
return this.table.getItemCount() > 0;
}
public void setPageSize(final int pageSize) {
this.pageSize = pageSize;
updateTableRows(
@ -251,6 +264,19 @@ public class EntityTable<ROW extends Entity> {
}
private Page<ROW> createTableRowsFromPage(final Page<ROW> page) {
if (page.isEmpty()) {
final GridData gridData = (GridData) this.table.getLayoutData();
gridData.heightHint = 30;
return page;
}
final GridData gridData = (GridData) this.table.getLayoutData();
if (page.numberOfPages > 1) {
gridData.heightHint = (this.pageSize + 1) * 27;
} else {
gridData.heightHint = (page.content.size() + 1) * 27;
}
for (final ROW row : page.content) {
final TableItem item = new TableItem(this.table, SWT.NONE);
item.setData(TABLE_ROW_DATA, row);
@ -267,41 +293,6 @@ public class EntityTable<ROW extends Entity> {
return page;
}
private void updateValues() {
final TableItem[] items = this.table.getItems();
final TableColumn[] columns = this.table.getColumns();
for (int i = 0; i < columns.length; i++) {
final ColumnDefinition<ROW> columnDefinition = this.columns.get(i);
if (columnDefinition.localized) {
for (int j = 0; j < items.length; j++) {
@SuppressWarnings("unchecked")
final ROW rowData = (ROW) items[j].getData(TABLE_ROW_DATA);
setValueToCell(items[j], i, columnDefinition.valueSupplier.apply(rowData));
}
}
}
}
private void setValueToCell(final TableItem item, final int index, final Object value) {
if (value instanceof Boolean) {
addBooleanCell(item, index, value);
} else {
if (value != null) {
item.setText(index, String.valueOf(value));
} else {
item.setText(index, Constants.EMPTY_NOTE);
}
}
}
private void addBooleanCell(final TableItem item, final int index, final Object value) {
if ((Boolean) value) {
item.setImage(index, ImageIcon.ACTIVE.getImage(item.getDisplay()));
} else {
item.setImage(index, ImageIcon.INACTIVE.getImage(item.getDisplay()));
}
}
private void adaptColumnWidth(final Event event) {
try {
final int currentTableWidth = this.table.getParent().getClientArea().width;
@ -323,8 +314,8 @@ public class EntityTable<ROW extends Entity> {
}
// NOTE this.layout() triggers the navigation to disappear unexpectedly!?
this.table.layout(true, true);
//this.table.layout(true, true);
//this.composite.layout(true, true);
} catch (final Exception e) {
log.warn("Failed to adaptColumnWidth: ", e);
}
@ -339,7 +330,7 @@ public class EntityTable<ROW extends Entity> {
this.table.indexOf(tableColumn),
tableColumn.getWidth())) {
this.composite.layout(true, true);
//this.composite.layout(true, true);
}
}
}
@ -353,4 +344,39 @@ public class EntityTable<ROW extends Entity> {
return getRowData(item).getEntityKey();
}
private static <ROW extends Entity> void updateValues(final EntityTable<ROW> table) {
final TableItem[] items = table.table.getItems();
final TableColumn[] columns = table.table.getColumns();
for (int i = 0; i < columns.length; i++) {
final ColumnDefinition<ROW> columnDefinition = table.columns.get(i);
if (columnDefinition.localized) {
for (int j = 0; j < items.length; j++) {
@SuppressWarnings("unchecked")
final ROW rowData = (ROW) items[j].getData(TABLE_ROW_DATA);
setValueToCell(items[j], i, columnDefinition.valueSupplier.apply(rowData));
}
}
}
}
private static void setValueToCell(final TableItem item, final int index, final Object value) {
if (value instanceof Boolean) {
addBooleanCell(item, index, value);
} else {
if (value != null) {
item.setText(index, String.valueOf(value));
} else {
item.setText(index, Constants.EMPTY_NOTE);
}
}
}
private static void addBooleanCell(final TableItem item, final int index, final Object value) {
if ((Boolean) value) {
item.setImage(index, ImageIcon.ACTIVE.getImage(item.getDisplay()));
} else {
item.setImage(index, ImageIcon.INACTIVE.getImage(item.getDisplay()));
}
}
}

View file

@ -10,12 +10,14 @@ package ch.ethz.seb.sebserver.gui.table;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BooleanSupplier;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.model.Page;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@ -40,6 +42,7 @@ public class TableBuilder<ROW extends Entity> {
final RestCall<Page<ROW>> restCall;
final List<ColumnDefinition<ROW>> columns = new ArrayList<>();
final List<TableRowAction> actions = new ArrayList<>();
LocTextKey emptyMessage;
private int pageSize = -1;
private int type = SWT.NONE;
@ -52,6 +55,11 @@ public class TableBuilder<ROW extends Entity> {
this.restCall = restCall;
}
public TableBuilder<ROW> withEmptyMessage(final LocTextKey emptyMessage) {
this.emptyMessage = emptyMessage;
return this;
}
public TableBuilder<ROW> withPaging(final int pageSize) {
this.pageSize = pageSize;
return this;
@ -62,6 +70,16 @@ public class TableBuilder<ROW extends Entity> {
return this;
}
public TableBuilder<ROW> withColumnIf(
final BooleanSupplier condition,
final ColumnDefinition<ROW> columnDefinition) {
if (condition != null && condition.getAsBoolean()) {
this.columns.add(columnDefinition);
}
return this;
}
public TableBuilder<ROW> withAction(final TableRowAction action) {
this.actions.add(action);
return this;
@ -80,7 +98,8 @@ public class TableBuilder<ROW extends Entity> {
this.widgetFactory,
this.columns,
this.actions,
this.pageSize);
this.pageSize,
this.emptyMessage);
}
}

View file

@ -8,6 +8,7 @@
package ch.ethz.seb.sebserver.gui.table;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@ -34,13 +35,12 @@ public class TableFilter<ROW extends Entity> {
public static enum CriteriaType {
TEXT,
SINGLE_SELECTION,
COUNTRY_SELECTION,
DATE
}
private final Composite composite;
private final EntityTable<ROW> entityTable;
private final List<FilterComponent> components;
private final List<FilterComponent> components = new ArrayList<>();
TableFilter(final EntityTable<ROW> entityTable) {
this.composite = new Composite(entityTable.composite, SWT.NONE);
@ -51,22 +51,11 @@ public class TableFilter<ROW extends Entity> {
layout.wrap = false;
this.composite.setLayout(layout);
// TODO just for debugging, remove when tested
// this.composite.setBackground(new Color(entityTable.composite.getDisplay(), new RGB(0, 0, 200)));
this.entityTable = entityTable;
this.components = entityTable.columns
.stream()
.map(column -> column.filterAttribute)
//.filter(Objects::nonNull)
.map(this::createFilterComponent)
.map(comp -> comp.build(this.composite))
.map(comp -> comp.reset())
.collect(Collectors.toList());
final FilterComponent lastComp = this.components.get(this.components.size() - 1);
if (lastComp instanceof TableFilter.NullFilter) {
this.components.remove(lastComp);
}
addActions();
buildComponents();
}
public MultiValueMap<String, String> getFilterParameter() {
@ -86,6 +75,24 @@ public class TableFilter<ROW extends Entity> {
.forEach(comp -> comp.reset());
}
private void buildComponents() {
this.components.clear();
this.components.addAll(this.entityTable.columns
.stream()
.map(column -> column.filterAttribute)
.map(this::createFilterComponent)
.map(comp -> comp.build(this.composite))
.map(comp -> comp.reset())
.collect(Collectors.toList()));
final FilterComponent lastComp = this.components.get(this.components.size() - 1);
if (lastComp instanceof TableFilter.NullFilter) {
this.components.remove(lastComp);
}
addActions();
}
private FilterComponent createFilterComponent(final TableFilterAttribute attribute) {
if (attribute == null) {
return new NullFilter();
@ -94,8 +101,8 @@ public class TableFilter<ROW extends Entity> {
switch (attribute.type) {
case TEXT:
return new TextFilter(attribute);
case COUNTRY_SELECTION:
return new LanguageFilter(attribute);
case SINGLE_SELECTION:
return new Selection(attribute);
default:
throw new IllegalArgumentException("Unsupported FilterAttributeType: " + attribute.type);
}
@ -103,7 +110,11 @@ public class TableFilter<ROW extends Entity> {
boolean adaptColumnWidth(final int columnIndex, final int width) {
if (columnIndex < this.components.size()) {
return this.components.get(columnIndex).adaptWidth(width);
final boolean adaptWidth = this.components.get(columnIndex).adaptWidth(width);
if (adaptWidth) {
this.composite.layout();
}
return adaptWidth;
}
return false;
@ -229,11 +240,11 @@ public class TableFilter<ROW extends Entity> {
}
private class LanguageFilter extends FilterComponent {
private class Selection extends FilterComponent {
private SingleSelection selector;
protected SingleSelection selector;
LanguageFilter(final TableFilterAttribute attribute) {
Selection(final TableFilterAttribute attribute) {
super(attribute);
}
@ -242,9 +253,7 @@ public class TableFilter<ROW extends Entity> {
this.selector = TableFilter.this.entityTable.widgetFactory
.singleSelectionLocalized(
parent,
TableFilter.this.entityTable.widgetFactory
.getI18nSupport()
.localizedLanguageResources());
this.attribute.resourceSupplier);
this.selector.setLayoutData(this.rowData);
return this;
}
@ -265,5 +274,13 @@ public class TableFilter<ROW extends Entity> {
return null;
}
@Override
boolean adaptWidth(final int width) {
// NOTE: for some unknown reason RWT acts differently on width-property for text inputs and selectors
// this is to adjust selection filter criteria to the list column width
return super.adaptWidth(width + 25);
}
}
}

View file

@ -17,7 +17,8 @@ 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.widget.WidgetFactory;
import ch.ethz.seb.sebserver.gui.service.page.PageUtils;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
public class TableNavigator {
@ -28,7 +29,10 @@ public class TableNavigator {
TableNavigator(final EntityTable<?> entityTable) {
this.composite = new Composite(entityTable.composite, SWT.NONE);
this.composite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
this.composite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, true));
// TODO just for debugging, remove when tested
// this.composite.setBackground(new Color(entityTable.composite.getDisplay(), new RGB(200, 0, 0)));
final GridLayout layout = new GridLayout(3, true);
layout.marginLeft = 20;
@ -39,7 +43,18 @@ public class TableNavigator {
public Page<?> update(final Page<?> pageData) {
// clear all
WidgetFactory.clearComposite(this.composite);
PageUtils.clearComposite(this.composite);
if (pageData.isEmpty()) {
// show empty message
if (this.entityTable.emptyMessage != null) {
this.entityTable.widgetFactory.labelLocalized(
this.composite,
CustomVariant.TEXT_H3,
this.entityTable.emptyMessage);
}
return pageData;
}
final int pageNumber = pageData.getPageNumber();
final int numberOfPages = pageData.getNumberOfPages();
@ -47,15 +62,14 @@ public class TableNavigator {
createPagingHeader(pageNumber, numberOfPages);
final Composite numNav = new Composite(this.composite, SWT.NONE);
numNav.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
final GridData gridData = new GridData(SWT.CENTER, SWT.TOP, true, false);
numNav.setLayoutData(gridData);
final RowLayout rowLayout = new RowLayout(SWT.HORIZONTAL);
rowLayout.spacing = 5;
numNav.setLayout(rowLayout);
if (numberOfPages > 1) {
if (pageNumber > 1) {
createRewardLabel(pageNumber, numNav);
}
createBackwardLabel(pageNumber > 1, pageNumber, numNav);
for (int i = pageNumber - PAGE_NAV_SIZE; i < pageNumber + PAGE_NAV_SIZE; i++) {
if (i >= 1 && i <= numberOfPages) {
@ -63,9 +77,7 @@ public class TableNavigator {
}
}
if (pageNumber < numberOfPages) {
createForwardLabel(pageNumber, numNav);
}
createForwardLabel(pageNumber < numberOfPages, pageNumber, numNav);
}
return pageData;
@ -89,7 +101,11 @@ public class TableNavigator {
}
}
private void createForwardLabel(final int pageNumber, final Composite parent) {
private void createForwardLabel(
final boolean visible,
final int pageNumber,
final Composite parent) {
final Label forward = new Label(parent, SWT.NONE);
forward.setText(">");
forward.setData(RWT.CUSTOM_VARIANT, "action");
@ -98,13 +114,21 @@ public class TableNavigator {
});
}
private void createRewardLabel(final int pageNumber, final Composite parent) {
final Label reward = new Label(parent, SWT.NONE);
reward.setText("<");
reward.setData(RWT.CUSTOM_VARIANT, "action");
reward.addListener(SWT.MouseDown, event -> {
this.entityTable.selectPage(pageNumber - 1);
});
private void createBackwardLabel(
final boolean visible,
final int pageNumber,
final Composite parent) {
final Label backward = new Label(parent, SWT.NONE);
backward.setText("<");
backward.setData(RWT.CUSTOM_VARIANT, "action");
if (visible) {
backward.addListener(SWT.MouseDown, event -> {
this.entityTable.selectPage(pageNumber - 1);
});
} else {
backward.setVisible(false);
}
}
}

View file

@ -23,6 +23,7 @@ import org.eclipse.swt.widgets.Label;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.util.Tuple;
import ch.ethz.seb.sebserver.gui.service.page.PageUtils;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
public class MultiSelection extends Composite implements Selection {
@ -48,7 +49,7 @@ public class MultiSelection extends Composite implements Selection {
final String selectionValue = getSelectionValue();
this.selected.clear();
this.labels.clear();
WidgetFactory.clearComposite(this);
PageUtils.clearComposite(this);
for (final Tuple<String> tuple : mapping) {
final Label label = new Label(this, SWT.NONE);
final GridData gridData = new GridData(SWT.FILL, SWT.CENTER, true, true);

View file

@ -25,7 +25,6 @@ import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
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;
@ -431,16 +430,6 @@ public class WidgetFactory {
}
}
public static void clearComposite(final Composite parent) {
if (parent == null) {
return;
}
for (final Control control : parent.getChildren()) {
control.dispose();
}
}
private static final Consumer<Label> labelFunction(
final LocTextKey locTextKey,
final LocTextKey locToolTipKey,

View file

@ -61,6 +61,7 @@ sebserver.actionpane.title=Actions
# Institution
################################
sebserver.institution.list.empty=No Institution has been found. Please adapt the filter or create a new Institution
sebserver.institution.list.title=Institutions
sebserver.institution.list.column.name=Name
sebserver.institution.list.column.urlSuffix=URL Suffix
@ -96,7 +97,9 @@ sebserver.useraccount.role.INSTITUTIONAL_ADMIN=Institutional Administrator
sebserver.useraccount.role.EXAM_ADMIN=Exam Administrator
sebserver.useraccount.role.EXAM_SUPPORTER=Exam Supporter
sebserver.useraccount.list.empty=No User Account has been found. Please adapt the filter or create a new User Account
sebserver.useraccount.list.title=User Accounts
sebserver.useraccount.list.column.institution=Institution
sebserver.useraccount.list.column.name=Name
sebserver.useraccount.list.column.username=User Name
sebserver.useraccount.list.column.email=Mail
@ -104,7 +107,7 @@ sebserver.useraccount.list.column.language=Language
sebserver.useraccount.list.column.active=Active
sebserver.useraccount.action.list=User Account
sebserver.useraccount.action.form=User Account
sebserver.useraccount.action.form=User Account of {0}
sebserver.useraccount.action.new=New User Account
sebserver.useraccount.action.view=View Selected
sebserver.useraccount.action.list.modify=Edit Selected
@ -118,7 +121,7 @@ sebserver.useraccount.action.change.password.save=Save New Password
sebserver.useraccount.info.pleaseSelect=Please Select a User Account first.
sebserver.useraccount.form.title=User Account : {0}
sebserver.useraccount.form.title=User Account
sebserver.useraccount.form.title.new=New User Account
sebserver.useraccount.form.institution=Institution
sebserver.useraccount.form.name=Name
@ -135,6 +138,25 @@ sebserver.useraccount.form.password.old=Old Password
sebserver.useraccount.form.password.new=New Password
sebserver.useraccount.form.password.new.confirm=Confirm New Password
################################
# LMS Setup
################################
sebserver.lmssetup.type.MOCKUP=Mock-up
sebserver.lmssetup.type.MOODLE=Moodle
sebserver.lmssetup.type.OPEN_EDX=Open edX
sebserver.lmssetup.list.empty=No LMS Setup has been found. Please adapt the filter or create a new LMS Setup
sebserver.lmssetup.list.title=LMS Setup
sebserver.lmssetup.list.column.institution=Institution
sebserver.lmssetup.list.column.name=Name
sebserver.lmssetup.list.column.type=LMS Type
sebserver.lmssetup.list.column.active=Active
sebserver.lmssetup.action.list=LMS Setups
sebserver.lmssetup.action.form=LMS Setup