From 98deb870ebb3dd03150b8788879c86187b2e9438 Mon Sep 17 00:00:00 2001 From: anhefti Date: Fri, 29 Mar 2019 14:34:51 +0100 Subject: [PATCH] SEBSERV-30 default action for list double click and code cleanup --- .../seb/sebserver/gui/content/ExamForm.java | 13 +- .../seb/sebserver/gui/content/ExamList.java | 69 ++++----- .../sebserver/gui/content/IndicatorForm.java | 4 +- .../gui/content/InstitutionForm.java | 4 +- .../gui/content/InstitutionList.java | 33 +++-- .../sebserver/gui/content/LmsSetupForm.java | 12 +- .../sebserver/gui/content/LmsSetupList.java | 69 ++++++--- .../gui/content/QuizDiscoveryList.java | 125 +++++++++++++--- .../UserAccountChangePasswordForm.java | 4 +- .../gui/content/UserAccountForm.java | 6 +- .../gui/content/UserAccountList.java | 140 +++++++++++------- .../gui/content/action/ActionDefinition.java | 12 ++ .../gui/content/action/ActionPane.java | 4 +- .../gui/content/activity/ActivitiesPane.java | 12 +- .../seb/sebserver/gui/form/FormBuilder.java | 6 +- .../seb/sebserver/gui/form/FormHandle.java | 8 +- .../sebserver/gui/form/TextFieldBuilder.java | 10 +- .../gui/service/page/ModalInputDialog.java | 68 +++++++-- .../{action/Action.java => PageAction.java} | 73 ++++----- .../gui/service/page/PageContext.java | 3 +- .../gui/service/page/event/ActionEvent.java | 6 +- .../page/event/ActionPublishEvent.java | 6 +- .../page/impl/ComposerServiceImpl.java | 4 +- .../gui/service/page/impl/MainPageState.java | 4 +- .../service/page/impl/PageContextImpl.java | 14 +- .../remote/webservice/api/RestService.java | 4 +- .../webservice/api/RestServiceImpl.java | 4 +- .../seb/sebserver/gui/table/EntityTable.java | 19 ++- .../seb/sebserver/gui/table/TableBuilder.java | 17 ++- .../sebserver/gui/widget/ColorSelection.java | 11 +- .../sebserver/gui/widget/WidgetFactory.java | 10 +- src/main/resources/messages.properties | 10 ++ 32 files changed, 529 insertions(+), 255 deletions(-) rename src/main/java/ch/ethz/seb/sebserver/gui/service/page/{action/Action.java => PageAction.java} (68%) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamForm.java index 25f86c01..ac72efe3 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamForm.java @@ -35,11 +35,11 @@ import ch.ethz.seb.sebserver.gui.form.PageFormService; 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.PageAction; import ch.ethz.seb.sebserver.gui.service.page.PageContext; import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys; import ch.ethz.seb.sebserver.gui.service.page.PageUtils; import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; -import ch.ethz.seb.sebserver.gui.service.page.action.Action; import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.DeleteIndicator; @@ -176,6 +176,7 @@ public class ExamForm implements TemplateComposer { QuizData.QUIZ_ATTR_DESCRIPTION, "sebserver.exam.form.description", exam.description) + .asArea() .readonly(true)) .addField(FormBuilder.text( QuizData.QUIZ_ATTR_START_TIME, @@ -273,7 +274,7 @@ public class ExamForm implements TemplateComposer { .createAction(ActionDefinition.EXAM_INDICATOR_MODIFY_FROM_LIST) .withParentEntityKey(entityKey) - .withSelect(indicatorTable::getSelection, Action::applySingleSelection, emptySelectionTextKey) + .withSelect(indicatorTable::getSelection, PageAction::applySingleSelection, emptySelectionTextKey) .publishIf(() -> modifyGrant && indicatorTable.hasAnyContent()) .createAction(ActionDefinition.EXAM_INDICATOR_DELETE_FROM_LIST) @@ -287,7 +288,7 @@ public class ExamForm implements TemplateComposer { } - private Action deleteSelectedIndicator(final Action action) { + private PageAction deleteSelectedIndicator(final PageAction action) { final EntityKey indicatorKey = action.getSingleSelection(); this.resourceService.getRestService() .getBuilder(DeleteIndicator.class) @@ -337,17 +338,17 @@ public class ExamForm implements TemplateComposer { .toString(); } - public static Action cancelModify(final Action action) { + public static PageAction cancelModify(final PageAction action) { final boolean importFromQuizData = BooleanUtils.toBoolean( action.pageContext().getAttribute(AttributeKeys.IMPORT_FROM_QUIZZ_DATA)); if (importFromQuizData) { final PageContext pageContext = action.pageContext(); - final Action activityHomeAction = pageContext.createAction(ActionDefinition.QUIZ_DISCOVERY_VIEW_LIST); + final PageAction activityHomeAction = pageContext.createAction(ActionDefinition.QUIZ_DISCOVERY_VIEW_LIST); action.pageContext().firePageEvent(new ActionEvent(activityHomeAction, false)); return activityHomeAction; } - return Action.onEmptyEntityKeyGoToActivityHome(action); + return PageAction.onEmptyEntityKeyGoToActivityHome(action); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamList.java index 48236a6c..6b8bd199 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/ExamList.java @@ -28,9 +28,9 @@ 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.I18nSupport; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.service.page.PageAction; 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.Action; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExams; import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; @@ -59,6 +59,13 @@ public class ExamList implements TemplateComposer { private final static LocTextKey columnTitleTypeKey = new LocTextKey("sebserver.exam.list.column.type"); + private final TableFilterAttribute institutionFilter; + private final TableFilterAttribute lmsFilter; + private final TableFilterAttribute nameFilter = + new TableFilterAttribute(CriteriaType.TEXT, QuizData.FILTER_ATTR_NAME); + private final TableFilterAttribute startTimeFilter = + new TableFilterAttribute(CriteriaType.DATE, QuizData.FILTER_ATTR_START_TIME); + protected ExamList( final WidgetFactory widgetFactory, final ResourceService resourceService, @@ -67,6 +74,16 @@ public class ExamList implements TemplateComposer { this.widgetFactory = widgetFactory; this.resourceService = resourceService; this.pageSize = (pageSize != null) ? pageSize : 20; + + this.institutionFilter = new TableFilterAttribute( + CriteriaType.SINGLE_SELECTION, + Entity.FILTER_ATTR_INSTITUTION, + this.resourceService::institutionResource); + + this.lmsFilter = new TableFilterAttribute( + CriteriaType.SINGLE_SELECTION, + LmsSetup.FILTER_ATTR_LMS_SETUP, + this.resourceService::lmsSetupResource); } @Override @@ -92,25 +109,19 @@ public class ExamList implements TemplateComposer { Domain.EXAM.ATTR_INSTITUTION_ID, new LocTextKey("sebserver.exam.list.column.institution"), examInstitutionNameFunction(this.resourceService), - new TableFilterAttribute( - CriteriaType.SINGLE_SELECTION, - Entity.FILTER_ATTR_INSTITUTION, - this.resourceService::institutionResource), + this.institutionFilter, false)) .withColumn(new ColumnDefinition<>( Domain.EXAM.ATTR_LMS_SETUP_ID, columnTitleLmsSetupKey, examLmsSetupNameFunction(this.resourceService), - new TableFilterAttribute( - CriteriaType.SINGLE_SELECTION, - LmsSetup.FILTER_ATTR_LMS_SETUP, - this.resourceService::lmsSetupResource), + this.lmsFilter, false)) .withColumn(new ColumnDefinition<>( QuizData.QUIZ_ATTR_NAME, columnTitleNameKey, Exam::getName, - new TableFilterAttribute(CriteriaType.TEXT, QuizData.FILTER_ATTR_NAME), + this.nameFilter, true)) .withColumn(new ColumnDefinition<>( QuizData.QUIZ_ATTR_START_TIME, @@ -118,32 +129,31 @@ public class ExamList implements TemplateComposer { "sebserver.exam.list.column.starttime", i18nSupport.getUsersTimeZoneTitleSuffix()), Exam::getStartTime, - new TableFilterAttribute(CriteriaType.DATE, QuizData.FILTER_ATTR_START_TIME), + this.startTimeFilter, true)) .withColumn(new ColumnDefinition<>( Domain.EXAM.ATTR_TYPE, columnTitleTypeKey, this::examTypeName, true)) + .withDefaultAction(pageContext + .clearEntityKeys() + .createAction(ActionDefinition.EXAM_VIEW_FROM_LIST)) .compose(content); // propagate content actions to action-pane final GrantCheck userGrant = currentUser.grantCheck(EntityType.EXAM); pageContext.clearEntityKeys() -// .createAction(ActionDefinition.TEST_ACTION) -// .withExec(this::testModalInput) -// .publish() - .createAction(ActionDefinition.EXAM_IMPORT) .publishIf(userGrant::im) .createAction(ActionDefinition.EXAM_VIEW_FROM_LIST) - .withSelect(table::getSelection, Action::applySingleSelection, emptySelectionTextKey) + .withSelect(table::getSelection, PageAction::applySingleSelection, emptySelectionTextKey) .publishIf(table::hasAnyContent) .createAction(ActionDefinition.EXAM_MODIFY_FROM_LIST) - .withSelect(table::getSelection, Action::applySingleSelection, emptySelectionTextKey) + .withSelect(table::getSelection, PageAction::applySingleSelection, emptySelectionTextKey) .publishIf(() -> userGrant.im() && table.hasAnyContent()); } @@ -167,29 +177,4 @@ public class ExamList implements TemplateComposer { .getText("sebserver.exam.type." + exam.type.name()); } -// private Action testModalInput(final Action action) { -// final ModalInputDialog dialog = new ModalInputDialog<>( -// action.pageContext().getParent().getShell(), -// this.widgetFactory); -// -// dialog.open( -// "Test Input Dialog", -// action.pageContext(), -// value -> { -// System.out.println("********************** value: " + value); -// }, -// pc -> { -// final Composite parent = pc.getParent(); -// final Label label = new Label(parent, SWT.NONE); -// label.setText("Please Enter:"); -// label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); -// -// final Text text = new Text(parent, SWT.LEFT | SWT.BORDER); -// text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); -// return () -> text.getText(); -// }); -// -// return action; -// } - } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/IndicatorForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/IndicatorForm.java index b2b21349..e8ca555f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/IndicatorForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/IndicatorForm.java @@ -26,9 +26,9 @@ 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.PageAction; 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.Action; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicator; @@ -144,7 +144,7 @@ public class IndicatorForm implements TemplateComposer { .createAction(ActionDefinition.EXAM_INDICATOR_CANCEL_MODIFY) .withEntityKey(parentEntityKey) - .withExec(Action::onEmptyEntityKeyGoToActivityHome) + .withExec(PageAction::onEmptyEntityKeyGoToActivityHome) .withConfirm("sebserver.overall.action.modify.cancel.confirm") .publishIf(() -> !isReadonly); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionForm.java index b1a10eb9..68b9d8a6 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionForm.java @@ -28,10 +28,10 @@ import ch.ethz.seb.sebserver.gui.form.FormBuilder; import ch.ethz.seb.sebserver.gui.form.FormHandle; import ch.ethz.seb.sebserver.gui.form.PageFormService; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.service.page.PageAction; import ch.ethz.seb.sebserver.gui.service.page.PageContext; import ch.ethz.seb.sebserver.gui.service.page.PageUtils; import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; -import ch.ethz.seb.sebserver.gui.service.page.action.Action; import ch.ethz.seb.sebserver.gui.service.remote.SebClientConfigDownload; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitution; @@ -180,7 +180,7 @@ public class InstitutionForm implements TemplateComposer { .createAction(ActionDefinition.INSTITUTION_CANCEL_MODIFY) .withEntityKey(entityKey) - .withExec(Action::onEmptyEntityKeyGoToActivityHome) + .withExec(PageAction::onEmptyEntityKeyGoToActivityHome) .withConfirm("sebserver.overall.action.modify.cancel.confirm") .publishIf(() -> !isReadonly); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionList.java index 20c178ca..bef5ba1e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionList.java @@ -18,9 +18,9 @@ import ch.ethz.seb.sebserver.gbl.model.institution.Institution; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.service.page.PageAction; 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.Action; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitutions; import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; @@ -34,6 +34,19 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; @GuiProfile public class InstitutionList implements TemplateComposer { + private static final LocTextKey EMPTY_LIST_TEXT_KEY = + new LocTextKey("sebserver.institution.list.empty"); + private static final LocTextKey TITLE_TEXT_KEY = + new LocTextKey("sebserver.institution.list.title"); + private static final LocTextKey NAME_TEXT_KEY = + new LocTextKey("sebserver.institution.list.column.name"); + private static final LocTextKey URL_TEXT_KEY = + new LocTextKey("sebserver.institution.list.column.urlSuffix"); + private static final LocTextKey ACTIVE_TEXT_KEY = + new LocTextKey("sebserver.institution.list.column.active"); + private static final LocTextKey EMPTY_SELECTION_TEXT_KEY = + new LocTextKey("sebserver.institution.info.pleaseSelect"); + private final WidgetFactory widgetFactory; private final RestService restService; private final CurrentUser currentUser; @@ -52,34 +65,36 @@ public class InstitutionList implements TemplateComposer { public void compose(final PageContext pageContext) { final Composite content = this.widgetFactory.defaultPageLayout( pageContext.getParent(), - new LocTextKey("sebserver.institution.list.title")); + TITLE_TEXT_KEY); // table final EntityTable table = this.widgetFactory.entityTableBuilder(this.restService.getRestCall(GetInstitutions.class)) - .withEmptyMessage(new LocTextKey("sebserver.institution.list.empty")) + .withEmptyMessage(EMPTY_LIST_TEXT_KEY) .withPaging(3) .withColumn(new ColumnDefinition<>( Domain.INSTITUTION.ATTR_NAME, - new LocTextKey("sebserver.institution.list.column.name"), + NAME_TEXT_KEY, entity -> entity.name, true)) .withColumn(new ColumnDefinition<>( Domain.INSTITUTION.ATTR_URL_SUFFIX, - new LocTextKey("sebserver.institution.list.column.urlSuffix"), + URL_TEXT_KEY, entity -> entity.urlSuffix, true)) .withColumn(new ColumnDefinition<>( Domain.INSTITUTION.ATTR_ACTIVE, - new LocTextKey("sebserver.institution.list.column.active"), + ACTIVE_TEXT_KEY, entity -> entity.active, true)) + .withDefaultAction(pageContext + .clearEntityKeys() + .createAction(ActionDefinition.INSTITUTION_VIEW_FROM_LIST)) .compose(content); // propagate content actions to action-pane final GrantCheck instGrant = this.currentUser.grantCheck(EntityType.INSTITUTION); final GrantCheck userGrant = this.currentUser.grantCheck(EntityType.USER); - final LocTextKey emptySelectionText = new LocTextKey("sebserver.institution.info.pleaseSelect"); pageContext.clearEntityKeys() .createAction(ActionDefinition.INSTITUTION_NEW) @@ -89,11 +104,11 @@ public class InstitutionList implements TemplateComposer { .publishIf(userGrant::w) .createAction(ActionDefinition.INSTITUTION_VIEW_FROM_LIST) - .withSelect(table::getSelection, Action::applySingleSelection, emptySelectionText) + .withSelect(table::getSelection, PageAction::applySingleSelection, EMPTY_SELECTION_TEXT_KEY) .publishIf(() -> table.hasAnyContent()) .createAction(ActionDefinition.INSTITUTION_MODIFY_FROM_LIST) - .withSelect(table::getSelection, Action::applySingleSelection, emptySelectionText) + .withSelect(table::getSelection, PageAction::applySingleSelection, EMPTY_SELECTION_TEXT_KEY) .publishIf(() -> instGrant.m() && table.hasAnyContent()); ; diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/LmsSetupForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/LmsSetupForm.java index 7a6fb26c..b7640ef7 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/LmsSetupForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/LmsSetupForm.java @@ -33,11 +33,11 @@ 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.PageAction; 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.PageUtils; import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; -import ch.ethz.seb.sebserver.gui.service.page.action.Action; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitution; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.GetLmsSetup; @@ -206,21 +206,21 @@ public class LmsSetupForm implements TemplateComposer { .createAction(ActionDefinition.LMS_SETUP_CANCEL_MODIFY) .withEntityKey(entityKey) - .withExec(Action::onEmptyEntityKeyGoToActivityHome) + .withExec(PageAction::onEmptyEntityKeyGoToActivityHome) .withConfirm("sebserver.overall.action.modify.cancel.confirm") .publishIf(() -> !readonly); } /** Save and test connection before activation */ - private Action activate(final Action action, final FormHandle formHandle) { + private PageAction activate(final PageAction action, final FormHandle formHandle) { final RestService restService = this.resourceService.getRestService(); - final Action testLmsSetup = this.testLmsSetup(action, formHandle); - final Action activation = restService.activation(testLmsSetup); + final PageAction testLmsSetup = this.testLmsSetup(action, formHandle); + final PageAction activation = restService.activation(testLmsSetup); return activation; } /** LmsSetup test action implementation */ - private Action testLmsSetup(final Action action, final FormHandle formHandle) { + private PageAction testLmsSetup(final PageAction action, final FormHandle formHandle) { // If we are in edit-mode we have to save the form before testing if (!action.pageContext().isReadonly()) { final Result postResult = formHandle.doAPIPost(); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/LmsSetupList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/LmsSetupList.java index 012ef558..db066f6b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/LmsSetupList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/LmsSetupList.java @@ -25,9 +25,9 @@ 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.PageAction; 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.Action; 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; @@ -43,6 +43,26 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; @GuiProfile public class LmsSetupList implements TemplateComposer { + private static final LocTextKey EMPTY_SELECTION_TEXT_KEY = + new LocTextKey("sebserver.lmssetup.info.pleaseSelect"); + private static final LocTextKey ACTIVITY_TEXT_KEY = + new LocTextKey("sebserver.lmssetup.list.column.active"); + private static final LocTextKey TYPE_TEXT_KEY = + new LocTextKey("sebserver.lmssetup.list.column.type"); + private static final LocTextKey NAME_TEXT_KEY = + new LocTextKey("sebserver.lmssetup.list.column.name"); + private static final LocTextKey INSTITUTION_TEXT_KEY = + new LocTextKey("sebserver.lmssetup.list.column.institution"); + private static final LocTextKey EMPTY_LIST_TEXT_KEY = + new LocTextKey("sebserver.lmssetup.list.empty"); + private static final LocTextKey TITLE_TEXT_KEY = + new LocTextKey("sebserver.lmssetup.list.title"); + + private final TableFilterAttribute institutionFilter; + private final TableFilterAttribute nameFilter = + new TableFilterAttribute(CriteriaType.TEXT, Entity.FILTER_ATTR_NAME); + private final TableFilterAttribute typeFilter; + private final WidgetFactory widgetFactory; private final ResourceService resourceService; private final int pageSize; @@ -55,6 +75,16 @@ public class LmsSetupList implements TemplateComposer { this.widgetFactory = widgetFactory; this.resourceService = resourceService; this.pageSize = (pageSize != null) ? pageSize : 20; + + this.institutionFilter = new TableFilterAttribute( + CriteriaType.SINGLE_SELECTION, + Entity.FILTER_ATTR_INSTITUTION, + this.resourceService::institutionResource); + + this.typeFilter = new TableFilterAttribute( + CriteriaType.SINGLE_SELECTION, + Domain.LMS_SETUP.ATTR_LMS_TYPE, + this.resourceService::lmsTypeResources); } @Override @@ -65,65 +95,58 @@ public class LmsSetupList implements TemplateComposer { // content page layout with title final Composite content = this.widgetFactory.defaultPageLayout( pageContext.getParent(), - new LocTextKey("sebserver.lmssetup.list.title")); + TITLE_TEXT_KEY); final boolean isSEBAdmin = currentUser.get().hasRole(UserRole.SEB_SERVER_ADMIN); // table + final EntityTable table = this.widgetFactory.entityTableBuilder(restService.getRestCall(GetLmsSetups.class)) - .withEmptyMessage(new LocTextKey("sebserver.lmssetup.list.empty")) + .withEmptyMessage(EMPTY_LIST_TEXT_KEY) .withPaging(this.pageSize) .withColumnIf(() -> isSEBAdmin, new ColumnDefinition<>( Domain.LMS_SETUP.ATTR_INSTITUTION_ID, - new LocTextKey("sebserver.lmssetup.list.column.institution"), + INSTITUTION_TEXT_KEY, lmsSetupInstitutionNameFunction(this.resourceService), - new TableFilterAttribute( - CriteriaType.SINGLE_SELECTION, - Entity.FILTER_ATTR_INSTITUTION, - this.resourceService::institutionResource), + this.institutionFilter, false)) .withColumn(new ColumnDefinition<>( Domain.LMS_SETUP.ATTR_NAME, - new LocTextKey("sebserver.lmssetup.list.column.name"), + NAME_TEXT_KEY, entity -> entity.name, - (isSEBAdmin) - ? new TableFilterAttribute(CriteriaType.TEXT, Entity.FILTER_ATTR_NAME) - : null, + (isSEBAdmin) ? this.nameFilter : null, true)) .withColumn(new ColumnDefinition<>( Domain.LMS_SETUP.ATTR_LMS_TYPE, - new LocTextKey("sebserver.lmssetup.list.column.type"), + TYPE_TEXT_KEY, this::lmsSetupTypeName, - (isSEBAdmin) - ? new TableFilterAttribute( - CriteriaType.SINGLE_SELECTION, - Domain.LMS_SETUP.ATTR_LMS_TYPE, - this.resourceService::lmsTypeResources) - : null, + (isSEBAdmin) ? this.typeFilter : null, false, true)) .withColumn(new ColumnDefinition<>( Domain.LMS_SETUP.ATTR_ACTIVE, - new LocTextKey("sebserver.lmssetup.list.column.active"), + ACTIVITY_TEXT_KEY, entity -> entity.active, true)) + .withDefaultAction(pageContext + .clearEntityKeys() + .createAction(ActionDefinition.LMS_SETUP_VIEW_FROM_LIST)) .compose(content); // propagate content actions to action-pane final GrantCheck userGrant = currentUser.grantCheck(EntityType.LMS_SETUP); - final LocTextKey emptySelectionText = new LocTextKey("sebserver.lmssetup.info.pleaseSelect"); pageContext.clearEntityKeys() .createAction(ActionDefinition.LMS_SETUP_NEW) .publishIf(userGrant::iw) .createAction(ActionDefinition.LMS_SETUP_VIEW_FROM_LIST) - .withSelect(table::getSelection, Action::applySingleSelection, emptySelectionText) + .withSelect(table::getSelection, PageAction::applySingleSelection, EMPTY_SELECTION_TEXT_KEY) .publishIf(() -> table.hasAnyContent()) .createAction(ActionDefinition.LMS_SETUP_MODIFY_FROM_LIST) - .withSelect(table::getSelection, Action::applySingleSelection, emptySelectionText) + .withSelect(table::getSelection, PageAction::applySingleSelection, EMPTY_SELECTION_TEXT_KEY) .publishIf(() -> userGrant.im() && table.hasAnyContent()); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/QuizDiscoveryList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/QuizDiscoveryList.java index 643989db..05713140 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/QuizDiscoveryList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/QuizDiscoveryList.java @@ -21,13 +21,16 @@ import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; +import ch.ethz.seb.sebserver.gui.form.FormBuilder; +import ch.ethz.seb.sebserver.gui.form.PageFormService; 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.ModalInputDialog; +import ch.ethz.seb.sebserver.gui.service.page.PageAction; import ch.ethz.seb.sebserver.gui.service.page.PageContext; import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys; import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; -import ch.ethz.seb.sebserver.gui.service.page.action.Action; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.quiz.GetQuizzes; import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; @@ -43,25 +46,48 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; @GuiProfile public class QuizDiscoveryList implements TemplateComposer { + // localized text keys + private static final LocTextKey TITLE_TEXT_KEY = + new LocTextKey("sebserver.quizdiscovery.list.title"); + private static final LocTextKey EMPTY_LIST_TEXT_KEY = + new LocTextKey("sebserver.quizdiscovery.list.empty"); + private final static LocTextKey EMPTY_SELECTION_TEXT = + new LocTextKey("sebserver.quizdiscovery.info.pleaseSelect"); + private final static LocTextKey LMS_TEXT_KEY = + new LocTextKey("sebserver.quizdiscovery.list.column.lmssetup"); + private final static LocTextKey NAME_TEXT_KEY = + new LocTextKey("sebserver.quizdiscovery.list.column.name"); + private final static LocTextKey DETAILS_TITLE_TEXT_KEY = + new LocTextKey("sebserver.quizdiscovery.quiz.details.title"); + + // filter attribute models + private final TableFilterAttribute lmsFilter; + private final TableFilterAttribute nameFilter = + new TableFilterAttribute(CriteriaType.TEXT, QuizData.FILTER_ATTR_NAME); + private final TableFilterAttribute startTimeFilter = + new TableFilterAttribute(CriteriaType.DATE, QuizData.FILTER_ATTR_START_TIME); + + // dependencies private final WidgetFactory widgetFactory; private final ResourceService resourceService; + private final PageFormService pageFormService; private final int pageSize; - private final static LocTextKey emptySelectionText = - new LocTextKey("sebserver.quizdiscovery.info.pleaseSelect"); - private final static LocTextKey columnTitleLmsSetup = - new LocTextKey("sebserver.quizdiscovery.list.column.lmssetup"); - private final static LocTextKey columnTitleName = - new LocTextKey("sebserver.quizdiscovery.list.column.name"); - protected QuizDiscoveryList( + final PageFormService pageFormService, final WidgetFactory widgetFactory, final ResourceService resourceService, @Value("${sebserver.gui.list.page.size}") final Integer pageSize) { + this.pageFormService = pageFormService; this.widgetFactory = widgetFactory; this.resourceService = resourceService; this.pageSize = (pageSize != null) ? pageSize : 20; + + this.lmsFilter = new TableFilterAttribute( + CriteriaType.SINGLE_SELECTION, + LmsSetup.FILTER_ATTR_LMS_SETUP, + this.resourceService::lmsSetupResource); } @Override @@ -73,27 +99,24 @@ public class QuizDiscoveryList implements TemplateComposer { // content page layout with title final Composite content = this.widgetFactory.defaultPageLayout( pageContext.getParent(), - new LocTextKey("sebserver.quizdiscovery.list.title")); + TITLE_TEXT_KEY); // table final EntityTable table = this.widgetFactory.entityTableBuilder(restService.getRestCall(GetQuizzes.class)) - .withEmptyMessage(new LocTextKey("sebserver.quizdiscovery.list.empty")) + .withEmptyMessage(EMPTY_LIST_TEXT_KEY) .withPaging(this.pageSize) .withColumn(new ColumnDefinition<>( QuizData.QUIZ_ATTR_LMS_SETUP_ID, - columnTitleLmsSetup, + LMS_TEXT_KEY, quizDataLmsSetupNameFunction(this.resourceService), - new TableFilterAttribute( - CriteriaType.SINGLE_SELECTION, - LmsSetup.FILTER_ATTR_LMS_SETUP, - this.resourceService::lmsSetupResource), + this.lmsFilter, false)) .withColumn(new ColumnDefinition<>( QuizData.QUIZ_ATTR_NAME, - columnTitleName, + NAME_TEXT_KEY, quizData -> quizData.name, - new TableFilterAttribute(CriteriaType.TEXT, QuizData.FILTER_ATTR_NAME), + this.nameFilter, true)) .withColumn(new ColumnDefinition<>( QuizData.QUIZ_ATTR_START_TIME, @@ -101,7 +124,7 @@ public class QuizDiscoveryList implements TemplateComposer { "sebserver.quizdiscovery.list.column.starttime", i18nSupport.getUsersTimeZoneTitleSuffix()), quizData -> quizData.startTime, - new TableFilterAttribute(CriteriaType.DATE, QuizData.FILTER_ATTR_START_TIME), + this.startTimeFilter, true)) .withColumn(new ColumnDefinition<>( QuizData.QUIZ_ATTR_END_TIME, @@ -110,6 +133,11 @@ public class QuizDiscoveryList implements TemplateComposer { i18nSupport.getUsersTimeZoneTitleSuffix()), quizData -> quizData.endTime, true)) + .withDefaultAction(t -> pageContext + .clearEntityKeys() + .createAction(ActionDefinition.QUIZ_DISCOVERY_SHOW_DETAILS) + .withExec(action -> this.showDetails(action, t.getSelectedROWData())) + .noEventPropagation()) .compose(content); // propagate content actions to action-pane @@ -120,11 +148,19 @@ public class QuizDiscoveryList implements TemplateComposer { .createAction(ActionDefinition.LMS_SETUP_NEW) .publishIf(lmsSetupGrant::iw) + .createAction(ActionDefinition.QUIZ_DISCOVERY_SHOW_DETAILS) + .withSelect( + table::getSelection, + action -> this.showDetails(action, table.getSelectedROWData()), + EMPTY_SELECTION_TEXT) + .noEventPropagation() + .publishIf(table::hasAnyContent) + .createAction(ActionDefinition.QUIZ_DISCOVERY_EXAM_IMPORT) .withSelect( table::getSelection, action -> this.importQuizData(action, table), - emptySelectionText) + EMPTY_SELECTION_TEXT) .publishIf(() -> examGrant.im() && table.hasAnyContent()); } @@ -133,7 +169,7 @@ public class QuizDiscoveryList implements TemplateComposer { .apply(String.valueOf(quizzData.lmsSetupId)); } - private Action importQuizData(final Action action, final EntityTable table) { + private PageAction importQuizData(final PageAction action, final EntityTable table) { final QuizData selectedROWData = table.getSelectedROWData(); return action @@ -142,4 +178,53 @@ public class QuizDiscoveryList implements TemplateComposer { .withAttribute(AttributeKeys.IMPORT_FROM_QUIZZ_DATA, "true"); } + private PageAction showDetails(final PageAction action, final QuizData quizData) { + action.getSingleSelection(); + + final ModalInputDialog dialog = new ModalInputDialog<>( + action.pageContext().getParent().getShell(), + this.widgetFactory); + + dialog.open( + DETAILS_TITLE_TEXT_KEY, + action.pageContext(), + pc -> createDetailsForm(quizData, pc)); + + return action; + } + + private void createDetailsForm(final QuizData quizData, final PageContext pc) { + this.widgetFactory.labelSeparator(pc.getParent()); + this.pageFormService.getBuilder(pc, 4) + .readonly(true) + .addField(FormBuilder.singleSelection( + QuizData.QUIZ_ATTR_LMS_SETUP_ID, + "sebserver.quizdiscovery.quiz.details.lms", + String.valueOf(quizData.lmsSetupId), + () -> this.resourceService.lmsSetupResource())) + .addField(FormBuilder.text( + QuizData.QUIZ_ATTR_NAME, + "sebserver.quizdiscovery.quiz.details.name", + quizData.name)) + .addField(FormBuilder.text( + QuizData.QUIZ_ATTR_DESCRIPTION, + "sebserver.quizdiscovery.quiz.details.description", + quizData.description) + .asArea()) + .addField(FormBuilder.text( + QuizData.QUIZ_ATTR_START_TIME, + "sebserver.quizdiscovery.quiz.details.starttime", + this.widgetFactory.getI18nSupport().formatDisplayDate(quizData.startTime))) + .addField(FormBuilder.text( + QuizData.QUIZ_ATTR_END_TIME, + "sebserver.quizdiscovery.quiz.details.endtime", + this.widgetFactory.getI18nSupport().formatDisplayDate(quizData.startTime))) + .addField(FormBuilder.text( + QuizData.QUIZ_ATTR_START_URL, + "sebserver.quizdiscovery.quiz.details.url", + quizData.startURL)) + .build(); + this.widgetFactory.labelSeparator(pc.getParent()); + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountChangePasswordForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountChangePasswordForm.java index 3d8ee63f..dd3bb323 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountChangePasswordForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountChangePasswordForm.java @@ -26,9 +26,9 @@ import ch.ethz.seb.sebserver.gui.form.FormHandle; import ch.ethz.seb.sebserver.gui.form.PageFormService; 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.PageAction; 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.Action; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.ChangePassword; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccount; @@ -122,7 +122,7 @@ public class UserAccountChangePasswordForm implements TemplateComposer { }) .publish() .createAction(ActionDefinition.USER_ACCOUNT_CANCEL_MODIFY) - .withExec(Action::onEmptyEntityKeyGoToActivityHome) + .withExec(PageAction::onEmptyEntityKeyGoToActivityHome) .withConfirm("sebserver.overall.action.modify.cancel.confirm") .publish(); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountForm.java index cf3e4609..51c5977f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountForm.java @@ -34,10 +34,10 @@ 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.PageAction; import ch.ethz.seb.sebserver.gui.service.page.PageContext; import ch.ethz.seb.sebserver.gui.service.page.PageUtils; import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; -import ch.ethz.seb.sebserver.gui.service.page.action.Action; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitution; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccount; @@ -214,7 +214,7 @@ public class UserAccountForm implements TemplateComposer { .createAction(ActionDefinition.USER_ACCOUNT_SAVE) .withEntityKey(entityKey) .withExec(action -> { - final Action postChanges = formHandle.processFormSave(action); + final PageAction postChanges = formHandle.processFormSave(action); if (ownAccount) { currentUser.refresh(); pageContext.forwardToMainPage(); @@ -225,7 +225,7 @@ public class UserAccountForm implements TemplateComposer { .createAction(ActionDefinition.USER_ACCOUNT_CANCEL_MODIFY) .withEntityKey(entityKey) - .withExec(Action::onEmptyEntityKeyGoToActivityHome) + .withExec(PageAction::onEmptyEntityKeyGoToActivityHome) .withConfirm("sebserver.overall.action.modify.cancel.confirm") .publishIf(() -> !readonly); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountList.java index 755ad0b8..b949df49 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/UserAccountList.java @@ -26,9 +26,9 @@ 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.PageAction; 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.Action; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount.GetUserAccounts; import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; @@ -44,6 +44,35 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; @GuiProfile public class UserAccountList implements TemplateComposer { + // localized text keys + private static final LocTextKey INSTITUTION_TEXT_KEY = + new LocTextKey("sebserver.useraccount.list.column.institution"); + private static final LocTextKey EMPTY_SELECTION_TEXT_KEY = + new LocTextKey("sebserver.useraccount.info.pleaseSelect"); + private static final LocTextKey ACTIVE_TEXT_KEY = + new LocTextKey("sebserver.useraccount.list.column.active"); + private static final LocTextKey LANG_TEXT_KEY = + new LocTextKey("sebserver.useraccount.list.column.language"); + private static final LocTextKey MAIL_TEXT_KEY = + new LocTextKey("sebserver.useraccount.list.column.email"); + private static final LocTextKey USER_NAME_TEXT_KEY = + new LocTextKey("sebserver.useraccount.list.column.username"); + private static final LocTextKey NAME_TEXT_KEY = + new LocTextKey("sebserver.useraccount.list.column.name"); + private static final LocTextKey TITLE_TEXT_KEY = + new LocTextKey("sebserver.useraccount.list.title"); + + // filter attribute models + private final TableFilterAttribute institutionFilter; + private final TableFilterAttribute nameFilter = + new TableFilterAttribute(CriteriaType.TEXT, Entity.FILTER_ATTR_NAME); + private final TableFilterAttribute usernameFilter = + new TableFilterAttribute(CriteriaType.TEXT, UserInfo.FILTER_ATTR_USER_NAME); + private final TableFilterAttribute mailFilter = + new TableFilterAttribute(CriteriaType.TEXT, UserInfo.FILTER_ATTR_EMAIL); + private final TableFilterAttribute languageFilter; + + // dependencies private final WidgetFactory widgetFactory; private final ResourceService resourceService; private final int pageSize; @@ -56,6 +85,16 @@ public class UserAccountList implements TemplateComposer { this.widgetFactory = widgetFactory; this.resourceService = resourceService; this.pageSize = (pageSize != null) ? pageSize : 20; + + this.institutionFilter = new TableFilterAttribute( + CriteriaType.SINGLE_SELECTION, + Entity.FILTER_ATTR_INSTITUTION, + this.resourceService::institutionResource); + + this.languageFilter = new TableFilterAttribute( + CriteriaType.SINGLE_SELECTION, + UserInfo.FILTER_ATTR_LANGUAGE, + this.resourceService::languageResources); } @Override @@ -65,73 +104,70 @@ public class UserAccountList implements TemplateComposer { // content page layout with title final Composite content = this.widgetFactory.defaultPageLayout( pageContext.getParent(), - new LocTextKey("sebserver.useraccount.list.title")); + TITLE_TEXT_KEY); final BooleanSupplier isSEBAdmin = () -> currentUser.get().hasRole(UserRole.SEB_SERVER_ADMIN); // table - final EntityTable table = - 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, - Entity.FILTER_ATTR_INSTITUTION, - this.resourceService::institutionResource), - false)) - .withColumn(new ColumnDefinition<>( - Domain.USER.ATTR_NAME, - new LocTextKey("sebserver.useraccount.list.column.name"), - entity -> entity.name, - new TableFilterAttribute(CriteriaType.TEXT, Entity.FILTER_ATTR_NAME), - true)) - .withColumn(new ColumnDefinition<>( - Domain.USER.ATTR_USERNAME, - new LocTextKey("sebserver.useraccount.list.column.username"), - entity -> entity.username, - new TableFilterAttribute(CriteriaType.TEXT, UserInfo.FILTER_ATTR_USER_NAME), - true)) - .withColumn(new ColumnDefinition<>( - Domain.USER.ATTR_EMAIL, - new LocTextKey("sebserver.useraccount.list.column.email"), - entity -> entity.email, - new TableFilterAttribute(CriteriaType.TEXT, UserInfo.FILTER_ATTR_EMAIL), - true)) - .withColumn(new ColumnDefinition<>( - Domain.USER.ATTR_LANGUAGE, - new LocTextKey("sebserver.useraccount.list.column.language"), - this::getLocaleDisplayText, - new TableFilterAttribute( - CriteriaType.SINGLE_SELECTION, - UserInfo.FILTER_ATTR_LANGUAGE, - this.resourceService::languageResources), - true, true)) - .withColumn(new ColumnDefinition<>( - Domain.USER.ATTR_ACTIVE, - new LocTextKey("sebserver.useraccount.list.column.active"), - entity -> entity.active, - true)) - .compose(content); + final EntityTable table = 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, + INSTITUTION_TEXT_KEY, + userInstitutionNameFunction(this.resourceService), + this.institutionFilter, + false)) + .withColumn(new ColumnDefinition<>( + Domain.USER.ATTR_NAME, + NAME_TEXT_KEY, + entity -> entity.name, + this.nameFilter, + true)) + .withColumn(new ColumnDefinition<>( + Domain.USER.ATTR_USERNAME, + USER_NAME_TEXT_KEY, + entity -> entity.username, + this.usernameFilter, + true)) + .withColumn(new ColumnDefinition<>( + Domain.USER.ATTR_EMAIL, + MAIL_TEXT_KEY, + entity -> entity.email, + this.mailFilter, + true)) + .withColumn(new ColumnDefinition<>( + Domain.USER.ATTR_LANGUAGE, + LANG_TEXT_KEY, + this::getLocaleDisplayText, + this.languageFilter, + true, true)) + .withColumn(new ColumnDefinition<>( + Domain.USER.ATTR_ACTIVE, + ACTIVE_TEXT_KEY, + entity -> entity.active, + true)) + .withDefaultAction(pageContext + .clearEntityKeys() + .createAction(ActionDefinition.USER_ACCOUNT_VIEW_FROM_LIST)) + .compose(content); // propagate content actions to action-pane final GrantCheck userGrant = currentUser.grantCheck(EntityType.USER); - final LocTextKey emptySelectionText = new LocTextKey("sebserver.useraccount.info.pleaseSelect"); pageContext.clearEntityKeys() .createAction(ActionDefinition.USER_ACCOUNT_NEW) .publishIf(userGrant::iw) .createAction(ActionDefinition.USER_ACCOUNT_VIEW_FROM_LIST) - .withSelect(table::getSelection, Action::applySingleSelection, emptySelectionText) + .withSelect(table::getSelection, PageAction::applySingleSelection, EMPTY_SELECTION_TEXT_KEY) .publishIf(() -> table.hasAnyContent()) .createAction(ActionDefinition.USER_ACCOUNT_MODIFY_FROM_LIST) - .withSelect(table::getSelection, Action::applySingleSelection, emptySelectionText) + .withSelect(table::getSelection, PageAction::applySingleSelection, EMPTY_SELECTION_TEXT_KEY) .publishIf(() -> userGrant.im() && table.hasAnyContent()); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java index 0824f29d..e2393c67 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java @@ -251,6 +251,10 @@ public enum ActionDefinition { QUIZ_DISCOVERY_VIEW_LIST( new LocTextKey("sebserver.quizdiscovery.action.list"), QuizDiscoveryList.class), + QUIZ_DISCOVERY_SHOW_DETAILS( + new LocTextKey("sebserver.quizdiscovery.action.details"), + ImageIcon.SHOW, + ActionCategory.QUIZ_LIST), QUIZ_DISCOVERY_EXAM_IMPORT( new LocTextKey("sebserver.quizdiscovery.action.import"), ImageIcon.IMPORT, @@ -382,6 +386,14 @@ public enum ActionDefinition { this(title, null, contentPaneComposer, ActionPane.class, null, activityAlias, null, null); } + private ActionDefinition( + final LocTextKey title, + final ImageIcon icon, + final ActionCategory category) { + + this(title, icon, null, ActionPane.class, null, null, category, null); + } + private ActionDefinition( final LocTextKey title, final ImageIcon icon, diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionPane.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionPane.java index 4b6c6b37..96b9e009 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionPane.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionPane.java @@ -28,9 +28,9 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.service.page.PageAction; 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.Action; 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; @@ -160,7 +160,7 @@ public class ActionPane implements TemplateComposer { actions.addListener(SWT.Selection, event -> { final TreeItem treeItem = (TreeItem) event.item; - final Action action = (Action) treeItem.getData(ACTION_EVENT_CALL_KEY); + final PageAction action = (PageAction) treeItem.getData(ACTION_EVENT_CALL_KEY); action.run(); if (!treeItem.isDisposed()) { diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/ActivitiesPane.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/ActivitiesPane.java index 4a530499..bee8f085 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/ActivitiesPane.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/ActivitiesPane.java @@ -24,10 +24,10 @@ import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; import ch.ethz.seb.sebserver.gbl.model.user.UserRole; import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.service.page.PageAction; import ch.ethz.seb.sebserver.gui.service.page.PageContext; import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys; import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; -import ch.ethz.seb.sebserver.gui.service.page.action.Action; import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent; import ch.ethz.seb.sebserver.gui.service.page.event.ActionEventListener; import ch.ethz.seb.sebserver.gui.service.page.event.PageEventListener; @@ -174,7 +174,7 @@ public class ActivitiesPane implements TemplateComposer { System.out.println("selected: " + treeItem); final MainPageState mainPageState = MainPageState.get(); - final Action action = getActivitySelection(treeItem); + final PageAction action = getActivitySelection(treeItem); if (mainPageState.action.definition != action.definition) { mainPageState.action = action; composerCtx.firePageEvent( @@ -192,7 +192,7 @@ public class ActivitiesPane implements TemplateComposer { } for (final TreeItem item : items) { - final Action action = getActivitySelection(item); + final PageAction action = getActivitySelection(item); if (action == null) { continue; } @@ -225,11 +225,11 @@ public class ActivitiesPane implements TemplateComposer { expand(item.getParentItem()); } - public static Action getActivitySelection(final TreeItem item) { - return (Action) item.getData(ATTR_ACTIVITY_SELECTION); + public static PageAction getActivitySelection(final TreeItem item) { + return (PageAction) item.getData(ATTR_ACTIVITY_SELECTION); } - public static void injectActivitySelection(final TreeItem item, final Action action) { + public static void injectActivitySelection(final TreeItem item, final PageAction action) { item.setData(ATTR_ACTIVITY_SELECTION, action); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/form/FormBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/form/FormBuilder.java index fbbab6f5..25a2a0c2 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/form/FormBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/form/FormBuilder.java @@ -71,7 +71,7 @@ public class FormBuilder { layout.horizontalSpacing = 10; layout.verticalSpacing = 10; layout.marginLeft = 10; - layout.marginTop = 10; + layout.marginTop = 0; this.formParent.setLayout(layout); this.formParent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); } @@ -169,6 +169,10 @@ public class FormBuilder { return this; } + public FormHandle build() { + return buildFor(null); + } + public FormHandle buildFor( final RestCall post) { diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/form/FormHandle.java b/src/main/java/ch/ethz/seb/sebserver/gui/form/FormHandle.java index e599f15f..3d70cadd 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/form/FormHandle.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/form/FormHandle.java @@ -19,10 +19,10 @@ import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gui.form.Form.FormFieldAccessor; 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.PageAction; import ch.ethz.seb.sebserver.gui.service.page.FieldValidationError; import ch.ethz.seb.sebserver.gui.service.page.PageContext; import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys; -import ch.ethz.seb.sebserver.gui.service.page.action.Action; import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCallError; @@ -57,7 +57,7 @@ public class FormHandle { * * @param action the save action context * @return the new Action context for read-only-view */ - public final Action processFormSave(final Action action) { + public final PageAction processFormSave(final PageAction action) { return handleFormPost(doAPIPost(), action); } @@ -86,10 +86,10 @@ public class FormHandle { * @param postResult The form post result * @param action the action that was applied with the form post * @return the new Action that was used to stay on page or go the read-only-view of the form */ - public Action handleFormPost(final Result postResult, final Action action) { + public PageAction handleFormPost(final Result postResult, final PageAction action) { return postResult .map(result -> { - Action resultAction = action.createNew() + PageAction resultAction = action.createNew() .withAttribute(AttributeKeys.READ_ONLY, "true"); if (resultAction.getEntityKey() == null) { resultAction = resultAction.withEntityKey(result.getEntityKey()); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/form/TextFieldBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/form/TextFieldBuilder.java index c34581fd..3f0c56cd 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/form/TextFieldBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/form/TextFieldBuilder.java @@ -17,6 +17,7 @@ public final class TextFieldBuilder extends FieldBuilder { boolean isPassword = false; boolean isNumber = false; + boolean isArea = false; TextFieldBuilder(final String name, final String label, final String value) { super(name, label, value); @@ -32,6 +33,11 @@ public final class TextFieldBuilder extends FieldBuilder { return this; } + public TextFieldBuilder asArea() { + this.isArea = true; + return this; + } + @Override void build(final FormBuilder builder) { if (this.isPassword && builder.readonly) { @@ -46,7 +52,9 @@ public final class TextFieldBuilder extends FieldBuilder { } else { final Text textInput = (this.isNumber) ? builder.widgetFactory.numberInput(builder.formParent, null) - : builder.widgetFactory.textInput(builder.formParent, this.isPassword); + : (this.isArea) + ? builder.widgetFactory.textAreaInput(builder.formParent) + : builder.widgetFactory.textInput(builder.formParent, this.isPassword); final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false, this.spanInput, 1); textInput.setLayoutData(gridData); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/ModalInputDialog.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/ModalInputDialog.java index 99566a6f..f7f88d36 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/ModalInputDialog.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/ModalInputDialog.java @@ -23,14 +23,20 @@ import org.eclipse.swt.widgets.Shell; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant; public class ModalInputDialog extends Dialog { private static final long serialVersionUID = -3448614119078234374L; - private final WidgetFactory widgetFactory; + private static final LocTextKey CANCEL_TEXT_KEY = + new LocTextKey("sebserver.overall.action.cancel"); + private static final LocTextKey OK_TEXT_KEY = + new LocTextKey("sebserver.overall.action.ok"); + private static final LocTextKey CLOSE_TEXT_KEY = + new LocTextKey("sebserver.overall.action.close"); - private T returnValue = null; + private final WidgetFactory widgetFactory; public ModalInputDialog( final Shell parent, @@ -41,16 +47,16 @@ public class ModalInputDialog extends Dialog { } public void open( - final String title, + final LocTextKey title, final PageContext pageContext, final Consumer callback, final ModalInputDialogComposer contentComposer) { - // Create the dialog window + // Create the selection dialog window final Shell shell = new Shell(getParent(), getStyle()); shell.setText(getText()); - shell.setData(RWT.CUSTOM_VARIANT, "message"); - shell.setText(title); + shell.setData(RWT.CUSTOM_VARIANT, CustomVariant.MESSAGE.key); + shell.setText(this.widgetFactory.getI18nSupport().getText(title)); shell.setLayout(new GridLayout(2, true)); shell.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); @@ -63,30 +69,62 @@ public class ModalInputDialog extends Dialog { final PageContext internalPageContext = pageContext.copyOf(main); final Supplier valueSuppier = contentComposer.compose(internalPageContext); - final Button ok = this.widgetFactory.buttonLocalized( - shell, - new LocTextKey("sebserver.overall.action.ok")); + final Button ok = this.widgetFactory.buttonLocalized(shell, OK_TEXT_KEY); GridData data = new GridData(GridData.HORIZONTAL_ALIGN_END); data.widthHint = 100; ok.setLayoutData(data); ok.addListener(SWT.Selection, event -> { - callback.accept(valueSuppier.get()); - this.returnValue = valueSuppier.get(); + if (valueSuppier != null) { + callback.accept(valueSuppier.get()); + } shell.close(); }); shell.setDefaultButton(ok); - final Button cancel = this.widgetFactory.buttonLocalized( - shell, - new LocTextKey("sebserver.overall.action.cancel")); - data = new GridData(GridData.CENTER); + final Button cancel = this.widgetFactory.buttonLocalized(shell, CANCEL_TEXT_KEY); + data = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); data.widthHint = 100; cancel.setLayoutData(data); cancel.addListener(SWT.Selection, event -> { shell.close(); }); + finishUp(shell); + } + + public void open( + final LocTextKey title, + final PageContext pageContext, + final Consumer contentComposer) { + + // Create the info dialog window + final Shell shell = new Shell(getParent(), getStyle()); + shell.setText(getText()); + shell.setData(RWT.CUSTOM_VARIANT, CustomVariant.MESSAGE.key); + shell.setText(this.widgetFactory.getI18nSupport().getText(title)); + shell.setLayout(new GridLayout()); + shell.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + final Composite main = new Composite(shell, SWT.NONE); + main.setLayout(new GridLayout()); + final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, true); + main.setLayoutData(gridData); + final PageContext internalPageContext = pageContext.copyOf(main); + contentComposer.accept(internalPageContext); + + final Button close = this.widgetFactory.buttonLocalized(shell, CLOSE_TEXT_KEY); + final GridData data = new GridData(GridData.HORIZONTAL_ALIGN_CENTER); + data.widthHint = 100; + close.setLayoutData(data); + close.addListener(SWT.Selection, event -> { + shell.close(); + }); + + finishUp(shell); + } + + private void finishUp(final Shell shell) { shell.pack(); final Rectangle bounds = shell.getBounds(); final Rectangle bounds2 = super.getParent().getDisplay().getBounds(); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/action/Action.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/PageAction.java similarity index 68% rename from src/main/java/ch/ethz/seb/sebserver/gui/service/page/action/Action.java rename to src/main/java/ch/ethz/seb/sebserver/gui/service/page/PageAction.java index 64db755e..c18f260a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/action/Action.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/PageAction.java @@ -6,7 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -package ch.ethz.seb.sebserver.gui.service.page.action; +package ch.ethz.seb.sebserver.gui.service.page; import java.util.Set; import java.util.function.BooleanSupplier; @@ -20,16 +20,14 @@ import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; -import ch.ethz.seb.sebserver.gui.service.page.PageContext; import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys; -import ch.ethz.seb.sebserver.gui.service.page.PageMessageException; import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent; import ch.ethz.seb.sebserver.gui.service.page.event.ActionPublishEvent; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCallError; -public final class Action implements Runnable { +public final class PageAction implements Runnable { - private static final Logger log = LoggerFactory.getLogger(Action.class); + private static final Logger log = LoggerFactory.getLogger(PageAction.class); public final ActionDefinition definition; Supplier confirm; @@ -40,9 +38,11 @@ public final class Action implements Runnable { private final PageContext originalPageContext; private PageContext pageContext; - private Function exec = Function.identity(); + private Function exec = Function.identity(); - public Action( + private boolean fireActionEvent = true; + + public PageAction( final ActionDefinition definition, final PageContext pageContext) { @@ -71,44 +71,46 @@ public final class Action implements Runnable { private void exec() { try { - final Action executedAction = this.exec.apply(this); - this.pageContext.firePageEvent(new ActionEvent(executedAction, false)); + final PageAction executedAction = this.exec.apply(this); + if (this.fireActionEvent) { + this.pageContext.firePageEvent(new ActionEvent(executedAction, false)); + } } catch (final PageMessageException pme) { - Action.this.pageContext.publishPageMessage(pme); + PageAction.this.pageContext.publishPageMessage(pme); } catch (final RestCallError restCallError) { if (restCallError.isFieldValidationError()) { - Action.this.pageContext.publishPageMessage( + PageAction.this.pageContext.publishPageMessage( new LocTextKey("sebserver.form.validation.error.title"), new LocTextKey("sebserver.form.validation.error.message")); } else { - log.error("Failed to execute action: {}", Action.this, restCallError); - Action.this.pageContext.notifyError("action.error.unexpected.message", restCallError); + log.error("Failed to execute action: {}", PageAction.this, restCallError); + PageAction.this.pageContext.notifyError("action.error.unexpected.message", restCallError); } } catch (final Throwable t) { - log.error("Failed to execute action: {}", Action.this, t); - Action.this.pageContext.notifyError("action.error.unexpected.message", t); + log.error("Failed to execute action: {}", PageAction.this, t); + PageAction.this.pageContext.notifyError("action.error.unexpected.message", t); } } - public Action createNew() { + public PageAction createNew() { return this.pageContext.createAction(this.definition); } - public Action withExec(final Function exec) { + public PageAction withExec(final Function exec) { this.exec = exec; return this; } - public Action withSelectionSupplier(final Supplier> selectionSupplier) { + public PageAction withSelectionSupplier(final Supplier> selectionSupplier) { this.selectionSupplier = selectionSupplier; return this; } - public Action withSelect( + public PageAction withSelect( final Supplier> selectionSupplier, - final Function exec, + final Function exec, final LocTextKey noSelectionMessage) { this.selectionSupplier = selectionSupplier; @@ -117,27 +119,32 @@ public final class Action implements Runnable { return this; } - public Action withConfirm(final String confirmationMessageKey) { + public PageAction withConfirm(final String confirmationMessageKey) { this.confirm = () -> new LocTextKey(confirmationMessageKey); return this; } - public Action withConfirm(final Supplier confirm) { + public PageAction withConfirm(final Supplier confirm) { this.confirm = confirm; return this; } - public Action withSuccess(final String successMessageKey) { + public PageAction withSuccess(final String successMessageKey) { this.successMessage = new LocTextKey(successMessageKey); return this; } - public Action resetEntityKey() { + public PageAction resetEntityKey() { this.pageContext = this.pageContext.withEntityKey(null); return this; } - public Action resetParentEntityKey() { + public PageAction noEventPropagation() { + this.fireActionEvent = false; + return this; + } + + public PageAction resetParentEntityKey() { this.pageContext = this.pageContext.withParentEntityKey(null); return this; } @@ -150,12 +157,12 @@ public final class Action implements Runnable { return this.pageContext; } - public Action withEntityKey(final EntityKey entityKey) { + public PageAction withEntityKey(final EntityKey entityKey) { this.pageContext = this.pageContext.withEntityKey(entityKey); return this; } - public Action withEntityKey(final Long modelId, final EntityType entityType) { + public PageAction withEntityKey(final Long modelId, final EntityType entityType) { if (modelId != null) { return withEntityKey(String.valueOf(modelId), entityType); } @@ -163,7 +170,7 @@ public final class Action implements Runnable { return this; } - public Action withEntityKey(final String modelId, final EntityType entityType) { + public PageAction withEntityKey(final String modelId, final EntityType entityType) { if (modelId == null || entityType == null) { return this; } @@ -172,12 +179,12 @@ public final class Action implements Runnable { return this; } - public Action withParentEntityKey(final EntityKey entityKey) { + public PageAction withParentEntityKey(final EntityKey entityKey) { this.pageContext = this.pageContext.withParentEntityKey(entityKey); return this; } - public Action withAttribute(final String name, final String value) { + public PageAction withAttribute(final String name, final String value) { this.pageContext = this.pageContext.withAttribute(name, value); return this; } @@ -225,14 +232,14 @@ public final class Action implements Runnable { return null; } - public static Action applySingleSelection(final Action action) { + public static PageAction applySingleSelection(final PageAction action) { return action.withEntityKey(action.getSingleSelection()); } - public static Action onEmptyEntityKeyGoToActivityHome(final Action action) { + public static PageAction onEmptyEntityKeyGoToActivityHome(final PageAction action) { if (action.getEntityKey() == null) { final PageContext pageContext = action.pageContext(); - final Action activityHomeAction = pageContext.createAction(action.definition.activityAlias); + final PageAction activityHomeAction = pageContext.createAction(action.definition.activityAlias); action.pageContext.firePageEvent(new ActionEvent(activityHomeAction, false)); return activityHomeAction; } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/PageContext.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/PageContext.java index 0b8440b2..bac0cabe 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/PageContext.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/PageContext.java @@ -16,7 +16,6 @@ import org.slf4j.LoggerFactory; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; -import ch.ethz.seb.sebserver.gui.service.page.action.Action; import ch.ethz.seb.sebserver.gui.service.page.event.PageEvent; /** Holds a page-context and defines some convenient functionality for page handling */ @@ -157,7 +156,7 @@ public interface PageContext { * @param event the concrete PageEvent instance */ void firePageEvent(T event); - Action createAction(ActionDefinition actionDefinition); + PageAction 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. diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/event/ActionEvent.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/event/ActionEvent.java index 06f78316..03b8c709 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/event/ActionEvent.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/event/ActionEvent.java @@ -8,16 +8,16 @@ package ch.ethz.seb.sebserver.gui.service.page.event; -import ch.ethz.seb.sebserver.gui.service.page.action.Action; +import ch.ethz.seb.sebserver.gui.service.page.PageAction; /** 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 Action action; + public final PageAction action; public final boolean activity; - public ActionEvent(final Action action, final boolean activity) { + public ActionEvent(final PageAction action, final boolean activity) { super(); this.action = action; this.activity = activity; diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/event/ActionPublishEvent.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/event/ActionPublishEvent.java index 57fb8ea8..fe130a14 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/event/ActionPublishEvent.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/event/ActionPublishEvent.java @@ -8,15 +8,15 @@ package ch.ethz.seb.sebserver.gui.service.page.event; -import ch.ethz.seb.sebserver.gui.service.page.action.Action; +import ch.ethz.seb.sebserver.gui.service.page.PageAction; /** 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 Action action; + public final PageAction action; - public ActionPublishEvent(final Action action) { + public ActionPublishEvent(final PageAction action) { this.action = action; } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ComposerServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ComposerServiceImpl.java index 412d50b9..dae9808b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ComposerServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ComposerServiceImpl.java @@ -91,7 +91,9 @@ public class ComposerServiceImpl implements ComposerService { final Class composerType, final PageContext pageContext) { - compose(composerType.getName(), pageContext); + if (composerType != null && pageContext != null) { + compose(composerType.getName(), pageContext); + } } @Override diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/MainPageState.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/MainPageState.java index aa35a59c..e505bc6a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/MainPageState.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/MainPageState.java @@ -15,13 +15,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ch.ethz.seb.sebserver.gui.content.MainPage; -import ch.ethz.seb.sebserver.gui.service.page.action.Action; +import ch.ethz.seb.sebserver.gui.service.page.PageAction; public final class MainPageState { private static final Logger log = LoggerFactory.getLogger(MainPageState.class); - public Action action = null; + public PageAction action = null; private MainPageState() { } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageContextImpl.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageContextImpl.java index 1b8d59d9..3ed27dad 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageContextImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/PageContextImpl.java @@ -32,11 +32,11 @@ import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; 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.PageAction; 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.PageMessageException; -import ch.ethz.seb.sebserver.gui.service.page.action.Action; 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.widget.Message; @@ -237,12 +237,18 @@ public class PageContextImpl implements PageContext { listeners.stream() .sorted(LIST_COMPARATOR) - .forEach(listener -> listener.notify(event)); + .forEach(listener -> { + try { + listener.notify(event); + } catch (final Exception e) { + log.error("Unexpected error while notify PageEventListener: ", e); + } + }); } @Override - public Action createAction(final ActionDefinition actionDefinition) { - return new Action(actionDefinition, this); + public PageAction createAction(final ActionDefinition actionDefinition) { + return new PageAction(actionDefinition, this); } @Override diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/RestService.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/RestService.java index 9ccaa26f..57217193 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/RestService.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/RestService.java @@ -12,7 +12,7 @@ import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; import ch.ethz.seb.sebserver.gbl.api.EntityType; -import ch.ethz.seb.sebserver.gui.service.page.action.Action; +import ch.ethz.seb.sebserver.gui.service.page.PageAction; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall.CallType; /** Interface to SEB Server webservice API thought RestCall's @@ -77,6 +77,6 @@ public interface RestService { * * @param action the Action that defines an entity activation * @return the successfully executed Action */ - Action activation(Action action); + PageAction activation(PageAction action); } \ No newline at end of file diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/RestServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/RestServiceImpl.java index 4bb6d678..9b74a6e7 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/RestServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/RestServiceImpl.java @@ -21,8 +21,8 @@ import ch.ethz.seb.sebserver.gbl.api.API; import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.JSONMapper; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.service.page.PageAction; import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys; -import ch.ethz.seb.sebserver.gui.service.page.action.Action; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall.CallType; import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder; import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.WebserviceURIService; @@ -114,7 +114,7 @@ public class RestServiceImpl implements RestService { } @Override - public Action activation(final Action action) { + public PageAction activation(final PageAction action) { if (action.definition.restCallType == null) { throw new IllegalArgumentException("ActionDefinition needs to define a restCallType to use this action"); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/table/EntityTable.java b/src/main/java/ch/ethz/seb/sebserver/gui/table/EntityTable.java index 2409844d..77684c56 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/table/EntityTable.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/table/EntityTable.java @@ -16,6 +16,7 @@ import java.util.List; import java.util.Objects; import java.util.Set; import java.util.function.Consumer; +import java.util.function.Function; import java.util.stream.Collectors; import org.eclipse.swt.SWT; @@ -38,6 +39,7 @@ import ch.ethz.seb.sebserver.gbl.model.Page; import ch.ethz.seb.sebserver.gbl.util.Utils; 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.PageAction; 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; @@ -77,7 +79,8 @@ public class EntityTable { final List> columns, final List actions, final int pageSize, - final LocTextKey emptyMessage) { + final LocTextKey emptyMessage, + final Function, PageAction> defaultActionFunction) { this.composite = new Composite(parent, type); this.widgetFactory = widgetFactory; @@ -124,6 +127,20 @@ public class EntityTable { this.table.setHeaderVisible(true); this.table.setLinesVisible(true); + if (defaultActionFunction != null) { + final PageAction defaultAction = defaultActionFunction.apply(this); + if (defaultAction != null) { + this.table.addListener(SWT.MouseDoubleClick, event -> { + final EntityKey selection = getSingleSelection(); + if (selection != null) { + defaultAction + .withEntityKey(selection) + .run(); + } + }); + } + } + this.navigator = new TableNavigator(this); createTableColumns(); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/table/TableBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/table/TableBuilder.java index 4becf9be..062322c5 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/table/TableBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/table/TableBuilder.java @@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.gui.table; import java.util.ArrayList; import java.util.List; import java.util.function.BooleanSupplier; +import java.util.function.Function; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Composite; @@ -18,6 +19,7 @@ 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.page.PageAction; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; @@ -43,7 +45,7 @@ public class TableBuilder { final List> columns = new ArrayList<>(); final List actions = new ArrayList<>(); LocTextKey emptyMessage; - + private Function, PageAction> defaultActionFunction; private int pageSize = -1; private int type = SWT.NONE; @@ -90,6 +92,16 @@ public class TableBuilder { return this; } + public TableBuilder withDefaultAction(final PageAction action) { + this.defaultActionFunction = table -> action; + return this; + } + + public TableBuilder withDefaultAction(final Function, PageAction> defaultActionFunction) { + this.defaultActionFunction = defaultActionFunction; + return this; + } + public EntityTable compose(final Composite parent) { return new EntityTable<>( this.type, @@ -99,7 +111,8 @@ public class TableBuilder { this.columns, this.actions, this.pageSize, - this.emptyMessage); + this.emptyMessage, + this.defaultActionFunction); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/ColorSelection.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/ColorSelection.java index d20be4f8..0b36b84f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/ColorSelection.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/ColorSelection.java @@ -127,9 +127,9 @@ public class ColorSelection extends Composite implements Selection { return null; } - return Integer.toHexString(color.red) - + Integer.toHexString(color.green) - + Integer.toHexString(color.blue); + return toColorFractionString(color.red) + + toColorFractionString(color.green) + + toColorFractionString(color.blue); } static RGB parseRGB(final String colorString) { @@ -144,4 +144,9 @@ public class ColorSelection extends Composite implements Selection { return new RGB(r, g, b); } + static String toColorFractionString(final int fraction) { + final String hexString = Integer.toHexString(fraction); + return (hexString.length() < 2) ? "0" + hexString : hexString; + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java index d8d1b12f..ef4a2fb5 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java @@ -116,7 +116,9 @@ public class WidgetFactory { SELECTION_READONLY("selectionReadonly"), FOOTER("footer"), - TITLE_LABEL("head") + TITLE_LABEL("head"), + + MESSAGE("message") ; @@ -253,6 +255,10 @@ public class WidgetFactory { return textInput(content, true); } + public Text textAreaInput(final Composite content) { + return new Text(content, SWT.LEFT | SWT.BORDER | SWT.MULTI); + } + public Text textInput(final Composite content, final boolean password) { return new Text(content, (password) ? SWT.LEFT | SWT.BORDER | SWT.PASSWORD @@ -326,6 +332,8 @@ public class WidgetFactory { public Label labelSeparator(final Composite parent) { final Label label = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL); + final GridData data = new GridData(SWT.FILL, SWT.TOP, true, true); + label.setLayoutData(data); return label; } diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index 853ede07..7b5e428d 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -14,6 +14,7 @@ sebserver.overall.action.filter=Apply Filter sebserver.overall.action.filter.clear=Clear Filter Criteria sebserver.overall.action.ok=OK sebserver.overall.action.cancel=Cancel +sebserver.overall.action.close=Close sebserver.overall.action.category.varia=Varia @@ -209,6 +210,15 @@ sebserver.quizdiscovery.info.pleaseSelect=Please Select a Quiz first sebserver.quizdiscovery.action.list=Quiz Discovery sebserver.quizdiscovery.action.import=Import as Exam +sebserver.quizdiscovery.action.details=Show Details + +sebserver.quizdiscovery.quiz.details.title=Quiz Details +sebserver.quizdiscovery.quiz.details.lms=LMS +sebserver.quizdiscovery.quiz.details.name=Name +sebserver.quizdiscovery.quiz.details.description=Description +sebserver.quizdiscovery.quiz.details.starttime=Start Time +sebserver.quizdiscovery.quiz.details.endtime=End Time +sebserver.quizdiscovery.quiz.details.url=Start URL ################################ # Exam