code cleanup

This commit is contained in:
anhefti 2020-03-05 16:05:56 +01:00
parent df884bd8d2
commit 884f9c78e0
40 changed files with 4320 additions and 4290 deletions

View file

@ -101,15 +101,15 @@ public class ExamForm implements TemplateComposer {
new LocTextKey("sebserver.exam.form.status");
private static final LocTextKey FORM_TYPE_TEXT_KEY =
new LocTextKey("sebserver.exam.form.type");
private static final LocTextKey FORM_ENDTIME_TEXT_KEY =
private static final LocTextKey FORM_END_TIME_TEXT_KEY =
new LocTextKey("sebserver.exam.form.endtime");
private static final LocTextKey FORM_STARTTIME_TEXT_KEY =
private static final LocTextKey FORM_START_TIME_TEXT_KEY =
new LocTextKey("sebserver.exam.form.starttime");
private static final LocTextKey FORM_DESCRIPTION_TEXT_KEY =
new LocTextKey("sebserver.exam.form.description");
private static final LocTextKey FORM_NAME_TEXT_KEY =
new LocTextKey("sebserver.exam.form.name");
private static final LocTextKey FORM_QUIZID_TEXT_KEY =
private static final LocTextKey FORM_QUIZ_ID_TEXT_KEY =
new LocTextKey("sebserver.exam.form.quizid");
private static final LocTextKey FORM_QUIZ_URL_TEXT_KEY =
new LocTextKey("sebserver.exam.form.quizurl");
@ -285,14 +285,14 @@ public class ExamForm implements TemplateComposer {
.addField(FormBuilder.text(
QuizData.QUIZ_ATTR_START_TIME,
FORM_STARTTIME_TEXT_KEY,
FORM_START_TIME_TEXT_KEY,
i18nSupport.formatDisplayDateWithTimeZone(exam.startTime))
.readonly(true)
.withInputSpan(3)
.withEmptyCellSeparation(false))
.addField(FormBuilder.text(
QuizData.QUIZ_ATTR_END_TIME,
FORM_ENDTIME_TEXT_KEY,
FORM_END_TIME_TEXT_KEY,
i18nSupport.formatDisplayDateWithTimeZone(exam.endTime))
.readonly(true)
.withInputSpan(3)
@ -300,7 +300,7 @@ public class ExamForm implements TemplateComposer {
.addField(FormBuilder.text(
Domain.EXAM.ATTR_EXTERNAL_ID,
FORM_QUIZID_TEXT_KEY,
FORM_QUIZ_ID_TEXT_KEY,
exam.externalId)
.readonly(true)
.withEmptyCellSeparation(false))

View file

@ -62,11 +62,11 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
@GuiProfile
public class ExamList implements TemplateComposer {
static final String EXAM_LIST_COLUMN_STARTTIME =
static final String EXAM_LIST_COLUMN_START_TIME =
"sebserver.exam.list.column.starttime";
static final LocTextKey PAGE_TITLE_KEY =
new LocTextKey("sebserver.exam.list.title");
static final LocTextKey NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUION =
static final LocTextKey NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUTION =
new LocTextKey("sebserver.exam.list.action.no.modify.privilege");
final static LocTextKey EMPTY_SELECTION_TEXT_KEY =
new LocTextKey("sebserver.exam.info.pleaseSelect");
@ -175,7 +175,7 @@ public class ExamList implements TemplateComposer {
.withColumn(new ColumnDefinition<>(
QuizData.QUIZ_ATTR_START_TIME,
new LocTextKey(
EXAM_LIST_COLUMN_STARTTIME,
EXAM_LIST_COLUMN_START_TIME,
i18nSupport.getUsersTimeZoneTitleSuffix()),
Exam::getStartTime)
.withFilter(new TableFilterAttribute(
@ -213,7 +213,7 @@ public class ExamList implements TemplateComposer {
.newAction(ActionDefinition.EXAM_MODIFY_FROM_LIST)
.withSelect(
table.getGrantedSelection(currentUser, NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUION),
table.getGrantedSelection(currentUser, NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUTION),
action -> modifyExam(action, table),
EMPTY_SELECTION_TEXT_KEY)
.publishIf(() -> userGrant.im() && table.hasAnyContent(), false);

View file

@ -46,7 +46,7 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@GuiProfile
public class LmsSetupList implements TemplateComposer {
private static final LocTextKey NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUION =
private static final LocTextKey NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUTION =
new LocTextKey("sebserver.lmssetup.list.action.no.modify.privilege");
private static final LocTextKey EMPTY_SELECTION_TEXT_KEY =
new LocTextKey("sebserver.lmssetup.info.pleaseSelect");
@ -173,7 +173,7 @@ public class LmsSetupList implements TemplateComposer {
.newAction(ActionDefinition.LMS_SETUP_MODIFY_FROM_LIST)
.withSelect(
table.getGrantedSelection(currentUser, NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUION),
table.getGrantedSelection(currentUser, NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUTION),
PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY)
.publishIf(() -> userGrant.im() && table.hasAnyContent(), false)

View file

@ -1,201 +1,196 @@
/*
* 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.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.swt.SWT;
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.Label;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Text;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
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.service.i18n.I18nSupport;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageService;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.page.impl.DefaultRegisterPage;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.SEBServerAuthorizationContext;
import ch.ethz.seb.sebserver.gui.widget.Message;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@Lazy
@Component
@GuiProfile
public class LoginPage implements TemplateComposer {
private static final Logger log = LoggerFactory.getLogger(LoginPage.class);
private final PageService pageService;
private final AuthorizationContextHolder authorizationContextHolder;
private final WidgetFactory widgetFactory;
private final I18nSupport i18nSupport;
private final DefaultRegisterPage defaultRegisterPage;
private final boolean registreringEnabled;
public LoginPage(
final PageService pageService,
final DefaultRegisterPage defaultRegisterPage,
@Value("${sebserver.gui.self-registering:false}") final Boolean registreringEnabled) {
this.pageService = pageService;
this.authorizationContextHolder = pageService.getAuthorizationContextHolder();
this.widgetFactory = pageService.getWidgetFactory();
this.i18nSupport = pageService.getI18nSupport();
this.defaultRegisterPage = defaultRegisterPage;
this.registreringEnabled = BooleanUtils.toBoolean(registreringEnabled);
}
@Override
public void compose(final PageContext pageContext) {
final Composite parent = pageContext.getParent();
WidgetFactory.setTestId(parent, "login-page");
WidgetFactory.setARIARole(parent, "composite");
final Composite loginGroup = new Composite(parent, SWT.NONE);
final GridLayout rowLayout = new GridLayout();
rowLayout.marginWidth = 20;
rowLayout.marginRight = 100;
loginGroup.setLayout(rowLayout);
loginGroup.setData(RWT.CUSTOM_VARIANT, WidgetFactory.CustomVariant.LOGIN.key);
final Label name = this.widgetFactory.labelLocalized(loginGroup, "sebserver.login.username");
name.setLayoutData(new GridData(300, -1));
name.setAlignment(SWT.BOTTOM);
final Text loginName = this.widgetFactory.textInput(loginGroup);
loginName.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false));
GridData gridData = new GridData(SWT.FILL, SWT.TOP, false, false);
gridData.verticalIndent = 10;
final Label pwd = this.widgetFactory.labelLocalized(loginGroup, "sebserver.login.pwd");
pwd.setLayoutData(gridData);
final Text loginPassword = this.widgetFactory.passwordInput(loginGroup);
loginPassword.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false));
final SEBServerAuthorizationContext authorizationContext = this.authorizationContextHolder
.getAuthorizationContext(RWT.getUISession().getHttpSession());
final Composite buttons = new Composite(loginGroup, SWT.NONE);
buttons.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
buttons.setLayout(new GridLayout(2, false));
buttons.setData(RWT.CUSTOM_VARIANT, WidgetFactory.CustomVariant.LOGIN_BACK.key);
final Button loginButton = this.widgetFactory.buttonLocalized(buttons, "sebserver.login.login");
gridData = new GridData(SWT.LEFT, SWT.TOP, false, false);
gridData.verticalIndent = 10;
loginButton.setLayoutData(gridData);
loginButton.addListener(SWT.Selection, event -> {
login(
pageContext,
loginName.getText(),
loginPassword.getText(),
authorizationContext);
});
loginName.addListener(SWT.KeyDown, event -> {
if (event.character == '\n' || event.character == '\r') {
if (StringUtils.isNotBlank(loginPassword.getText())) {
login(
pageContext,
loginName.getText(),
loginPassword.getText(),
authorizationContext);
} else {
loginPassword.setFocus();
}
}
});
loginPassword.addListener(SWT.KeyDown, event -> {
if (event.character == '\n' || event.character == '\r') {
if (StringUtils.isNotBlank(loginName.getText())) {
login(
pageContext,
loginName.getText(),
loginPassword.getText(),
authorizationContext);
} else {
loginName.setFocus();
}
}
});
if (this.registreringEnabled) {
final Button registerButton = this.widgetFactory.buttonLocalized(buttons, "sebserver.login.register");
gridData = new GridData(SWT.LEFT, SWT.TOP, false, false);
gridData.verticalIndent = 10;
registerButton.setLayoutData(gridData);
registerButton.addListener(SWT.Selection, event -> {
pageContext.forwardToPage(this.defaultRegisterPage);
});
}
}
private void login(
final PageContext pageContext,
final String loginName,
final CharSequence loginPassword,
final SEBServerAuthorizationContext authorizationContext) {
final String username = loginName;
try {
final boolean loggedIn = authorizationContext.login(
username,
loginPassword);
if (loggedIn) {
// Set users locale on page after successful login
try {
RWT.getUISession()
.getHttpSession()
.setAttribute(I18nSupport.ATTR_CURRENT_SESSION_LOCALE, authorizationContext
.getLoggedInUser()
.getOrThrow().language);
} catch (final IllegalStateException e) {
log.error("Set current locale for session failed: ", e);
}
RWT.setLocale(this.i18nSupport.getUsersFormatLocale());
pageContext.forwardToMainPage();
} else {
loginError(pageContext, "sebserver.login.failed.message");
}
} catch (final Exception e) {
log.error("Unexpected error while trying to login with user: {}", username, e);
loginError(pageContext, "Unexpected Error. Please call an Administrator");
}
}
private void loginError(
final PageContext pageContext,
final String message) {
this.pageService.logout(pageContext);
final MessageBox error = new Message(
pageContext.getShell(),
this.i18nSupport.getText("sebserver.login.failed.title"),
this.i18nSupport.getText(message, message),
SWT.ERROR,
this.i18nSupport);
error.open(null);
}
}
/*
* 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.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.swt.SWT;
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.Label;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Text;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
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.service.i18n.I18nSupport;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageService;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.page.impl.DefaultRegisterPage;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.SEBServerAuthorizationContext;
import ch.ethz.seb.sebserver.gui.widget.Message;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@Lazy
@Component
@GuiProfile
public class LoginPage implements TemplateComposer {
private static final Logger log = LoggerFactory.getLogger(LoginPage.class);
private final PageService pageService;
private final AuthorizationContextHolder authorizationContextHolder;
private final WidgetFactory widgetFactory;
private final I18nSupport i18nSupport;
private final DefaultRegisterPage defaultRegisterPage;
private final boolean registeringEnabled;
public LoginPage(
final PageService pageService,
final DefaultRegisterPage defaultRegisterPage,
@Value("${sebserver.gui.self-registering:false}") final Boolean registeringEnabled) {
this.pageService = pageService;
this.authorizationContextHolder = pageService.getAuthorizationContextHolder();
this.widgetFactory = pageService.getWidgetFactory();
this.i18nSupport = pageService.getI18nSupport();
this.defaultRegisterPage = defaultRegisterPage;
this.registeringEnabled = BooleanUtils.toBoolean(registeringEnabled);
}
@Override
public void compose(final PageContext pageContext) {
final Composite parent = pageContext.getParent();
WidgetFactory.setTestId(parent, "login-page");
WidgetFactory.setARIARole(parent, "composite");
final Composite loginGroup = new Composite(parent, SWT.NONE);
final GridLayout rowLayout = new GridLayout();
rowLayout.marginWidth = 20;
rowLayout.marginRight = 100;
loginGroup.setLayout(rowLayout);
loginGroup.setData(RWT.CUSTOM_VARIANT, WidgetFactory.CustomVariant.LOGIN.key);
final Label name = this.widgetFactory.labelLocalized(loginGroup, "sebserver.login.username");
name.setLayoutData(new GridData(300, -1));
name.setAlignment(SWT.BOTTOM);
final Text loginName = this.widgetFactory.textInput(loginGroup);
loginName.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false));
GridData gridData = new GridData(SWT.FILL, SWT.TOP, false, false);
gridData.verticalIndent = 10;
final Label pwd = this.widgetFactory.labelLocalized(loginGroup, "sebserver.login.pwd");
pwd.setLayoutData(gridData);
final Text loginPassword = this.widgetFactory.passwordInput(loginGroup);
loginPassword.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false));
final SEBServerAuthorizationContext authorizationContext = this.authorizationContextHolder
.getAuthorizationContext(RWT.getUISession().getHttpSession());
final Composite buttons = new Composite(loginGroup, SWT.NONE);
buttons.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
buttons.setLayout(new GridLayout(2, false));
buttons.setData(RWT.CUSTOM_VARIANT, WidgetFactory.CustomVariant.LOGIN_BACK.key);
final Button loginButton = this.widgetFactory.buttonLocalized(buttons, "sebserver.login.login");
gridData = new GridData(SWT.LEFT, SWT.TOP, false, false);
gridData.verticalIndent = 10;
loginButton.setLayoutData(gridData);
loginButton.addListener(SWT.Selection, event -> login(
pageContext,
loginName.getText(),
loginPassword.getText(),
authorizationContext));
loginName.addListener(SWT.KeyDown, event -> {
if (event.character == '\n' || event.character == '\r') {
if (StringUtils.isNotBlank(loginPassword.getText())) {
login(
pageContext,
loginName.getText(),
loginPassword.getText(),
authorizationContext);
} else {
loginPassword.setFocus();
}
}
});
loginPassword.addListener(SWT.KeyDown, event -> {
if (event.character == '\n' || event.character == '\r') {
if (StringUtils.isNotBlank(loginName.getText())) {
login(
pageContext,
loginName.getText(),
loginPassword.getText(),
authorizationContext);
} else {
loginName.setFocus();
}
}
});
if (this.registeringEnabled) {
final Button registerButton = this.widgetFactory.buttonLocalized(buttons, "sebserver.login.register");
gridData = new GridData(SWT.LEFT, SWT.TOP, false, false);
gridData.verticalIndent = 10;
registerButton.setLayoutData(gridData);
registerButton.addListener(SWT.Selection, event -> pageContext.forwardToPage(this.defaultRegisterPage));
}
}
private void login(
final PageContext pageContext,
final String loginName,
final CharSequence loginPassword,
final SEBServerAuthorizationContext authorizationContext) {
try {
final boolean loggedIn = authorizationContext.login(
loginName,
loginPassword);
if (loggedIn) {
// Set users locale on page after successful login
try {
RWT.getUISession()
.getHttpSession()
.setAttribute(I18nSupport.ATTR_CURRENT_SESSION_LOCALE, authorizationContext
.getLoggedInUser()
.getOrThrow().language);
} catch (final IllegalStateException e) {
log.error("Set current locale for session failed: ", e);
}
RWT.setLocale(this.i18nSupport.getUsersFormatLocale());
pageContext.forwardToMainPage();
} else {
loginError(pageContext, "sebserver.login.failed.message");
}
} catch (final Exception e) {
log.error("Unexpected error while trying to login with user: {}", loginName, e);
loginError(pageContext, "Unexpected Error. Please call an Administrator");
}
}
private void loginError(
final PageContext pageContext,
final String message) {
this.pageService.logout(pageContext);
final MessageBox error = new Message(
pageContext.getShell(),
this.i18nSupport.getText("sebserver.login.failed.title"),
this.i18nSupport.getText(message, message),
SWT.ERROR,
this.i18nSupport);
error.open(null);
}
}

View file

@ -1,184 +1,184 @@
/*
* 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.Consumer;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.content.activity.ActivitiesPane;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageService;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionEventListener;
import ch.ethz.seb.sebserver.gui.service.page.event.PageEventListener;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon;
@Lazy
@Component
@GuiProfile
public class MainPage implements TemplateComposer {
static final Logger log = LoggerFactory.getLogger(MainPage.class);
private static final int ACTIVITY_PANE_WEIGHT = 18;
private static final int CONTENT_PANE_WEIGHT = 65;
private static final int ACTION_PANE_WEIGHT = 20;
private static final int[] DEFAULT_SASH_WEIGHTS = new int[] {
ACTIVITY_PANE_WEIGHT,
CONTENT_PANE_WEIGHT,
ACTION_PANE_WEIGHT
};
private static final int[] OPENED_SASH_WEIGHTS = new int[] { 0, 100, 0 };
private final WidgetFactory widgetFactory;
private final PolyglotPageService polyglotPageService;
public MainPage(
final WidgetFactory widgetFactory,
final PolyglotPageService polyglotPageService) {
this.widgetFactory = widgetFactory;
this.polyglotPageService = polyglotPageService;
}
@Override
public void compose(final PageContext pageContext) {
final Composite parent = pageContext.getParent();
parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
WidgetFactory.setTestId(parent, "main-page");
final SashForm mainSash = new SashForm(parent, SWT.HORIZONTAL);
final GridLayout gridLayout = new GridLayout();
mainSash.setLayout(gridLayout);
mainSash.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
final Composite nav = new Composite(mainSash, SWT.NONE);
nav.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
final GridLayout navLayout = new GridLayout();
navLayout.marginHeight = 20;
navLayout.marginWidth = 0;
nav.setLayout(navLayout);
final Composite content = PageService.createManagedVScrolledComposite(
mainSash,
scrolledComposite -> {
final Composite result = new Composite(scrolledComposite, SWT.NONE);
result.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
final GridLayout contentOuterlayout = new GridLayout();
contentOuterlayout.marginHeight = 0;
contentOuterlayout.marginWidth = 0;
result.setLayout(contentOuterlayout);
return result;
},
false);
final Label toggleView = this.widgetFactory.imageButton(
ImageIcon.MAXIMIZE,
content,
new LocTextKey("sebserver.mainpage.maximize.tooltip"),
event -> {
final Label ib = (Label) event.widget;
if ((Boolean) ib.getData("fullScreen")) {
mainSash.setWeights(DEFAULT_SASH_WEIGHTS);
ib.setData("fullScreen", false);
ib.setImage(WidgetFactory.ImageIcon.MAXIMIZE.getImage(ib.getDisplay()));
this.polyglotPageService.injectI18n(
ib,
null,
new LocTextKey("sebserver.mainpage.maximize.tooltip"));
} else {
mainSash.setWeights(OPENED_SASH_WEIGHTS);
ib.setData("fullScreen", true);
ib.setImage(WidgetFactory.ImageIcon.MINIMIZE.getImage(ib.getDisplay()));
this.polyglotPageService.injectI18n(
ib,
null,
new LocTextKey("sebserver.mainpage.minimize.tooltip"));
}
});
final GridData gridData = new GridData(SWT.RIGHT, SWT.TOP, true, false);
toggleView.setLayoutData(gridData);
toggleView.setData("fullScreen", false);
final Composite contentObjects = new Composite(content, SWT.NONE);
contentObjects.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
final GridLayout contentObjectslayout = new GridLayout();
contentObjectslayout.marginHeight = 0;
contentObjectslayout.marginWidth = 0;
contentObjects.setLayout(contentObjectslayout);
contentObjects.setData(
PageEventListener.LISTENER_ATTRIBUTE_KEY,
new ContentActionEventListener(event -> pageContext
.composerService()
.compose(
event.action.definition.targetState.contentPaneComposer(),
event.action.pageContext().copyOf(contentObjects)),
2));
final Composite actionPane = new Composite(mainSash, SWT.NONE);
actionPane.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
final GridLayout actionPaneGrid = new GridLayout();
actionPane.setLayout(actionPaneGrid);
actionPane.setData(RWT.CUSTOM_VARIANT, "actionPane");
actionPane.setData(
PageEventListener.LISTENER_ATTRIBUTE_KEY,
new ContentActionEventListener(event -> pageContext
.composerService()
.compose(
event.action.definition.targetState.actionPaneComposer(),
event.action.pageContext().copyOf(actionPane)),
1));
pageContext.composerService().compose(
ActivitiesPane.class,
pageContext.copyOf(nav));
mainSash.setWeights(DEFAULT_SASH_WEIGHTS);
}
private static final class ContentActionEventListener implements ActionEventListener {
private final int priority;
private final Consumer<ActionEvent> apply;
protected ContentActionEventListener(final Consumer<ActionEvent> apply, final int priority) {
this.apply = apply;
this.priority = priority;
}
@Override
public int priority() {
return this.priority;
}
@Override
public void notify(final ActionEvent event) {
this.apply.accept(event);
}
}
}
/*
* 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.Consumer;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.content.activity.ActivitiesPane;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageService;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionEventListener;
import ch.ethz.seb.sebserver.gui.service.page.event.PageEventListener;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon;
@Lazy
@Component
@GuiProfile
public class MainPage implements TemplateComposer {
static final Logger log = LoggerFactory.getLogger(MainPage.class);
private static final int ACTIVITY_PANE_WEIGHT = 18;
private static final int CONTENT_PANE_WEIGHT = 65;
private static final int ACTION_PANE_WEIGHT = 20;
private static final int[] DEFAULT_SASH_WEIGHTS = new int[] {
ACTIVITY_PANE_WEIGHT,
CONTENT_PANE_WEIGHT,
ACTION_PANE_WEIGHT
};
private static final int[] OPENED_SASH_WEIGHTS = new int[] { 0, 100, 0 };
private final WidgetFactory widgetFactory;
private final PolyglotPageService polyglotPageService;
public MainPage(
final WidgetFactory widgetFactory,
final PolyglotPageService polyglotPageService) {
this.widgetFactory = widgetFactory;
this.polyglotPageService = polyglotPageService;
}
@Override
public void compose(final PageContext pageContext) {
final Composite parent = pageContext.getParent();
parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
WidgetFactory.setTestId(parent, "main-page");
final SashForm mainSash = new SashForm(parent, SWT.HORIZONTAL);
final GridLayout gridLayout = new GridLayout();
mainSash.setLayout(gridLayout);
mainSash.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
final Composite nav = new Composite(mainSash, SWT.NONE);
nav.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
final GridLayout navLayout = new GridLayout();
navLayout.marginHeight = 20;
navLayout.marginWidth = 0;
nav.setLayout(navLayout);
final Composite content = PageService.createManagedVScrolledComposite(
mainSash,
scrolledComposite -> {
final Composite result = new Composite(scrolledComposite, SWT.NONE);
result.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
final GridLayout contentOuterLayout = new GridLayout();
contentOuterLayout.marginHeight = 0;
contentOuterLayout.marginWidth = 0;
result.setLayout(contentOuterLayout);
return result;
},
false);
final Label toggleView = this.widgetFactory.imageButton(
ImageIcon.MAXIMIZE,
content,
new LocTextKey("sebserver.mainpage.maximize.tooltip"),
event -> {
final Label ib = (Label) event.widget;
if ((Boolean) ib.getData("fullScreen")) {
mainSash.setWeights(DEFAULT_SASH_WEIGHTS);
ib.setData("fullScreen", false);
ib.setImage(WidgetFactory.ImageIcon.MAXIMIZE.getImage(ib.getDisplay()));
this.polyglotPageService.injectI18n(
ib,
null,
new LocTextKey("sebserver.mainpage.maximize.tooltip"));
} else {
mainSash.setWeights(OPENED_SASH_WEIGHTS);
ib.setData("fullScreen", true);
ib.setImage(WidgetFactory.ImageIcon.MINIMIZE.getImage(ib.getDisplay()));
this.polyglotPageService.injectI18n(
ib,
null,
new LocTextKey("sebserver.mainpage.minimize.tooltip"));
}
});
final GridData gridData = new GridData(SWT.RIGHT, SWT.TOP, true, false);
toggleView.setLayoutData(gridData);
toggleView.setData("fullScreen", false);
final Composite contentObjects = new Composite(content, SWT.NONE);
contentObjects.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
final GridLayout contentObjectsLayout = new GridLayout();
contentObjectsLayout.marginHeight = 0;
contentObjectsLayout.marginWidth = 0;
contentObjects.setLayout(contentObjectsLayout);
contentObjects.setData(
PageEventListener.LISTENER_ATTRIBUTE_KEY,
new ContentActionEventListener(event -> pageContext
.composerService()
.compose(
event.action.definition.targetState.contentPaneComposer(),
event.action.pageContext().copyOf(contentObjects)),
2));
final Composite actionPane = new Composite(mainSash, SWT.NONE);
actionPane.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
final GridLayout actionPaneGrid = new GridLayout();
actionPane.setLayout(actionPaneGrid);
actionPane.setData(RWT.CUSTOM_VARIANT, "actionPane");
actionPane.setData(
PageEventListener.LISTENER_ATTRIBUTE_KEY,
new ContentActionEventListener(event -> pageContext
.composerService()
.compose(
event.action.definition.targetState.actionPaneComposer(),
event.action.pageContext().copyOf(actionPane)),
1));
pageContext.composerService().compose(
ActivitiesPane.class,
pageContext.copyOf(nav));
mainSash.setWeights(DEFAULT_SASH_WEIGHTS);
}
private static final class ContentActionEventListener implements ActionEventListener {
private final int priority;
private final Consumer<ActionEvent> apply;
protected ContentActionEventListener(final Consumer<ActionEvent> apply, final int priority) {
this.apply = apply;
this.priority = priority;
}
@Override
public int priority() {
return this.priority;
}
@Override
public void notify(final ActionEvent event) {
this.apply.accept(event);
}
}
}

View file

@ -240,8 +240,8 @@ public class QuizDiscoveryList implements TemplateComposer {
}
private static Function<QuizData, String> quizDataLmsSetupNameFunction(final ResourceService resourceService) {
return quizzData -> resourceService.getLmsSetupNameFunction()
.apply(String.valueOf(quizzData.lmsSetupId));
return quizData -> resourceService.getLmsSetupNameFunction()
.apply(String.valueOf(quizData.lmsSetupId));
}
private PageAction importQuizData(final PageAction action, final EntityTable<QuizData> table) {

View file

@ -247,9 +247,7 @@ public class RegisterPage implements TemplateComposer {
gridData = new GridData(SWT.LEFT, SWT.TOP, false, false);
gridData.verticalIndent = 10;
cancelButton.setLayoutData(gridData);
cancelButton.addListener(SWT.Selection, event -> {
pageContext.forwardToLoginPage();
});
cancelButton.addListener(SWT.Selection, event -> pageContext.forwardToLoginPage());
}

View file

@ -48,7 +48,7 @@ import ch.ethz.seb.sebserver.gui.table.TableFilter.CriteriaType;
@GuiProfile
public class SebClientConfigList implements TemplateComposer {
private static final LocTextKey NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUION =
private static final LocTextKey NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUTION =
new LocTextKey("sebserver.clientconfig.list.action.no.modify.privilege");
private static final LocTextKey EMPTY_LIST_TEXT_KEY =
new LocTextKey("sebserver.clientconfig.list.empty");
@ -176,7 +176,7 @@ public class SebClientConfigList implements TemplateComposer {
.newAction(ActionDefinition.SEB_CLIENT_CONFIG_MODIFY_FROM_LIST)
.withSelect(
table.getGrantedSelection(this.currentUser, NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUION),
table.getGrantedSelection(this.currentUser, NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUTION),
PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY)
.publishIf(() -> clientConfigGrant.im() && table.hasAnyContent(), false)

View file

@ -81,7 +81,7 @@ final class SebExamConfigImportPopup {
};
}
private static final boolean doImport(
private static boolean doImport(
final PageService pageService,
final FormHandle<ConfigurationNode> formHandle,
final boolean newConfig) {
@ -93,7 +93,7 @@ final class SebExamConfigImportPopup {
final PageContext context = formHandle.getContext();
// Ad-hoc field validation
formHandle.process(name -> true, field -> field.resetError());
formHandle.process(name -> true, Form.FormFieldAccessor::resetError);
final String fieldValue = form.getFieldValue(Domain.CONFIGURATION_NODE.ATTR_NAME);
if (StringUtils.isBlank(fieldValue)) {
form.setFieldError(
@ -116,7 +116,7 @@ final class SebExamConfigImportPopup {
return false;
}
if (fieldControl != null && fieldControl instanceof FileUploadSelection) {
if (fieldControl instanceof FileUploadSelection) {
final FileUploadSelection fileUpload = (FileUploadSelection) fieldControl;
final InputStream inputStream = fileUpload.getInputStream();
if (inputStream != null) {
@ -162,7 +162,6 @@ final class SebExamConfigImportPopup {
new ActionEvent(action),
action.pageContext());
}
return true;
} else {
final Exception error = configuration.getError();
if (error instanceof RestCallError) {
@ -188,12 +187,12 @@ final class SebExamConfigImportPopup {
SebExamConfigPropForm.FORM_TITLE,
configuration.getError());
return true;
}
return true;
} else {
formHandle.getContext().publishPageMessage(
new LocTextKey("sebserver.error.unexpected"),
new LocTextKey("Please selecte a valid SEB Exam Configuration File"));
new LocTextKey("Please select a valid SEB Exam Configuration File"));
}
}
@ -271,7 +270,7 @@ final class SebExamConfigImportPopup {
void cancelUpload() {
if (this.form != null) {
final Control fieldControl = this.form.getFieldInput(API.IMPORT_FILE_ATTR_NAME);
if (fieldControl != null && fieldControl instanceof FileUploadSelection) {
if (fieldControl instanceof FileUploadSelection) {
((FileUploadSelection) fieldControl).close();
}
}

View file

@ -43,7 +43,7 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@GuiProfile
public class SebExamConfigList implements TemplateComposer {
private static final LocTextKey NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUION =
private static final LocTextKey NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUTION =
new LocTextKey("sebserver.examconfig.list.action.no.modify.privilege");
private static final LocTextKey EMPTY_CONFIG_LIST_TEXT_KEY =
new LocTextKey("sebserver.examconfig.list.empty");
@ -165,7 +165,7 @@ public class SebExamConfigList implements TemplateComposer {
.newAction(ActionDefinition.SEB_EXAM_CONFIG_MODIFY_PROP_FROM_LIST)
.withSelect(
configTable.getGrantedSelection(this.currentUser, NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUION),
configTable.getGrantedSelection(this.currentUser, NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUTION),
PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY)
.publishIf(() -> examConfigGrant.im() && configTable.hasAnyContent(), false)

View file

@ -265,7 +265,7 @@ public class SebExamConfigPropForm implements TemplateComposer {
.noEventPropagation()
.publishIf(() -> modifyGrant && isReadonly)
.newAction(ActionDefinition.SEB_EXAM_CONFIG_COPY_CONFIG_AS_TEMPALTE)
.newAction(ActionDefinition.SEA_EXAM_CONFIG_COPY_CONFIG_AS_TEMPLATE)
.withEntityKey(entityKey)
.withExec(SebExamConfigCreationPopup.configCreationFunction(
this.pageService,
@ -313,7 +313,7 @@ public class SebExamConfigPropForm implements TemplateComposer {
.withColumn(new ColumnDefinition<>(
QuizData.QUIZ_ATTR_START_TIME,
new LocTextKey(
ExamList.EXAM_LIST_COLUMN_STARTTIME,
ExamList.EXAM_LIST_COLUMN_START_TIME,
this.pageService.getI18nSupport().getUsersTimeZoneTitleSuffix()),
ExamConfigurationMap::getExamStartTime))

View file

@ -190,7 +190,7 @@ public class SebExamConfigSettingsForm implements TemplateComposer {
.ignoreMoveAwayFromEdit()
.publishIf(() -> examConfigGrant.iw() && !readonly)
.newAction(ActionDefinition.SEB_EXAM_CONFIG_COPY_CONFIG_AS_TEMPALTE)
.newAction(ActionDefinition.SEA_EXAM_CONFIG_COPY_CONFIG_AS_TEMPLATE)
.withEntityKey(entityKey)
.withExec(SebExamConfigCreationPopup.configCreationFunction(
this.pageService,

View file

@ -1,41 +1,41 @@
/*
* 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;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
public enum ActionCategory {
FORM(null, 0),
INSTITUTION_LIST(new LocTextKey("sebserver.institution.list.actions"), 1),
USER_ACCOUNT_LIST(new LocTextKey("sebserver.useraccount.list.actions"), 1),
LMS_SETUP_LIST(new LocTextKey("sebserver.lmssetup.list.actions"), 1),
QUIZ_LIST(new LocTextKey("sebserver.quizdiscovery.list.actions"), 1),
EXAM_LIST(new LocTextKey("sebserver.exam.list.actions"), 1),
EXAM_CONFIG_MAPPING_LIST(new LocTextKey("sebserver.exam.configuration.list.actions"), 1),
INDICATOR_LIST(new LocTextKey("sebserver.exam.indicator.list.actions"), 2),
SEB_CLIENT_CONFIG_LIST(new LocTextKey("sebserver.clientconfig.list.actions"), 1),
SEB_EXAM_CONFIG_LIST(new LocTextKey("sebserver.examconfig.list.actions"), 1),
SEB_CONFIG_TEMPLATE_LIST(new LocTextKey("sebserver.configtemplate.list.actions"), 1),
SEB_CONFIG_TEMPLATE_ATTRIBUTE_LIST(new LocTextKey("sebserver.configtemplate.attr.list.actions"), 1),
RUNNING_EXAM_LIST(new LocTextKey("sebserver.monitoring.exam.list.actions"), 1),
CLIENT_EVENT_LIST(new LocTextKey("sebserver.monitoring.exam.connection.list.actions"), 1),
LOGS_USER_ACTIVITY_LIST(new LocTextKey("sebserver.userlogs.list.actions"), 1),
LOGS_SEB_CLIENT_LIST(new LocTextKey("sebserver.userlogs.list.actions"), 1),
VARIA(new LocTextKey("sebserver.overall.action.category.varia"), 0),
FILTER(new LocTextKey("sebserver.overall.action.category.filter"), 50);
public final LocTextKey title;
public final int slotPosition;
private ActionCategory(final LocTextKey title, final int slotPosition) {
this.title = title;
this.slotPosition = slotPosition;
}
}
/*
* 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;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
public enum ActionCategory {
FORM(null, 0),
INSTITUTION_LIST(new LocTextKey("sebserver.institution.list.actions"), 1),
USER_ACCOUNT_LIST(new LocTextKey("sebserver.useraccount.list.actions"), 1),
LMS_SETUP_LIST(new LocTextKey("sebserver.lmssetup.list.actions"), 1),
QUIZ_LIST(new LocTextKey("sebserver.quizdiscovery.list.actions"), 1),
EXAM_LIST(new LocTextKey("sebserver.exam.list.actions"), 1),
EXAM_CONFIG_MAPPING_LIST(new LocTextKey("sebserver.exam.configuration.list.actions"), 1),
INDICATOR_LIST(new LocTextKey("sebserver.exam.indicator.list.actions"), 2),
SEB_CLIENT_CONFIG_LIST(new LocTextKey("sebserver.clientconfig.list.actions"), 1),
SEB_EXAM_CONFIG_LIST(new LocTextKey("sebserver.examconfig.list.actions"), 1),
SEB_CONFIG_TEMPLATE_LIST(new LocTextKey("sebserver.configtemplate.list.actions"), 1),
SEB_CONFIG_TEMPLATE_ATTRIBUTE_LIST(new LocTextKey("sebserver.configtemplate.attr.list.actions"), 1),
RUNNING_EXAM_LIST(new LocTextKey("sebserver.monitoring.exam.list.actions"), 1),
CLIENT_EVENT_LIST(new LocTextKey("sebserver.monitoring.exam.connection.list.actions"), 1),
LOGS_USER_ACTIVITY_LIST(new LocTextKey("sebserver.userlogs.list.actions"), 1),
LOGS_SEB_CLIENT_LIST(new LocTextKey("sebserver.userlogs.list.actions"), 1),
VARIA(new LocTextKey("sebserver.overall.action.category.varia"), 0),
FILTER(new LocTextKey("sebserver.overall.action.category.filter"), 50);
public final LocTextKey title;
public final int slotPosition;
ActionCategory(final LocTextKey title, final int slotPosition) {
this.title = title;
this.slotPosition = slotPosition;
}
}

View file

@ -467,7 +467,7 @@ public enum ActionDefinition {
new LocTextKey("sebserver.examconfig.action.copy"),
ImageIcon.COPY,
ActionCategory.FORM),
SEB_EXAM_CONFIG_COPY_CONFIG_AS_TEMPALTE(
SEA_EXAM_CONFIG_COPY_CONFIG_AS_TEMPLATE(
new LocTextKey("sebserver.examconfig.action.copy-as-template"),
ImageIcon.TEMPLATE,
ActionCategory.FORM),

View file

@ -8,10 +8,17 @@
package ch.ethz.seb.sebserver.gui.content.action;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageService;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionActivationEventListener;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionPublishEventListener;
import ch.ethz.seb.sebserver.gui.service.page.event.PageEventListener;
import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.rap.rwt.template.ImageCell;
@ -31,19 +38,9 @@ import org.eclipse.swt.widgets.TreeItem;
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.i18n.PolyglotPageService;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageService;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionActivationEvent;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionActivationEventListener;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionPublishEvent;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionPublishEventListener;
import ch.ethz.seb.sebserver.gui.service.page.event.PageEventListener;
import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
@Lazy
@Component

View file

@ -1,42 +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.activity;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.Activity;
public enum ActivityDefinition implements Activity {
SEB_ADMINISTRATION(new LocTextKey("sebserver.overall.activity.title.serveradmin")),
INSTITUTION(new LocTextKey("sebserver.institution.action.list")),
USER_ACCOUNT(new LocTextKey("sebserver.useraccount.action.list")),
USER_ACTIVITY_LOGS(new LocTextKey("sebserver.logs.activity.userlogs")),
LMS_SETUP(new LocTextKey("sebserver.lmssetup.action.list")),
QUIZ_DISCOVERY(new LocTextKey("sebserver.quizdiscovery.action.list")),
EXAM_ADMINISTRATION(new LocTextKey("sebserver.overall.activity.title.examadmin")),
EXAM(new LocTextKey("sebserver.exam.action.list")),
SEB_CONFIGURATION(new LocTextKey("sebserver.overall.activity.title.sebconfig")),
SEB_CLIENT_CONFIG(new LocTextKey("sebserver.clientconfig.action.list")),
SEB_EXAM_CONFIG(new LocTextKey("sebserver.examconfig.action.list")),
SEB_EXAM_CONFIG_TEMPLATE(new LocTextKey("sebserver.configtemplate.action.list")),
MONITORING(new LocTextKey("sebserver.overall.activity.title.monitoring")),
MONITORING_EXAMS(new LocTextKey("sebserver.monitoring.action.list")),
SEB_CLIENT_LOGS(new LocTextKey("sebserver.logs.activity.seblogs"));
public final LocTextKey displayName;
private ActivityDefinition(final LocTextKey displayName) {
this.displayName = displayName;
}
@Override
public LocTextKey displayName() {
return this.displayName;
}
}
/*
* 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.activity;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.Activity;
public enum ActivityDefinition implements Activity {
SEB_ADMINISTRATION(new LocTextKey("sebserver.overall.activity.title.serveradmin")),
INSTITUTION(new LocTextKey("sebserver.institution.action.list")),
USER_ACCOUNT(new LocTextKey("sebserver.useraccount.action.list")),
USER_ACTIVITY_LOGS(new LocTextKey("sebserver.logs.activity.userlogs")),
LMS_SETUP(new LocTextKey("sebserver.lmssetup.action.list")),
QUIZ_DISCOVERY(new LocTextKey("sebserver.quizdiscovery.action.list")),
EXAM_ADMINISTRATION(new LocTextKey("sebserver.overall.activity.title.examadmin")),
EXAM(new LocTextKey("sebserver.exam.action.list")),
SEB_CONFIGURATION(new LocTextKey("sebserver.overall.activity.title.sebconfig")),
SEB_CLIENT_CONFIG(new LocTextKey("sebserver.clientconfig.action.list")),
SEB_EXAM_CONFIG(new LocTextKey("sebserver.examconfig.action.list")),
SEB_EXAM_CONFIG_TEMPLATE(new LocTextKey("sebserver.configtemplate.action.list")),
MONITORING(new LocTextKey("sebserver.overall.activity.title.monitoring")),
MONITORING_EXAMS(new LocTextKey("sebserver.monitoring.action.list")),
SEB_CLIENT_LOGS(new LocTextKey("sebserver.logs.activity.seblogs"));
public final LocTextKey displayName;
ActivityDefinition(final LocTextKey displayName) {
this.displayName = displayName;
}
@Override
public LocTextKey displayName() {
return this.displayName;
}
}

View file

@ -94,7 +94,7 @@ public enum PageStateDefinitionImpl implements PageStateDefinition {
public final Class<? extends TemplateComposer> actionPaneComposer;
public final Activity activityAnchor;
private PageStateDefinitionImpl(
PageStateDefinitionImpl(
final Type type,
final Class<? extends TemplateComposer> contentPaneComposer,
final Activity activityAnchor) {
@ -102,7 +102,7 @@ public enum PageStateDefinitionImpl implements PageStateDefinition {
this(type, contentPaneComposer, ActionPane.class, activityAnchor);
}
private PageStateDefinitionImpl(
PageStateDefinitionImpl(
final Type type,
final Class<? extends TemplateComposer> contentPaneComposer,
final Class<? extends TemplateComposer> actionPaneComposer,

View file

@ -44,7 +44,6 @@ public class ThresholdListBuilder extends FieldBuilder<Collection<Threshold>> {
final Control titleLabel = createTitleLabel(builder.formParent, builder, this);
if (builder.readonly || this.readonly) {
// No read-only view needed for this so far?
return;
} else {
final Composite fieldGrid = createFieldGrid(builder.formParent, this.spanInput);

View file

@ -1,109 +1,109 @@
/*
* 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.examconfig;
import java.util.Collection;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.swt.widgets.Composite;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.TemplateAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.View;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.gui.service.examconfig.impl.AttributeMapping;
import ch.ethz.seb.sebserver.gui.service.examconfig.impl.ViewContext;
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.impl.PageAction;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
public interface ExamConfigurationService {
public static final String ATTRIBUTE_LABEL_LOC_TEXT_PREFIX = "sebserver.examconfig.props.label.";
public static final String GROUP_LABEL_LOC_TEXT_PREFIX = "sebserver.examconfig.props.group.";
public static final String TOOL_TIP_SUFFIX = ".tooltip";
public static final String TABLE_ROW_TITLE_SUFFIX = ".row.title";
WidgetFactory getWidgetFactory();
InputFieldBuilder getInputFieldBuilder(
ConfigurationAttribute attribute,
Orientation orientation);
Result<AttributeMapping> getAttributes(Long templateId);
Result<AttributeMapping> getAttributes(
final TemplateAttribute attribute,
final Orientation defaultOrientation);
List<View> getViews(AttributeMapping allAttributes);
ViewContext createViewContext(
PageContext pageContext,
Configuration configuration,
View view,
AttributeMapping attributeMapping,
int rows,
boolean readonly);
Composite createViewGrid(
Composite parent,
ViewContext viewContext);
void initInputFieldValues(
Long configurationId,
Collection<ViewContext> viewContexts);
PageAction resetToDefaults(PageAction action);
PageAction removeFromView(PageAction action);
PageAction attachToDefaultView(final PageAction action);
static String attributeNameKey(final ConfigurationAttribute attribute) {
if (attribute == null) {
return null;
}
return ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + attribute.name;
}
static LocTextKey attributeNameLocKey(final ConfigurationAttribute attribute) {
if (attribute == null) {
return null;
}
return new LocTextKey(attributeNameKey(attribute));
}
static LocTextKey getToolTipKey(
final ConfigurationAttribute attribute,
final I18nSupport i18nSupport) {
final String attributeNameKey = ExamConfigurationService.attributeNameKey(attribute) + TOOL_TIP_SUFFIX;
if (StringUtils.isBlank(i18nSupport.getText(attributeNameKey, ""))) {
return null;
} else {
return new LocTextKey(attributeNameKey);
}
}
static LocTextKey getTablePopupTitleKey(
final ConfigurationAttribute attribute,
final I18nSupport i18nSupport) {
return new LocTextKey(ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + attribute.name + TABLE_ROW_TITLE_SUFFIX);
}
}
/*
* 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.examconfig;
import java.util.Collection;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.swt.widgets.Composite;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.TemplateAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.View;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.gui.service.examconfig.impl.AttributeMapping;
import ch.ethz.seb.sebserver.gui.service.examconfig.impl.ViewContext;
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.impl.PageAction;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
public interface ExamConfigurationService {
String ATTRIBUTE_LABEL_LOC_TEXT_PREFIX = "sebserver.examconfig.props.label.";
String GROUP_LABEL_LOC_TEXT_PREFIX = "sebserver.examconfig.props.group.";
String TOOL_TIP_SUFFIX = ".tooltip";
String TABLE_ROW_TITLE_SUFFIX = ".row.title";
WidgetFactory getWidgetFactory();
InputFieldBuilder getInputFieldBuilder(
ConfigurationAttribute attribute,
Orientation orientation);
Result<AttributeMapping> getAttributes(Long templateId);
Result<AttributeMapping> getAttributes(
final TemplateAttribute attribute,
final Orientation defaultOrientation);
List<View> getViews(AttributeMapping allAttributes);
ViewContext createViewContext(
PageContext pageContext,
Configuration configuration,
View view,
AttributeMapping attributeMapping,
int rows,
boolean readonly);
Composite createViewGrid(
Composite parent,
ViewContext viewContext);
void initInputFieldValues(
Long configurationId,
Collection<ViewContext> viewContexts);
PageAction resetToDefaults(PageAction action);
PageAction removeFromView(PageAction action);
PageAction attachToDefaultView(final PageAction action);
static String attributeNameKey(final ConfigurationAttribute attribute) {
if (attribute == null) {
return null;
}
return ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + attribute.name;
}
static LocTextKey attributeNameLocKey(final ConfigurationAttribute attribute) {
if (attribute == null) {
return null;
}
return new LocTextKey(attributeNameKey(attribute));
}
static LocTextKey getToolTipKey(
final ConfigurationAttribute attribute,
final I18nSupport i18nSupport) {
final String attributeNameKey = ExamConfigurationService.attributeNameKey(attribute) + TOOL_TIP_SUFFIX;
if (StringUtils.isBlank(i18nSupport.getText(attributeNameKey, ""))) {
return null;
} else {
return new LocTextKey(attributeNameKey);
}
}
static LocTextKey getTablePopupTitleKey(
final ConfigurationAttribute attribute,
final I18nSupport i18nSupport) {
return new LocTextKey(ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + attribute.name + TABLE_ROW_TITLE_SUFFIX);
}
}

View file

@ -1,338 +1,336 @@
/*
* 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.examconfig.impl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues.TableValue;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon;
public abstract class AbstractTableFieldBuilder implements InputFieldBuilder {
private static final Logger log = LoggerFactory.getLogger(AbstractTableFieldBuilder.class);
private static final int ROW_HEIGHT = 20;
private static final int NAV_HEIGHT = 40;
private static final int TABLE_WIDTH_SPACE = 50;
protected final RestService restService;
protected final WidgetFactory widgetFactory;
protected InputFieldBuilderSupplier inputFieldBuilderSupplier;
protected AbstractTableFieldBuilder(
final RestService restService,
final WidgetFactory widgetFactory) {
this.restService = restService;
this.widgetFactory = widgetFactory;
}
@Override
public void init(final InputFieldBuilderSupplier inputFieldBuilderSupplier) {
this.inputFieldBuilderSupplier = inputFieldBuilderSupplier;
}
protected Table createTable(final Composite parent, final TableContext tableContext) {
final Table table = new Table(parent, SWT.NONE | SWT.V_SCROLL);
table.setLayout(new GridLayout());
final GridData gridData = new GridData(
SWT.FILL, SWT.FILL,
true, false,
tableContext.orientation.width(),
tableContext.orientation.height());
gridData.heightHint = tableContext.orientation.height() * ROW_HEIGHT + NAV_HEIGHT;
table.setLayoutData(gridData);
table.setHeaderVisible(true);
table.addListener(SWT.Resize, event -> adaptColumnWidth(table, tableContext));
return table;
}
protected TableContext createTableContext(final ConfigurationAttribute attribute, final ViewContext viewContext) {
final TableContext tableContext = new TableContext(
this.inputFieldBuilderSupplier,
this.widgetFactory,
attribute,
viewContext);
return tableContext;
}
protected void setSelectionListener(final Table table, final AbstractTableInputField tableField) {
table.addListener(SWT.MouseDoubleClick, event -> {
final int selectionIndex = table.getSelectionIndex();
if (selectionIndex >= 0) {
tableField.openForm(selectionIndex);
}
});
}
protected void adaptColumnWidth(
final Table table,
final TableContext tableContext) {
try {
final boolean readonly = tableContext.getViewContext().readonly;
final int currentTableWidth = table.getClientArea().width - TABLE_WIDTH_SPACE;
final TableColumn[] columns = table.getColumns();
final List<Orientation> orientations = tableContext
.getColumnAttributes()
.stream()
.map(attr -> tableContext.getOrientation(attr.id))
.collect(Collectors.toList());
final Integer div = orientations
.stream()
.map(o -> o.width)
.reduce(0, (acc, val) -> acc + val);
final int widthUnit = currentTableWidth / div;
for (int i = 0; i < columns.length - ((readonly) ? 0 : 2); i++) {
columns[i].setWidth(widthUnit * orientations.get(i).width);
}
} catch (final Exception e) {
log.warn("Failed to adaptColumnWidth: ", e);
}
}
protected static void setValueToCell(
final TableContext tableContext,
final TableItem item,
final int cellIndex,
final ConfigurationAttribute attribute,
final TableValue tableValue) {
switch (attribute.type) {
case CHECKBOX: {
item.setImage(
cellIndex,
(BooleanUtils.toBoolean((tableValue != null) ? tableValue.value : null))
? ImageIcon.YES.getImage(item.getDisplay())
: ImageIcon.NO.getImage(item.getDisplay()));
break;
}
case SINGLE_SELECTION: {
final String key = ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX +
attribute.getName() + "." +
tableValue.value;
item.setText(
cellIndex,
tableContext.i18nSupport().getText(key, getValue(attribute, tableValue)));
break;
}
default: {
item.setText(cellIndex, getValue(attribute, tableValue));
break;
}
}
}
private static String getValue(
final ConfigurationAttribute attribute,
final TableValue tableValue) {
if (tableValue == null) {
if (StringUtils.isBlank(attribute.defaultValue)) {
return Constants.EMPTY_NOTE;
} else {
return attribute.defaultValue;
}
} else {
if (StringUtils.isBlank(tableValue.value)) {
return Constants.EMPTY_NOTE;
} else {
return tableValue.value;
}
}
}
static abstract class AbstractTableInputField extends AbstractInputField<Table> {
protected final TableContext tableContext;
protected AbstractTableInputField(
final ConfigurationAttribute attribute,
final Orientation orientation,
final Table control,
final Label errorLabel,
final TableContext tableContext) {
super(attribute, orientation, control, errorLabel);
this.tableContext = tableContext;
}
@Override
public ConfigurationValue initValue(final Collection<ConfigurationValue> values) {
clearTable();
// get all child values as TableValues
final List<TableValue> tableValues = getChildValues(
this.tableContext,
this.attribute,
values);
initValue(tableValues);
return null;
}
abstract void initValue(final List<TableValue> tableValues);
abstract void openForm(final int selectionIndex);
abstract void applyTableRowValues(final int index);
protected List<TableValue> getChildValues(
final TableContext tableContext,
final ConfigurationAttribute attribute,
final Collection<ConfigurationValue> values) {
return values.stream()
.filter(v -> isChildValue(tableContext, attribute, v))
.map(TableValue::of)
.collect(Collectors.toList());
}
protected boolean isChildValue(
final TableContext tableContext,
final ConfigurationAttribute attribute,
final ConfigurationValue value) {
if (!tableContext.getViewContext().attributeMapping.attributeIdMapping
.containsKey(value.attributeId)) {
return false;
}
ConfigurationAttribute attr = tableContext.getAttribute(value.attributeId);
if (attr == null) {
return false;
}
while (attr.parentId != null) {
if (attribute.id.equals(attr.parentId)) {
return true;
}
attr = tableContext.getAttribute(attr.parentId);
}
return false;
}
protected Map<Integer, Map<Long, TableValue>> createRowIndexMap(final List<TableValue> tableValues) {
final Map<Integer, Map<Long, TableValue>> indexMapping = new HashMap<>();
for (final TableValue tableValue : tableValues) {
final Map<Long, TableValue> rowValues = indexMapping.computeIfAbsent(
tableValue.listIndex,
key -> new HashMap<>());
rowValues.put(tableValue.attributeId, tableValue);
}
return indexMapping;
}
protected void valuesFromIndexMap(
final List<Map<Long, TableValue>> values,
final Map<Integer, Map<Long, TableValue>> indexMapping) {
values.clear();
final List<Integer> rows = new ArrayList<>(indexMapping.keySet());
rows.sort((i1, i2) -> i1.compareTo(i2));
rows
.stream()
.forEach(i -> {
final Map<Long, TableValue> rowValues = indexMapping.get(i);
values.add(rowValues);
});
}
protected void applyFormValues(
final List<Map<Long, TableValue>> values,
final Map<Long, TableValue> rowValues,
final int index) {
if (!values.isEmpty()) {
values.remove(index);
values.add(index, rowValues);
applyTableRowValues(index);
}
// send values to web-service
this.tableContext.getValueChangeListener()
.tableChanged(extractTableValue(values));
}
protected ConfigurationTableValues extractTableValue(final List<Map<Long, TableValue>> values) {
final List<TableValue> collect = values
.stream()
.flatMap(map -> map.values().stream())
.collect(Collectors.toList());
return new ConfigurationTableValues(
this.tableContext.getInstitutionId(),
this.tableContext.getConfigurationId(),
this.attribute.id,
collect);
}
@Override
public void setDefaultValue() {
// NOTE this just empty the list for now
// TODO do we need default values for lists?
clearTable();
final List<TableValue> values = new ArrayList<>();
this.tableContext.getValueChangeListener().tableChanged(
new ConfigurationTableValues(
this.tableContext.getInstitutionId(),
this.tableContext.getConfigurationId(),
this.attribute.id,
values));
}
void clearTable() {
this.control.setSelection(-1);
if (this.control.getItemCount() > 0) {
for (final TableItem item : this.control.getItems()) {
item.dispose();
}
}
}
@Override
protected void setValueToControl(final String value) {
throw new UnsupportedOperationException();
}
@Override
public String getValue() {
throw new UnsupportedOperationException();
}
}
}
/*
* 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.examconfig.impl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues.TableValue;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon;
public abstract class AbstractTableFieldBuilder implements InputFieldBuilder {
private static final Logger log = LoggerFactory.getLogger(AbstractTableFieldBuilder.class);
private static final int ROW_HEIGHT = 20;
private static final int NAV_HEIGHT = 40;
private static final int TABLE_WIDTH_SPACE = 50;
protected final RestService restService;
protected final WidgetFactory widgetFactory;
protected InputFieldBuilderSupplier inputFieldBuilderSupplier;
protected AbstractTableFieldBuilder(
final RestService restService,
final WidgetFactory widgetFactory) {
this.restService = restService;
this.widgetFactory = widgetFactory;
}
@Override
public void init(final InputFieldBuilderSupplier inputFieldBuilderSupplier) {
this.inputFieldBuilderSupplier = inputFieldBuilderSupplier;
}
protected Table createTable(final Composite parent, final TableContext tableContext) {
final Table table = new Table(parent, SWT.NONE | SWT.V_SCROLL);
table.setLayout(new GridLayout());
final GridData gridData = new GridData(
SWT.FILL, SWT.FILL,
true, false,
tableContext.orientation.width(),
tableContext.orientation.height());
gridData.heightHint = tableContext.orientation.height() * ROW_HEIGHT + NAV_HEIGHT;
table.setLayoutData(gridData);
table.setHeaderVisible(true);
table.addListener(SWT.Resize, event -> adaptColumnWidth(table, tableContext));
return table;
}
protected TableContext createTableContext(final ConfigurationAttribute attribute, final ViewContext viewContext) {
return new TableContext(
this.inputFieldBuilderSupplier,
this.widgetFactory,
attribute,
viewContext);
}
protected void setSelectionListener(final Table table, final AbstractTableInputField tableField) {
table.addListener(SWT.MouseDoubleClick, event -> {
final int selectionIndex = table.getSelectionIndex();
if (selectionIndex >= 0) {
tableField.openForm(selectionIndex);
}
});
}
protected void adaptColumnWidth(
final Table table,
final TableContext tableContext) {
try {
final boolean readonly = tableContext.getViewContext().readonly;
final int currentTableWidth = table.getClientArea().width - TABLE_WIDTH_SPACE;
final TableColumn[] columns = table.getColumns();
final List<Orientation> orientations = tableContext
.getColumnAttributes()
.stream()
.map(attr -> tableContext.getOrientation(attr.id))
.collect(Collectors.toList());
final Integer div = orientations
.stream()
.map(o -> o.width)
.reduce(0, Integer::sum);
final int widthUnit = currentTableWidth / div;
for (int i = 0; i < columns.length - ((readonly) ? 0 : 2); i++) {
columns[i].setWidth(widthUnit * orientations.get(i).width);
}
} catch (final Exception e) {
log.warn("Failed to adaptColumnWidth: ", e);
}
}
protected static void setValueToCell(
final TableContext tableContext,
final TableItem item,
final int cellIndex,
final ConfigurationAttribute attribute,
final TableValue tableValue) {
switch (attribute.type) {
case CHECKBOX: {
item.setImage(
cellIndex,
(BooleanUtils.toBoolean((tableValue != null) ? tableValue.value : null))
? ImageIcon.YES.getImage(item.getDisplay())
: ImageIcon.NO.getImage(item.getDisplay()));
break;
}
case SINGLE_SELECTION: {
final String key = ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX +
attribute.getName() + "." +
tableValue.value;
item.setText(
cellIndex,
tableContext.i18nSupport().getText(key, getValue(attribute, tableValue)));
break;
}
default: {
item.setText(cellIndex, getValue(attribute, tableValue));
break;
}
}
}
private static String getValue(
final ConfigurationAttribute attribute,
final TableValue tableValue) {
if (tableValue == null) {
if (StringUtils.isBlank(attribute.defaultValue)) {
return Constants.EMPTY_NOTE;
} else {
return attribute.defaultValue;
}
} else {
if (StringUtils.isBlank(tableValue.value)) {
return Constants.EMPTY_NOTE;
} else {
return tableValue.value;
}
}
}
static abstract class AbstractTableInputField extends AbstractInputField<Table> {
protected final TableContext tableContext;
protected AbstractTableInputField(
final ConfigurationAttribute attribute,
final Orientation orientation,
final Table control,
final Label errorLabel,
final TableContext tableContext) {
super(attribute, orientation, control, errorLabel);
this.tableContext = tableContext;
}
@Override
public ConfigurationValue initValue(final Collection<ConfigurationValue> values) {
clearTable();
// get all child values as TableValues
final List<TableValue> tableValues = getChildValues(
this.tableContext,
this.attribute,
values);
initValue(tableValues);
return null;
}
abstract void initValue(final List<TableValue> tableValues);
abstract void openForm(final int selectionIndex);
abstract void applyTableRowValues(final int index);
protected List<TableValue> getChildValues(
final TableContext tableContext,
final ConfigurationAttribute attribute,
final Collection<ConfigurationValue> values) {
return values.stream()
.filter(v -> isChildValue(tableContext, attribute, v))
.map(TableValue::of)
.collect(Collectors.toList());
}
protected boolean isChildValue(
final TableContext tableContext,
final ConfigurationAttribute attribute,
final ConfigurationValue value) {
if (!tableContext.getViewContext().attributeMapping.attributeIdMapping
.containsKey(value.attributeId)) {
return false;
}
ConfigurationAttribute attr = tableContext.getAttribute(value.attributeId);
if (attr == null) {
return false;
}
while (attr.parentId != null) {
if (attribute.id.equals(attr.parentId)) {
return true;
}
attr = tableContext.getAttribute(attr.parentId);
}
return false;
}
protected Map<Integer, Map<Long, TableValue>> createRowIndexMap(final List<TableValue> tableValues) {
final Map<Integer, Map<Long, TableValue>> indexMapping = new HashMap<>();
for (final TableValue tableValue : tableValues) {
final Map<Long, TableValue> rowValues = indexMapping.computeIfAbsent(
tableValue.listIndex,
key -> new HashMap<>());
rowValues.put(tableValue.attributeId, tableValue);
}
return indexMapping;
}
protected void valuesFromIndexMap(
final List<Map<Long, TableValue>> values,
final Map<Integer, Map<Long, TableValue>> indexMapping) {
values.clear();
final List<Integer> rows = new ArrayList<>(indexMapping.keySet());
rows.sort(Integer::compareTo);
rows
.forEach(i -> {
final Map<Long, TableValue> rowValues = indexMapping.get(i);
values.add(rowValues);
});
}
protected void applyFormValues(
final List<Map<Long, TableValue>> values,
final Map<Long, TableValue> rowValues,
final int index) {
if (!values.isEmpty()) {
values.remove(index);
values.add(index, rowValues);
applyTableRowValues(index);
}
// send values to web-service
this.tableContext.getValueChangeListener()
.tableChanged(extractTableValue(values));
}
protected ConfigurationTableValues extractTableValue(final List<Map<Long, TableValue>> values) {
final List<TableValue> collect = values
.stream()
.flatMap(map -> map.values().stream())
.collect(Collectors.toList());
return new ConfigurationTableValues(
this.tableContext.getInstitutionId(),
this.tableContext.getConfigurationId(),
this.attribute.id,
collect);
}
@Override
public void setDefaultValue() {
// NOTE this just empty the list for now
// TODO do we need default values for lists?
clearTable();
final List<TableValue> values = new ArrayList<>();
this.tableContext.getValueChangeListener().tableChanged(
new ConfigurationTableValues(
this.tableContext.getInstitutionId(),
this.tableContext.getConfigurationId(),
this.attribute.id,
values));
}
void clearTable() {
this.control.setSelection(-1);
if (this.control.getItemCount() > 0) {
for (final TableItem item : this.control.getItems()) {
item.dispose();
}
}
}
@Override
protected void setValueToControl(final String value) {
throw new UnsupportedOperationException();
}
@Override
public String getValue() {
throw new UnsupportedOperationException();
}
}
}

View file

@ -1,225 +1,225 @@
/*
* 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.examconfig.impl;
import java.util.Collection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.TitleOrientation;
import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputField;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
interface CellFieldBuilderAdapter {
static CellFieldBuilderAdapter DUMMY_BUILDER_ADAPTER = new CellFieldBuilderAdapter() {
@Override
public void createCell(final ViewGridBuilder builder) {
}
@Override
public String toString() {
return "[DUMMY]";
}
};
void createCell(ViewGridBuilder builder);
default void balanceGrid(final CellFieldBuilderAdapter[][] grid, final int x, final int y) {
}
static CellFieldBuilderAdapter fieldBuilderAdapter(
final InputFieldBuilder inputFieldBuilder,
final ConfigurationAttribute attribute) {
return new CellFieldBuilderAdapter() {
@Override
public void createCell(final ViewGridBuilder builder) {
final InputField inputField = inputFieldBuilder.createInputField(
builder.parent,
attribute,
builder.viewContext);
if (inputField != null) {
builder.viewContext.registerInputField(inputField);
}
}
@Override
public String toString() {
return "[FIELD]";
}
};
}
static CellFieldBuilderAdapter labelBuilder(
final ConfigurationAttribute attribute,
final Orientation orientation) {
return new CellFieldBuilderAdapter() {
private int span = 1;
@Override
public void createCell(final ViewGridBuilder builder) {
final WidgetFactory widgetFactory = builder.examConfigurationService.getWidgetFactory();
final Label label = widgetFactory.labelLocalized(
builder.parent,
new LocTextKey(ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + attribute.name,
attribute.name));
final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false);
switch (orientation.title) {
case LEFT:
case RIGHT: {
label.setAlignment(SWT.LEFT);
gridData.verticalIndent = 5;
break;
}
case RIGHT_SPAN:
case LEFT_SPAN: {
label.setAlignment(SWT.LEFT);
gridData.horizontalSpan = (span > 1) ? span : orientation.width;
gridData.verticalIndent = 5;
break;
}
case TOP: {
gridData.horizontalSpan = orientation.width;
gridData.verticalAlignment = SWT.BOTTOM;
break;
}
default: {
label.setAlignment(SWT.LEFT);
break;
}
}
label.setLayoutData(gridData);
label.pack();
}
@Override
public void balanceGrid(final CellFieldBuilderAdapter[][] grid, final int x, final int y) {
if (grid[y][x] != this) {
return;
}
if (orientation.title == TitleOrientation.LEFT_SPAN) {
int xpos = x - 1;
while (xpos >= 0 && grid[y][xpos] == null && span < orientation.width) {
grid[y][xpos] = this;
grid[y][xpos + 1] = DUMMY_BUILDER_ADAPTER;
this.span++;
xpos--;
}
}
}
@Override
public String toString() {
return "[LABEL]";
}
};
}
static CellFieldBuilderAdapter passwordConfirmLabel(
final ConfigurationAttribute attribute,
final Orientation orientation) {
return new CellFieldBuilderAdapter() {
@Override
public void createCell(final ViewGridBuilder builder) {
final WidgetFactory widgetFactory = builder.examConfigurationService.getWidgetFactory();
final Label label = widgetFactory.labelLocalized(
builder.parent,
new LocTextKey(
ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + attribute.name
+ ".confirm"));
final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false);
label.setAlignment(SWT.LEFT);
gridData.verticalIndent = 20;
label.setLayoutData(gridData);
}
@Override
public String toString() {
return "[PASSWORD CONFIRM LABEL]";
}
};
}
static class GroupCellFieldBuilderAdapter implements CellFieldBuilderAdapter {
final Collection<Orientation> orientationsOfGroup;
int x = 100;
int y = 100;
int width = 1;
int height = 1;
GroupCellFieldBuilderAdapter(final Collection<Orientation> orientationsOfGroup) {
this.orientationsOfGroup = orientationsOfGroup;
for (final Orientation o : this.orientationsOfGroup) {
final int xpos = o.xPosition - ((o.title == TitleOrientation.LEFT) ? 1 : 0);
this.x = (xpos < this.x) ? xpos : this.x;
final int ypos = o.yPosition - ((o.title == TitleOrientation.TOP) ? 1 : 0);
this.y = (ypos < this.y) ? ypos : this.y;
this.width = (this.width < o.xpos() + o.width()) ? o.xpos() + o.width() : this.width;
this.height = (this.height < o.ypos() + o.height()) ? o.ypos() + o.height() : this.height;
}
this.width = this.width - this.x;
this.height = this.height - this.y + 1;
}
@Override
public void createCell(final ViewGridBuilder builder) {
final WidgetFactory widgetFactory = builder.examConfigurationService.getWidgetFactory();
final Orientation o = this.orientationsOfGroup.stream().findFirst().get();
final LocTextKey groupLabelKey = new LocTextKey(
ExamConfigurationService.GROUP_LABEL_LOC_TEXT_PREFIX +
o.groupId,
o.groupId);
final LocTextKey groupTooltipKey = new LocTextKey(
ExamConfigurationService.GROUP_LABEL_LOC_TEXT_PREFIX +
o.groupId +
ExamConfigurationService.TOOL_TIP_SUFFIX,
o.groupId);
final Group group = widgetFactory.groupLocalized(
builder.parent,
this.width,
groupLabelKey,
groupTooltipKey);
group.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, this.width, this.height));
final ViewGridBuilder groupBuilder = new ViewGridBuilder(
group,
builder.viewContext,
this,
builder.examConfigurationService);
for (final Orientation orientation : this.orientationsOfGroup) {
final ConfigurationAttribute attribute = builder.viewContext.getAttribute(orientation.attributeId);
groupBuilder.add(attribute);
}
groupBuilder.compose();
}
}
/*
* 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.examconfig.impl;
import java.util.Collection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.TitleOrientation;
import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputField;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
interface CellFieldBuilderAdapter {
CellFieldBuilderAdapter DUMMY_BUILDER_ADAPTER = new CellFieldBuilderAdapter() {
@Override
public void createCell(final ViewGridBuilder builder) {
}
@Override
public String toString() {
return "[DUMMY]";
}
};
void createCell(ViewGridBuilder builder);
default void balanceGrid(final CellFieldBuilderAdapter[][] grid, final int x, final int y) {
}
static CellFieldBuilderAdapter fieldBuilderAdapter(
final InputFieldBuilder inputFieldBuilder,
final ConfigurationAttribute attribute) {
return new CellFieldBuilderAdapter() {
@Override
public void createCell(final ViewGridBuilder builder) {
final InputField inputField = inputFieldBuilder.createInputField(
builder.parent,
attribute,
builder.viewContext);
if (inputField != null) {
builder.viewContext.registerInputField(inputField);
}
}
@Override
public String toString() {
return "[FIELD]";
}
};
}
static CellFieldBuilderAdapter labelBuilder(
final ConfigurationAttribute attribute,
final Orientation orientation) {
return new CellFieldBuilderAdapter() {
private int span = 1;
@Override
public void createCell(final ViewGridBuilder builder) {
final WidgetFactory widgetFactory = builder.examConfigurationService.getWidgetFactory();
final Label label = widgetFactory.labelLocalized(
builder.parent,
new LocTextKey(ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + attribute.name,
attribute.name));
final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false);
switch (orientation.title) {
case LEFT:
case RIGHT: {
label.setAlignment(SWT.LEFT);
gridData.verticalIndent = 5;
break;
}
case RIGHT_SPAN:
case LEFT_SPAN: {
label.setAlignment(SWT.LEFT);
gridData.horizontalSpan = (span > 1) ? span : orientation.width;
gridData.verticalIndent = 5;
break;
}
case TOP: {
gridData.horizontalSpan = orientation.width;
gridData.verticalAlignment = SWT.BOTTOM;
break;
}
default: {
label.setAlignment(SWT.LEFT);
break;
}
}
label.setLayoutData(gridData);
label.pack();
}
@Override
public void balanceGrid(final CellFieldBuilderAdapter[][] grid, final int x, final int y) {
if (grid[y][x] != this) {
return;
}
if (orientation.title == TitleOrientation.LEFT_SPAN) {
int xpos = x - 1;
while (xpos >= 0 && grid[y][xpos] == null && span < orientation.width) {
grid[y][xpos] = this;
grid[y][xpos + 1] = DUMMY_BUILDER_ADAPTER;
this.span++;
xpos--;
}
}
}
@Override
public String toString() {
return "[LABEL]";
}
};
}
static CellFieldBuilderAdapter passwordConfirmLabel(
final ConfigurationAttribute attribute,
final Orientation orientation) {
return new CellFieldBuilderAdapter() {
@Override
public void createCell(final ViewGridBuilder builder) {
final WidgetFactory widgetFactory = builder.examConfigurationService.getWidgetFactory();
final Label label = widgetFactory.labelLocalized(
builder.parent,
new LocTextKey(
ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + attribute.name
+ ".confirm"));
final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false);
label.setAlignment(SWT.LEFT);
gridData.verticalIndent = 20;
label.setLayoutData(gridData);
}
@Override
public String toString() {
return "[PASSWORD CONFIRM LABEL]";
}
};
}
class GroupCellFieldBuilderAdapter implements CellFieldBuilderAdapter {
final Collection<Orientation> orientationsOfGroup;
int x = 100;
int y = 100;
int width = 1;
int height = 1;
GroupCellFieldBuilderAdapter(final Collection<Orientation> orientationsOfGroup) {
this.orientationsOfGroup = orientationsOfGroup;
for (final Orientation o : this.orientationsOfGroup) {
final int xpos = o.xPosition - ((o.title == TitleOrientation.LEFT) ? 1 : 0);
this.x = Math.min(xpos, this.x);
final int ypos = o.yPosition - ((o.title == TitleOrientation.TOP) ? 1 : 0);
this.y = Math.min(ypos, this.y);
this.width = Math.max(this.width, o.xpos() + o.width());
this.height = Math.max(this.height, o.ypos() + o.height());
}
this.width = this.width - this.x;
this.height = this.height - this.y + 1;
}
@Override
public void createCell(final ViewGridBuilder builder) {
final WidgetFactory widgetFactory = builder.examConfigurationService.getWidgetFactory();
final Orientation o = this.orientationsOfGroup.stream().findFirst().orElse(null);
final LocTextKey groupLabelKey = new LocTextKey(
ExamConfigurationService.GROUP_LABEL_LOC_TEXT_PREFIX +
o.groupId,
o.groupId);
final LocTextKey groupTooltipKey = new LocTextKey(
ExamConfigurationService.GROUP_LABEL_LOC_TEXT_PREFIX +
o.groupId +
ExamConfigurationService.TOOL_TIP_SUFFIX,
o.groupId);
final Group group = widgetFactory.groupLocalized(
builder.parent,
this.width,
groupLabelKey,
groupTooltipKey);
group.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, this.width, this.height));
final ViewGridBuilder groupBuilder = new ViewGridBuilder(
group,
builder.viewContext,
this,
builder.examConfigurationService);
for (final Orientation orientation : this.orientationsOfGroup) {
final ConfigurationAttribute attribute = builder.viewContext.getAttribute(orientation.attributeId);
groupBuilder.add(attribute);
}
groupBuilder.compose();
}
}
}

View file

@ -1,134 +1,134 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui.service.examconfig.impl;
import java.util.Objects;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
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.sebconfig.AttributeType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.TitleOrientation;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputField;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder;
import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@Lazy
@Component
@GuiProfile
public class CheckBoxBuilder implements InputFieldBuilder {
private final WidgetFactory widgetFactory;
protected CheckBoxBuilder(final WidgetFactory widgetFactory) {
this.widgetFactory = widgetFactory;
}
@Override
public boolean builderFor(
final ConfigurationAttribute attribute,
final Orientation orientation) {
if (attribute == null) {
return false;
}
return attribute.type == AttributeType.CHECKBOX;
}
@Override
public InputField createInputField(
final Composite parent,
final ConfigurationAttribute attribute,
final ViewContext viewContext) {
Objects.requireNonNull(parent);
Objects.requireNonNull(attribute);
Objects.requireNonNull(viewContext);
final I18nSupport i18nSupport = this.widgetFactory.getI18nSupport();
final Orientation orientation = viewContext
.getOrientation(attribute.id);
final Composite innerGrid = InputFieldBuilder
.createInnerGrid(parent, attribute, orientation);
final Button checkbox = this.widgetFactory.buttonLocalized(
innerGrid,
SWT.CHECK,
(orientation.title == TitleOrientation.NONE)
? ExamConfigurationService.attributeNameLocKey(attribute)
: null,
ExamConfigurationService.getToolTipKey(attribute, i18nSupport));
final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false);
gridData.verticalIndent = 0;
checkbox.setLayoutData(gridData);
final CheckboxField checkboxField = new CheckboxField(
attribute,
viewContext.getOrientation(attribute.id),
checkbox);
if (viewContext.readonly) {
checkbox.setEnabled(false);
} else {
checkbox.addListener(
SWT.Selection,
event -> viewContext.getValueChangeListener().valueChanged(
viewContext,
attribute,
checkboxField.getValue(),
checkboxField.listIndex));
}
return checkboxField;
}
static final class CheckboxField extends AbstractInputField<Button> {
CheckboxField(
final ConfigurationAttribute attribute,
final Orientation orientation,
final Button control) {
super(attribute, orientation, control, null);
}
@Override
protected void setValueToControl(final String value) {
this.control.setSelection(Boolean.valueOf(this.initValue));
}
@Override
public String getValue() {
return this.control.getSelection()
? Constants.TRUE_STRING
: Constants.FALSE_STRING;
}
@Override
public String getReadableValue() {
return this.control.getSelection()
? "Active"
: "Inactive";
}
}
}
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui.service.examconfig.impl;
import java.util.Objects;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
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.sebconfig.AttributeType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.TitleOrientation;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputField;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder;
import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@Lazy
@Component
@GuiProfile
public class CheckBoxBuilder implements InputFieldBuilder {
private final WidgetFactory widgetFactory;
protected CheckBoxBuilder(final WidgetFactory widgetFactory) {
this.widgetFactory = widgetFactory;
}
@Override
public boolean builderFor(
final ConfigurationAttribute attribute,
final Orientation orientation) {
if (attribute == null) {
return false;
}
return attribute.type == AttributeType.CHECKBOX;
}
@Override
public InputField createInputField(
final Composite parent,
final ConfigurationAttribute attribute,
final ViewContext viewContext) {
Objects.requireNonNull(parent);
Objects.requireNonNull(attribute);
Objects.requireNonNull(viewContext);
final I18nSupport i18nSupport = this.widgetFactory.getI18nSupport();
final Orientation orientation = viewContext
.getOrientation(attribute.id);
final Composite innerGrid = InputFieldBuilder
.createInnerGrid(parent, attribute, orientation);
final Button checkbox = this.widgetFactory.buttonLocalized(
innerGrid,
SWT.CHECK,
(orientation.title == TitleOrientation.NONE)
? ExamConfigurationService.attributeNameLocKey(attribute)
: null,
ExamConfigurationService.getToolTipKey(attribute, i18nSupport));
final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false);
gridData.verticalIndent = 0;
checkbox.setLayoutData(gridData);
final CheckboxField checkboxField = new CheckboxField(
attribute,
viewContext.getOrientation(attribute.id),
checkbox);
if (viewContext.readonly) {
checkbox.setEnabled(false);
} else {
checkbox.addListener(
SWT.Selection,
event -> viewContext.getValueChangeListener().valueChanged(
viewContext,
attribute,
checkboxField.getValue(),
checkboxField.listIndex));
}
return checkboxField;
}
static final class CheckboxField extends AbstractInputField<Button> {
CheckboxField(
final ConfigurationAttribute attribute,
final Orientation orientation,
final Button control) {
super(attribute, orientation, control, null);
}
@Override
protected void setValueToControl(final String value) {
this.control.setSelection(Boolean.parseBoolean(this.initValue));
}
@Override
public String getValue() {
return this.control.getSelection()
? Constants.TRUE_STRING
: Constants.FALSE_STRING;
}
@Override
public String getReadableValue() {
return this.control.getSelection()
? "Active"
: "Inactive";
}
}
}

View file

@ -1,270 +1,270 @@
/*
* 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.examconfig.impl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.sebconfig.AttributeType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues.TableValue;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputField;
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.impl.ModalInputDialog;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@Lazy
@Component
@GuiProfile
public class CompositeTableFieldBuilder extends AbstractTableFieldBuilder {
private static final Logger log = LoggerFactory.getLogger(CompositeTableFieldBuilder.class);
private static final String TABLE_ENTRY_NAME = "TABLE_ENTRY";
private static final String TABLE_COLUMN_NAME_KEY = "TABLE_COLUMN_NAME";
protected CompositeTableFieldBuilder(
final RestService restService,
final WidgetFactory widgetFactory) {
super(restService, widgetFactory);
}
@Override
public boolean builderFor(
final ConfigurationAttribute attribute,
final Orientation orientation) {
if (attribute == null) {
return false;
}
return AttributeType.COMPOSITE_TABLE == attribute.type;
}
@Override
public InputField createInputField(
final Composite parent,
final ConfigurationAttribute attribute,
final ViewContext viewContext) {
final I18nSupport i18nSupport = viewContext.getI18nSupport();
final TableContext tableContext = createTableContext(attribute, viewContext);
final Table table = createTable(parent, tableContext);
final String resources = attribute.getResources();
final String[] columnsAndRows = StringUtils.split(
resources,
Constants.EMBEDDED_LIST_SEPARATOR);
final String[] columns = (columnsAndRows.length == 2)
? StringUtils.split(columnsAndRows[0], Constants.LIST_SEPARATOR)
: new String[] { TABLE_ENTRY_NAME };
final String[] rows = (columnsAndRows.length == 2)
? StringUtils.split(columnsAndRows[1], Constants.LIST_SEPARATOR)
: StringUtils.split(columnsAndRows[0], Constants.LIST_SEPARATOR);
final String attributeNameKey = ExamConfigurationService.attributeNameKey(attribute);
for (int i = 0; i < columns.length; i++) {
final TableColumn column = this.widgetFactory.tableColumnLocalized(
table,
new LocTextKey(attributeNameKey + "." + columns[i]),
new LocTextKey(attributeNameKey + "." + columns[i] + ".tootlip"));
column.setData(TABLE_COLUMN_NAME_KEY, columns[i]);
column.setWidth(100);
column.setResizable(false);
column.setMoveable(false);
}
for (int i = 0; i < rows.length; i++) {
final TableItem item = new TableItem(table, SWT.NONE);
for (int j = 0; j < columns.length; j++) {
if (TABLE_ENTRY_NAME.equals(columns[j])) {
item.setText(j, i18nSupport.getText(
ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + rows[i],
rows[i]));
}
}
}
final CompositeTableInputField tableField = new CompositeTableInputField(
tableContext,
table,
Arrays.asList(columns),
Arrays.asList(rows));
setSelectionListener(table, tableField);
return tableField;
}
@Override
protected void adaptColumnWidth(
final Table table,
final TableContext tableContext) {
try {
final int currentTableWidth = table.getClientArea().width - 50;
final TableColumn[] columns = table.getColumns();
final int widthUnit = currentTableWidth / (columns.length + 2);
for (int i = 0; i < columns.length; i++) {
final int factor = (TABLE_ENTRY_NAME.equals(columns[i].getData(TABLE_COLUMN_NAME_KEY))) ? 4 : 1;
columns[i].setWidth(widthUnit * factor);
}
} catch (final Exception e) {
log.warn("Failed to adaptColumnWidth: ", e);
}
}
static final class CompositeTableInputField extends AbstractTableInputField {
final List<String> columns;
final List<String> rows;
final List<Map<Long, TableValue>> values;
CompositeTableInputField(
final TableContext tableContext,
final Table control,
final List<String> columns,
final List<String> rows) {
super(tableContext.attribute, tableContext.orientation, control, null, tableContext);
this.values = new ArrayList<>();
this.columns = columns;
this.rows = rows;
}
@Override
void initValue(final List<TableValue> tableValues) {
valuesFromIndexMap(this.values, createRowIndexMap(tableValues));
for (int i = 0; i < this.values.size(); i++) {
setRowValues(i, this.values.get(i));
}
}
@Override
protected void applyTableRowValues(final int index) {
setRowValues(index, this.values.get(index));
}
private void setRowValues(final int index, final Map<Long, TableValue> map) {
final TableItem rowItem = this.control.getItem(index);
for (final TableValue val : map.values()) {
final Orientation orientation = this.tableContext.getOrientation(val.attributeId);
final String groupId = orientation.getGroupId();
if (StringUtils.isNotBlank(groupId)) {
final int cellIndex = this.columns.indexOf(groupId);
if (cellIndex >= 0) {
setValueToCell(
this.tableContext,
rowItem,
cellIndex,
this.tableContext.getAttribute(val.attributeId),
val);
}
}
}
}
@Override
protected void openForm(final int selectionIndex) {
final String row = this.rows.get(selectionIndex);
final Map<Long, TableValue> rowValues = this.values.get(selectionIndex);
final TableRowFormBuilder builder = new TableRowFormBuilder(
this.tableContext,
rowValues,
row);
final ModalInputDialog<Map<Long, TableValue>> dialog = new ModalInputDialog<Map<Long, TableValue>>(
this.control.getShell(),
this.tableContext.getWidgetFactory())
.setDialogWidth(500);
if (this.tableContext.getViewContext().readonly) {
dialog.open(
new LocTextKey(ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + row),
builder);
} else {
dialog.open(
new LocTextKey(ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + row),
(Consumer<Map<Long, TableValue>>) rowVals -> applyFormValues(
this.values,
rowVals,
selectionIndex),
() -> this.tableContext.getValueChangeListener()
.tableChanged(extractTableValue(this.values)),
builder);
}
}
@Override
protected Map<Integer, Map<Long, TableValue>> createRowIndexMap(final List<TableValue> tableValues) {
final Map<Integer, Map<Long, TableValue>> indexMapping = new HashMap<>();
for (final TableValue tableValue : tableValues) {
final ConfigurationAttribute attribute = this.tableContext
.getViewContext()
.getAttribute(tableValue.attributeId);
final String groupId = ConfigurationAttribute.getDependencyValue(
ConfigurationAttribute.DEPENDENCY_GROUP_ID,
attribute);
final int index = this.rows.indexOf(groupId);
final Map<Long, TableValue> rowValues = indexMapping.computeIfAbsent(
index,
key -> new HashMap<>());
rowValues.put(tableValue.attributeId, tableValue);
}
return indexMapping;
}
@Override
void clearTable() {
// nothing to clear for this table type
}
@Override
protected boolean isChildValue(
final TableContext tableContext,
final ConfigurationAttribute attribute,
final ConfigurationValue value) {
final boolean childValue = super.isChildValue(tableContext, attribute, value);
if (childValue) {
final ConfigurationAttribute attr = tableContext.getAttribute(value.attributeId);
return ConfigurationAttribute.hasDependency(
ConfigurationAttribute.DEPENDENCY_GROUP_ID,
attr);
}
return childValue;
}
}
}
/*
* 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.examconfig.impl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.sebconfig.AttributeType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues.TableValue;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputField;
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.impl.ModalInputDialog;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@Lazy
@Component
@GuiProfile
public class CompositeTableFieldBuilder extends AbstractTableFieldBuilder {
private static final Logger log = LoggerFactory.getLogger(CompositeTableFieldBuilder.class);
private static final String TABLE_ENTRY_NAME = "TABLE_ENTRY";
private static final String TABLE_COLUMN_NAME_KEY = "TABLE_COLUMN_NAME";
protected CompositeTableFieldBuilder(
final RestService restService,
final WidgetFactory widgetFactory) {
super(restService, widgetFactory);
}
@Override
public boolean builderFor(
final ConfigurationAttribute attribute,
final Orientation orientation) {
if (attribute == null) {
return false;
}
return AttributeType.COMPOSITE_TABLE == attribute.type;
}
@Override
public InputField createInputField(
final Composite parent,
final ConfigurationAttribute attribute,
final ViewContext viewContext) {
final I18nSupport i18nSupport = viewContext.getI18nSupport();
final TableContext tableContext = createTableContext(attribute, viewContext);
final Table table = createTable(parent, tableContext);
final String resources = attribute.getResources();
final String[] columnsAndRows = StringUtils.split(
resources,
Constants.EMBEDDED_LIST_SEPARATOR);
final String[] columns = (columnsAndRows.length == 2)
? StringUtils.split(columnsAndRows[0], Constants.LIST_SEPARATOR)
: new String[] { TABLE_ENTRY_NAME };
final String[] rows = (columnsAndRows.length == 2)
? StringUtils.split(columnsAndRows[1], Constants.LIST_SEPARATOR)
: StringUtils.split(columnsAndRows[0], Constants.LIST_SEPARATOR);
final String attributeNameKey = ExamConfigurationService.attributeNameKey(attribute);
for (int i = 0; i < columns.length; i++) {
final TableColumn column = this.widgetFactory.tableColumnLocalized(
table,
new LocTextKey(attributeNameKey + "." + columns[i]),
new LocTextKey(attributeNameKey + "." + columns[i] + ".tootlip"));
column.setData(TABLE_COLUMN_NAME_KEY, columns[i]);
column.setWidth(100);
column.setResizable(false);
column.setMoveable(false);
}
for (int i = 0; i < rows.length; i++) {
final TableItem item = new TableItem(table, SWT.NONE);
for (int j = 0; j < columns.length; j++) {
if (TABLE_ENTRY_NAME.equals(columns[j])) {
item.setText(j, i18nSupport.getText(
ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + rows[i],
rows[i]));
}
}
}
final CompositeTableInputField tableField = new CompositeTableInputField(
tableContext,
table,
Arrays.asList(columns),
Arrays.asList(rows));
setSelectionListener(table, tableField);
return tableField;
}
@Override
protected void adaptColumnWidth(
final Table table,
final TableContext tableContext) {
try {
final int currentTableWidth = table.getClientArea().width - 50;
final TableColumn[] columns = table.getColumns();
final int widthUnit = currentTableWidth / (columns.length + 2);
for (int i = 0; i < columns.length; i++) {
final int factor = (TABLE_ENTRY_NAME.equals(columns[i].getData(TABLE_COLUMN_NAME_KEY))) ? 4 : 1;
columns[i].setWidth(widthUnit * factor);
}
} catch (final Exception e) {
log.warn("Failed to adaptColumnWidth: ", e);
}
}
static final class CompositeTableInputField extends AbstractTableInputField {
final List<String> columns;
final List<String> rows;
final List<Map<Long, TableValue>> values;
CompositeTableInputField(
final TableContext tableContext,
final Table control,
final List<String> columns,
final List<String> rows) {
super(tableContext.attribute, tableContext.orientation, control, null, tableContext);
this.values = new ArrayList<>();
this.columns = columns;
this.rows = rows;
}
@Override
void initValue(final List<TableValue> tableValues) {
valuesFromIndexMap(this.values, createRowIndexMap(tableValues));
for (int i = 0; i < this.values.size(); i++) {
setRowValues(i, this.values.get(i));
}
}
@Override
protected void applyTableRowValues(final int index) {
setRowValues(index, this.values.get(index));
}
private void setRowValues(final int index, final Map<Long, TableValue> map) {
final TableItem rowItem = this.control.getItem(index);
for (final TableValue val : map.values()) {
final Orientation orientation = this.tableContext.getOrientation(val.attributeId);
final String groupId = orientation.getGroupId();
if (StringUtils.isNotBlank(groupId)) {
final int cellIndex = this.columns.indexOf(groupId);
if (cellIndex >= 0) {
setValueToCell(
this.tableContext,
rowItem,
cellIndex,
this.tableContext.getAttribute(val.attributeId),
val);
}
}
}
}
@Override
protected void openForm(final int selectionIndex) {
final String row = this.rows.get(selectionIndex);
final Map<Long, TableValue> rowValues = this.values.get(selectionIndex);
final TableRowFormBuilder builder = new TableRowFormBuilder(
this.tableContext,
rowValues,
row);
final ModalInputDialog<Map<Long, TableValue>> dialog = new ModalInputDialog<Map<Long, TableValue>>(
this.control.getShell(),
this.tableContext.getWidgetFactory())
.setDialogWidth(500);
if (this.tableContext.getViewContext().readonly) {
dialog.open(
new LocTextKey(ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + row),
builder);
} else {
dialog.open(
new LocTextKey(ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + row),
(Consumer<Map<Long, TableValue>>) _rowValues -> applyFormValues(
this.values,
_rowValues,
selectionIndex),
() -> this.tableContext.getValueChangeListener()
.tableChanged(extractTableValue(this.values)),
builder);
}
}
@Override
protected Map<Integer, Map<Long, TableValue>> createRowIndexMap(final List<TableValue> tableValues) {
final Map<Integer, Map<Long, TableValue>> indexMapping = new HashMap<>();
for (final TableValue tableValue : tableValues) {
final ConfigurationAttribute attribute = this.tableContext
.getViewContext()
.getAttribute(tableValue.attributeId);
final String groupId = ConfigurationAttribute.getDependencyValue(
ConfigurationAttribute.DEPENDENCY_GROUP_ID,
attribute);
final int index = this.rows.indexOf(groupId);
final Map<Long, TableValue> rowValues = indexMapping.computeIfAbsent(
index,
key -> new HashMap<>());
rowValues.put(tableValue.attributeId, tableValue);
}
return indexMapping;
}
@Override
void clearTable() {
// nothing to clear for this table type
}
@Override
protected boolean isChildValue(
final TableContext tableContext,
final ConfigurationAttribute attribute,
final ConfigurationValue value) {
final boolean childValue = super.isChildValue(tableContext, attribute, value);
if (childValue) {
final ConfigurationAttribute attr = tableContext.getAttribute(value.attributeId);
return ConfigurationAttribute.hasDependency(
ConfigurationAttribute.DEPENDENCY_GROUP_ID,
attr);
}
return childValue;
}
}
}

View file

@ -1,423 +1,414 @@
/*
* 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.examconfig.impl;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.tomcat.util.buf.StringUtils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.APIMessage.ErrorMessage;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.TemplateAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.View;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder;
import ch.ethz.seb.sebserver.gui.service.examconfig.ValueChangeListener;
import ch.ethz.seb.sebserver.gui.service.examconfig.ValueChangeRule;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
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.impl.PageAction;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCallError;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.AttachDefaultOrientation;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetConfigAttributes;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetConfigurationValues;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetOrientations;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetViewList;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.RemoveOrientation;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.ResetTemplateValues;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfigTableValues;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfigValue;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@Lazy
@Service
@GuiProfile
public class ExamConfigurationServiceImpl implements ExamConfigurationService {
private static final Logger log = LoggerFactory.getLogger(ExamConfigurationServiceImpl.class);
private final RestService restService;
private final JSONMapper jsonMapper;
private final WidgetFactory widgetFactory;
private final InputFieldBuilderSupplier inputFieldBuilderSupplier;
private final Collection<ValueChangeRule> valueChangeRules;
public ExamConfigurationServiceImpl(
final RestService restService,
final JSONMapper jsonMapper,
final WidgetFactory widgetFactory,
final InputFieldBuilderSupplier inputFieldBuilderSupplier,
final Collection<ValueChangeRule> valueChangeRules) {
this.restService = restService;
this.jsonMapper = jsonMapper;
this.widgetFactory = widgetFactory;
this.inputFieldBuilderSupplier = inputFieldBuilderSupplier;
this.valueChangeRules = Utils.immutableCollectionOf(valueChangeRules);
}
@Override
public WidgetFactory getWidgetFactory() {
return this.widgetFactory;
}
@Override
public InputFieldBuilder getInputFieldBuilder(
final ConfigurationAttribute attribute,
final Orientation orientation) {
return this.inputFieldBuilderSupplier.getInputFieldBuilder(attribute, orientation);
}
@Override
public Result<AttributeMapping> getAttributes(final Long templateId) {
return Result.tryCatch(() -> {
return new AttributeMapping(
templateId,
getAttributes(),
getOrientations(templateId));
});
}
@Override
public Result<AttributeMapping> getAttributes(
final TemplateAttribute attribute,
final Orientation defaultOrientation) {
final List<Orientation> orientations = getOrientations(attribute.templateId);
if (attribute.getOrientation() == null) {
orientations.add(defaultOrientation);
}
return Result.tryCatch(() -> {
return new AttributeMapping(
attribute.templateId,
getAttributes(),
orientations);
});
}
private List<Orientation> getOrientations(final Long templateId) {
return this.restService
.getBuilder(GetOrientations.class)
.withQueryParam(Orientation.FILTER_ATTR_TEMPLATE_ID, String.valueOf(templateId))
.call()
.onError(t -> log.error("Failed to get all Orientation of template {}", templateId, t))
.getOrThrow();
}
private List<ConfigurationAttribute> getAttributes() {
return this.restService
.getBuilder(GetConfigAttributes.class)
.call()
.onError(t -> log.error("Failed to get all ConfigurationAttribute"))
.getOrThrow();
}
@Override
public List<View> getViews(final AttributeMapping allAttributes) {
final Collection<Long> viewIds = allAttributes.getViewIds();
if (viewIds == null || viewIds.isEmpty()) {
return Collections.emptyList();
}
final String ids = StringUtils.join(
viewIds
.stream()
.map(String::valueOf)
.collect(Collectors.toList()),
Constants.LIST_SEPARATOR_CHAR);
return this.restService.getBuilder(GetViewList.class)
.withQueryParam(API.PARAM_MODEL_ID_LIST, ids)
.call()
.getOrThrow()
.stream()
.sorted((v1, v2) -> v1.position.compareTo(v2.position))
.collect(Collectors.toList());
}
@Override
public ViewContext createViewContext(
final PageContext pageContext,
final Configuration configuration,
final View view,
final AttributeMapping attributeMapping,
final int rows,
final boolean readonly) {
return new ViewContext(
configuration,
view,
rows,
attributeMapping,
new ValueChangeListenerImpl(
pageContext,
this.restService,
this.jsonMapper,
this.valueChangeRules),
this.widgetFactory.getI18nSupport(),
readonly);
}
@Override
public Composite createViewGrid(final Composite parent, final ViewContext viewContext) {
final Composite composite = new Composite(parent, SWT.NONE);
final GridLayout gridLayout = new GridLayout(viewContext.getColumns(), true);
gridLayout.verticalSpacing = 0;
composite.setLayout(gridLayout);
final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false);
composite.setLayoutData(gridData);
final ViewGridBuilder viewGridBuilder = new ViewGridBuilder(
composite,
viewContext,
this);
for (final ConfigurationAttribute attribute : viewContext.getAttributes()) {
final Orientation orientation = viewContext.getOrientation(attribute.id);
if (orientation != null && viewContext.getId().equals(orientation.viewId)) {
viewGridBuilder.add(attribute);
}
}
viewGridBuilder.compose();
return composite;
}
@Override
public void initInputFieldValues(
final Long configurationId,
final Collection<ViewContext> viewContexts) {
if (viewContexts == null || viewContexts.size() < 1) {
log.warn("No viewContexts available");
return;
}
final Collection<ConfigurationValue> attributeValues = this.restService
.getBuilder(GetConfigurationValues.class)
.withQueryParam(
ConfigurationValue.FILTER_ATTR_CONFIGURATION_ID,
String.valueOf(configurationId))
.call()
.onError(t -> log.error(
"Failed to get all ConfigurationValue for configuration with id: {}",
configurationId))
.getOrElse(Collections::emptyList);
viewContexts
.forEach(vc -> vc.setValuesToInputFields(attributeValues));
}
@Override
public final PageAction resetToDefaults(final PageAction action) {
final EntityKey parentEntityKey = action.pageContext().getParentEntityKey();
final Set<EntityKey> selection = action.getMultiSelection();
if (selection != null && !selection.isEmpty()) {
selection.stream().forEach(entityKey -> {
callTemplateAction(
ResetTemplateValues.class,
parentEntityKey.modelId,
entityKey.modelId);
});
} else {
final EntityKey entityKey = action.getEntityKey();
callTemplateAction(
ResetTemplateValues.class,
parentEntityKey.modelId,
entityKey.modelId);
}
return action;
}
@Override
public final PageAction removeFromView(final PageAction action) {
final EntityKey parentEntityKey = action.pageContext().getParentEntityKey();
final Set<EntityKey> selection = action.getMultiSelection();
if (selection != null && !selection.isEmpty()) {
selection.stream().forEach(entityKey -> {
callTemplateAction(
RemoveOrientation.class,
parentEntityKey.modelId,
entityKey.modelId);
});
} else {
final EntityKey entityKey = action.getEntityKey();
callTemplateAction(
RemoveOrientation.class,
parentEntityKey.modelId,
entityKey.modelId);
}
return action;
}
@Override
public final PageAction attachToDefaultView(final PageAction action) {
final EntityKey parentEntityKey = action.pageContext().getParentEntityKey();
final Set<EntityKey> selection = action.getMultiSelection();
if (selection != null && !selection.isEmpty()) {
selection.stream().forEach(entityKey -> {
callTemplateAction(
AttachDefaultOrientation.class,
parentEntityKey.modelId,
entityKey.modelId);
});
} else {
final EntityKey entityKey = action.getEntityKey();
callTemplateAction(
AttachDefaultOrientation.class,
parentEntityKey.modelId,
entityKey.modelId);
}
return action;
}
private void callTemplateAction(
final Class<? extends RestCall<TemplateAttribute>> actionType,
final String templateId,
final String attributeId) {
this.restService.getBuilder(actionType)
.withURIVariable(API.PARAM_PARENT_MODEL_ID, templateId)
.withURIVariable(API.PARAM_MODEL_ID, attributeId)
.call()
.getOrThrow();
}
private static final class ValueChangeListenerImpl implements ValueChangeListener {
public static final String VALIDATION_ERROR_KEY_PREFIX = "sebserver.examconfig.props.validation.";
private final PageContext pageContext;
private final RestService restService;
private final JSONMapper jsonMapper;
private final Collection<ValueChangeRule> valueChangeRules;
protected ValueChangeListenerImpl(
final PageContext pageContext,
final RestService restService,
final JSONMapper jsonMapper,
final Collection<ValueChangeRule> valueChangeRules) {
this.pageContext = pageContext;
this.restService = restService;
this.jsonMapper = jsonMapper;
this.valueChangeRules = valueChangeRules;
}
@Override
public void valueChanged(
final ViewContext context,
final ConfigurationAttribute attribute,
final String value,
final int listIndex) {
final ConfigurationValue configurationValue = new ConfigurationValue(
null,
context.getInstitutionId(),
context.getConfigurationId(),
attribute.id,
listIndex,
value);
try {
final String jsonValue = this.jsonMapper.writeValueAsString(configurationValue);
final Result<ConfigurationValue> savedValue = this.restService.getBuilder(SaveExamConfigValue.class)
.withBody(jsonValue)
.call();
if (savedValue.hasError()) {
context.showError(attribute.id, verifyErrorMessage(savedValue.getError()));
} else {
this.notifyGUI(context, attribute, savedValue.get());
}
} catch (final Exception e) {
this.pageContext.notifySaveError(EntityType.CONFIGURATION_VALUE, e);
}
}
@Override
public void tableChanged(final ConfigurationTableValues tableValue) {
this.restService.getBuilder(SaveExamConfigTableValues.class)
.withBody(tableValue)
.call();
}
private String verifyErrorMessage(final Exception error) {
if (error instanceof RestCallError) {
final List<APIMessage> errorMessages = ((RestCallError) error).getErrorMessages();
if (errorMessages.isEmpty()) {
return "";
}
final APIMessage apiMessage = errorMessages.get(0);
if (!ErrorMessage.FIELD_VALIDATION.isOf(apiMessage)) {
return "";
}
final FieldValidationError fieldValidationError = new FieldValidationError(apiMessage);
return this.pageContext.getI18nSupport().getText(new LocTextKey(
VALIDATION_ERROR_KEY_PREFIX + fieldValidationError.errorType,
(Object[]) fieldValidationError.getAttributes()));
}
log.warn("Unexpected error happened while trying to set SEB configuration value: ", error);
return VALIDATION_ERROR_KEY_PREFIX + "unexpected";
}
@Override
public void notifyGUI(
final ViewContext viewContext,
final ConfigurationAttribute attribute,
final ConfigurationValue value) {
this.valueChangeRules.stream()
.filter(rule -> rule.observesAttribute(attribute))
.forEach(rule -> rule.applyRule(viewContext, attribute, value));
}
}
}
/*
* 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.examconfig.impl;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.tomcat.util.buf.StringUtils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.APIMessage.ErrorMessage;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.TemplateAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.View;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder;
import ch.ethz.seb.sebserver.gui.service.examconfig.ValueChangeListener;
import ch.ethz.seb.sebserver.gui.service.examconfig.ValueChangeRule;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
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.impl.PageAction;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCallError;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.AttachDefaultOrientation;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetConfigAttributes;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetConfigurationValues;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetOrientations;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetViewList;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.RemoveOrientation;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.ResetTemplateValues;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfigTableValues;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfigValue;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@Lazy
@Service
@GuiProfile
public class ExamConfigurationServiceImpl implements ExamConfigurationService {
private static final Logger log = LoggerFactory.getLogger(ExamConfigurationServiceImpl.class);
private final RestService restService;
private final JSONMapper jsonMapper;
private final WidgetFactory widgetFactory;
private final InputFieldBuilderSupplier inputFieldBuilderSupplier;
private final Collection<ValueChangeRule> valueChangeRules;
public ExamConfigurationServiceImpl(
final RestService restService,
final JSONMapper jsonMapper,
final WidgetFactory widgetFactory,
final InputFieldBuilderSupplier inputFieldBuilderSupplier,
final Collection<ValueChangeRule> valueChangeRules) {
this.restService = restService;
this.jsonMapper = jsonMapper;
this.widgetFactory = widgetFactory;
this.inputFieldBuilderSupplier = inputFieldBuilderSupplier;
this.valueChangeRules = Utils.immutableCollectionOf(valueChangeRules);
}
@Override
public WidgetFactory getWidgetFactory() {
return this.widgetFactory;
}
@Override
public InputFieldBuilder getInputFieldBuilder(
final ConfigurationAttribute attribute,
final Orientation orientation) {
return this.inputFieldBuilderSupplier.getInputFieldBuilder(attribute, orientation);
}
@Override
public Result<AttributeMapping> getAttributes(final Long templateId) {
return Result.tryCatch(() -> new AttributeMapping(
templateId,
getAttributes(),
getOrientations(templateId)));
}
@Override
public Result<AttributeMapping> getAttributes(
final TemplateAttribute attribute,
final Orientation defaultOrientation) {
final List<Orientation> orientations = getOrientations(attribute.templateId);
if (attribute.getOrientation() == null) {
orientations.add(defaultOrientation);
}
return Result.tryCatch(() -> new AttributeMapping(
attribute.templateId,
getAttributes(),
orientations));
}
private List<Orientation> getOrientations(final Long templateId) {
return this.restService
.getBuilder(GetOrientations.class)
.withQueryParam(Orientation.FILTER_ATTR_TEMPLATE_ID, String.valueOf(templateId))
.call()
.onError(t -> log.error("Failed to get all Orientation of template {}", templateId, t))
.getOrThrow();
}
private List<ConfigurationAttribute> getAttributes() {
return this.restService
.getBuilder(GetConfigAttributes.class)
.call()
.onError(t -> log.error("Failed to get all ConfigurationAttribute"))
.getOrThrow();
}
@Override
public List<View> getViews(final AttributeMapping allAttributes) {
final Collection<Long> viewIds = allAttributes.getViewIds();
if (viewIds == null || viewIds.isEmpty()) {
return Collections.emptyList();
}
final String ids = StringUtils.join(
viewIds
.stream()
.map(String::valueOf)
.collect(Collectors.toList()),
Constants.LIST_SEPARATOR_CHAR);
return this.restService.getBuilder(GetViewList.class)
.withQueryParam(API.PARAM_MODEL_ID_LIST, ids)
.call()
.getOrThrow()
.stream()
.sorted(Comparator.comparing(v -> v.position))
.collect(Collectors.toList());
}
@Override
public ViewContext createViewContext(
final PageContext pageContext,
final Configuration configuration,
final View view,
final AttributeMapping attributeMapping,
final int rows,
final boolean readonly) {
return new ViewContext(
configuration,
view,
rows,
attributeMapping,
new ValueChangeListenerImpl(
pageContext,
this.restService,
this.jsonMapper,
this.valueChangeRules),
this.widgetFactory.getI18nSupport(),
readonly);
}
@Override
public Composite createViewGrid(final Composite parent, final ViewContext viewContext) {
final Composite composite = new Composite(parent, SWT.NONE);
final GridLayout gridLayout = new GridLayout(viewContext.getColumns(), true);
gridLayout.verticalSpacing = 0;
composite.setLayout(gridLayout);
final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false);
composite.setLayoutData(gridData);
final ViewGridBuilder viewGridBuilder = new ViewGridBuilder(
composite,
viewContext,
this);
for (final ConfigurationAttribute attribute : viewContext.getAttributes()) {
final Orientation orientation = viewContext.getOrientation(attribute.id);
if (orientation != null && viewContext.getId().equals(orientation.viewId)) {
viewGridBuilder.add(attribute);
}
}
viewGridBuilder.compose();
return composite;
}
@Override
public void initInputFieldValues(
final Long configurationId,
final Collection<ViewContext> viewContexts) {
if (viewContexts == null || viewContexts.size() < 1) {
log.warn("No viewContexts available");
return;
}
final Collection<ConfigurationValue> attributeValues = this.restService
.getBuilder(GetConfigurationValues.class)
.withQueryParam(
ConfigurationValue.FILTER_ATTR_CONFIGURATION_ID,
String.valueOf(configurationId))
.call()
.onError(t -> log.error(
"Failed to get all ConfigurationValue for configuration with id: {}",
configurationId))
.getOrElse(Collections::emptyList);
viewContexts
.forEach(vc -> vc.setValuesToInputFields(attributeValues));
}
@Override
public final PageAction resetToDefaults(final PageAction action) {
final EntityKey parentEntityKey = action.pageContext().getParentEntityKey();
final Set<EntityKey> selection = action.getMultiSelection();
if (selection != null && !selection.isEmpty()) {
selection.forEach(entityKey -> callTemplateAction(
ResetTemplateValues.class,
parentEntityKey.modelId,
entityKey.modelId));
} else {
final EntityKey entityKey = action.getEntityKey();
callTemplateAction(
ResetTemplateValues.class,
parentEntityKey.modelId,
entityKey.modelId);
}
return action;
}
@Override
public final PageAction removeFromView(final PageAction action) {
final EntityKey parentEntityKey = action.pageContext().getParentEntityKey();
final Set<EntityKey> selection = action.getMultiSelection();
if (selection != null && !selection.isEmpty()) {
selection.forEach(entityKey -> callTemplateAction(
RemoveOrientation.class,
parentEntityKey.modelId,
entityKey.modelId));
} else {
final EntityKey entityKey = action.getEntityKey();
callTemplateAction(
RemoveOrientation.class,
parentEntityKey.modelId,
entityKey.modelId);
}
return action;
}
@Override
public final PageAction attachToDefaultView(final PageAction action) {
final EntityKey parentEntityKey = action.pageContext().getParentEntityKey();
final Set<EntityKey> selection = action.getMultiSelection();
if (selection != null && !selection.isEmpty()) {
selection.forEach(entityKey -> callTemplateAction(
AttachDefaultOrientation.class,
parentEntityKey.modelId,
entityKey.modelId));
} else {
final EntityKey entityKey = action.getEntityKey();
callTemplateAction(
AttachDefaultOrientation.class,
parentEntityKey.modelId,
entityKey.modelId);
}
return action;
}
private void callTemplateAction(
final Class<? extends RestCall<TemplateAttribute>> actionType,
final String templateId,
final String attributeId) {
this.restService.getBuilder(actionType)
.withURIVariable(API.PARAM_PARENT_MODEL_ID, templateId)
.withURIVariable(API.PARAM_MODEL_ID, attributeId)
.call()
.getOrThrow();
}
private static final class ValueChangeListenerImpl implements ValueChangeListener {
public static final String VALIDATION_ERROR_KEY_PREFIX = "sebserver.examconfig.props.validation.";
private final PageContext pageContext;
private final RestService restService;
private final JSONMapper jsonMapper;
private final Collection<ValueChangeRule> valueChangeRules;
protected ValueChangeListenerImpl(
final PageContext pageContext,
final RestService restService,
final JSONMapper jsonMapper,
final Collection<ValueChangeRule> valueChangeRules) {
this.pageContext = pageContext;
this.restService = restService;
this.jsonMapper = jsonMapper;
this.valueChangeRules = valueChangeRules;
}
@Override
public void valueChanged(
final ViewContext context,
final ConfigurationAttribute attribute,
final String value,
final int listIndex) {
final ConfigurationValue configurationValue = new ConfigurationValue(
null,
context.getInstitutionId(),
context.getConfigurationId(),
attribute.id,
listIndex,
value);
try {
final String jsonValue = this.jsonMapper.writeValueAsString(configurationValue);
final Result<ConfigurationValue> savedValue = this.restService.getBuilder(SaveExamConfigValue.class)
.withBody(jsonValue)
.call();
if (savedValue.hasError()) {
context.showError(attribute.id, verifyErrorMessage(savedValue.getError()));
} else {
this.notifyGUI(context, attribute, savedValue.get());
}
} catch (final Exception e) {
this.pageContext.notifySaveError(EntityType.CONFIGURATION_VALUE, e);
}
}
@Override
public void tableChanged(final ConfigurationTableValues tableValue) {
this.restService.getBuilder(SaveExamConfigTableValues.class)
.withBody(tableValue)
.call();
}
private String verifyErrorMessage(final Exception error) {
if (error instanceof RestCallError) {
final List<APIMessage> errorMessages = ((RestCallError) error).getErrorMessages();
if (errorMessages.isEmpty()) {
return "";
}
final APIMessage apiMessage = errorMessages.get(0);
if (!ErrorMessage.FIELD_VALIDATION.isOf(apiMessage)) {
return "";
}
final FieldValidationError fieldValidationError = new FieldValidationError(apiMessage);
return this.pageContext.getI18nSupport().getText(new LocTextKey(
VALIDATION_ERROR_KEY_PREFIX + fieldValidationError.errorType,
(Object[]) fieldValidationError.getAttributes()));
}
log.warn("Unexpected error happened while trying to set SEB configuration value: ", error);
return VALIDATION_ERROR_KEY_PREFIX + "unexpected";
}
@Override
public void notifyGUI(
final ViewContext viewContext,
final ConfigurationAttribute attribute,
final ConfigurationValue value) {
this.valueChangeRules.stream()
.filter(rule -> rule.observesAttribute(attribute))
.forEach(rule -> rule.applyRule(viewContext, attribute, value));
}
}
}

View file

@ -1,132 +1,130 @@
/*
* 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.examconfig.impl;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.swt.widgets.Composite;
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.sebconfig.AttributeType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputField;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder;
import ch.ethz.seb.sebserver.gui.widget.GridTable;
import ch.ethz.seb.sebserver.gui.widget.GridTable.ColumnDef;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@Lazy
@Component
@GuiProfile
public class InlineTableFieldBuilder implements InputFieldBuilder {
private final WidgetFactory widgetFactory;
protected InlineTableFieldBuilder(final WidgetFactory widgetFactory) {
this.widgetFactory = widgetFactory;
}
@Override
public boolean builderFor(
final ConfigurationAttribute attribute,
final Orientation orientation) {
return attribute != null && attribute.type == AttributeType.INLINE_TABLE;
}
@Override
public InputField createInputField(
final Composite parent,
final ConfigurationAttribute attribute,
final ViewContext viewContext) {
final Orientation orientation = viewContext
.getOrientation(attribute.id);
final Composite innerGrid = InputFieldBuilder
.createInnerGrid(parent, attribute, orientation);
final Map<String, String> defaultValues = StringUtils.isBlank(attribute.defaultValue)
? Collections.emptyMap()
: Arrays.asList(StringUtils.split(
attribute.defaultValue,
Constants.EMBEDDED_LIST_SEPARATOR))
.stream()
.map(valueString -> StringUtils.split(
valueString,
Constants.FORM_URL_ENCODED_NAME_VALUE_SEPARATOR))
.collect(Collectors.toMap(
valueMap -> valueMap[0],
valueMap -> (valueMap.length > 1) ? valueMap[1] : StringUtils.EMPTY));
final List<ColumnDef> columns = Arrays.asList(StringUtils.split(
attribute.getResources(),
Constants.EMBEDDED_LIST_SEPARATOR))
.stream()
.map(columnString -> ColumnDef.fromString(columnString, defaultValues))
.collect(Collectors.toList());
final GridTable gridTable = new GridTable(
innerGrid,
columns,
ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + attribute.name + ".",
this.widgetFactory);
final InlineTableInputField inlineTableInputField = new InlineTableInputField(
attribute,
viewContext.getOrientation(attribute.id),
gridTable);
if (viewContext.readonly) {
gridTable.setEnabled(false);
gridTable.setListener(event -> {
});
} else {
gridTable.setListener(event -> viewContext.getValueChangeListener().valueChanged(
viewContext,
attribute,
inlineTableInputField.getValue(),
inlineTableInputField.listIndex));
}
return inlineTableInputField;
}
static final class InlineTableInputField extends AbstractInputField<GridTable> {
protected InlineTableInputField(
final ConfigurationAttribute attribute,
final Orientation orientation,
final GridTable control) {
super(attribute, orientation, control, null);
}
@Override
public String getValue() {
return this.control.getValue();
}
@Override
protected void setValueToControl(final String value) {
this.control.setValue(value);
}
}
}
/*
* 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.examconfig.impl;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.swt.widgets.Composite;
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.sebconfig.AttributeType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputField;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder;
import ch.ethz.seb.sebserver.gui.widget.GridTable;
import ch.ethz.seb.sebserver.gui.widget.GridTable.ColumnDef;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@Lazy
@Component
@GuiProfile
public class InlineTableFieldBuilder implements InputFieldBuilder {
private final WidgetFactory widgetFactory;
protected InlineTableFieldBuilder(final WidgetFactory widgetFactory) {
this.widgetFactory = widgetFactory;
}
@Override
public boolean builderFor(
final ConfigurationAttribute attribute,
final Orientation orientation) {
return attribute != null && attribute.type == AttributeType.INLINE_TABLE;
}
@Override
public InputField createInputField(
final Composite parent,
final ConfigurationAttribute attribute,
final ViewContext viewContext) {
final Orientation orientation = viewContext
.getOrientation(attribute.id);
final Composite innerGrid = InputFieldBuilder
.createInnerGrid(parent, attribute, orientation);
final Map<String, String> defaultValues = StringUtils.isBlank(attribute.defaultValue)
? Collections.emptyMap()
: Arrays.stream(StringUtils.split(
attribute.defaultValue,
Constants.EMBEDDED_LIST_SEPARATOR))
.map(valueString -> StringUtils.split(
valueString,
Constants.FORM_URL_ENCODED_NAME_VALUE_SEPARATOR))
.collect(Collectors.toMap(
valueMap -> valueMap[0],
valueMap -> (valueMap.length > 1) ? valueMap[1] : StringUtils.EMPTY));
final List<ColumnDef> columns = Arrays.stream(StringUtils.split(
attribute.getResources(),
Constants.EMBEDDED_LIST_SEPARATOR))
.map(columnString -> ColumnDef.fromString(columnString, defaultValues))
.collect(Collectors.toList());
final GridTable gridTable = new GridTable(
innerGrid,
columns,
ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + attribute.name + ".",
this.widgetFactory);
final InlineTableInputField inlineTableInputField = new InlineTableInputField(
attribute,
viewContext.getOrientation(attribute.id),
gridTable);
if (viewContext.readonly) {
gridTable.setEnabled(false);
gridTable.setListener(event -> {
});
} else {
gridTable.setListener(event -> viewContext.getValueChangeListener().valueChanged(
viewContext,
attribute,
inlineTableInputField.getValue(),
inlineTableInputField.listIndex));
}
return inlineTableInputField;
}
static final class InlineTableInputField extends AbstractInputField<GridTable> {
protected InlineTableInputField(
final ConfigurationAttribute attribute,
final Orientation orientation,
final GridTable control) {
super(attribute, orientation, control, null);
}
@Override
public String getValue() {
return this.control.getValue();
}
@Override
protected void setValueToControl(final String value) {
this.control.setValue(value);
}
}
}

View file

@ -1,47 +1,46 @@
/*
* 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.examconfig.impl;
import java.util.Collection;
import java.util.NoSuchElementException;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder;
@Lazy
@Service
@GuiProfile
public class InputFieldBuilderSupplier {
private final Collection<InputFieldBuilder> inputFieldBuilder;
protected InputFieldBuilderSupplier(final Collection<InputFieldBuilder> inputFieldBuilder) {
this.inputFieldBuilder = inputFieldBuilder;
inputFieldBuilder
.stream()
.forEach(builder -> builder.init(this));
}
public InputFieldBuilder getInputFieldBuilder(
final ConfigurationAttribute attribute,
final Orientation orientation) {
return this.inputFieldBuilder
.stream()
.filter(b -> b.builderFor(attribute, orientation))
.findFirst()
.orElseThrow(() -> new NoSuchElementException("No InputFieldBuilder found for : " + attribute.type));
}
}
/*
* 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.examconfig.impl;
import java.util.Collection;
import java.util.NoSuchElementException;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder;
@Lazy
@Service
@GuiProfile
public class InputFieldBuilderSupplier {
private final Collection<InputFieldBuilder> inputFieldBuilder;
protected InputFieldBuilderSupplier(final Collection<InputFieldBuilder> inputFieldBuilder) {
this.inputFieldBuilder = inputFieldBuilder;
inputFieldBuilder
.forEach(builder -> builder.init(this));
}
public InputFieldBuilder getInputFieldBuilder(
final ConfigurationAttribute attribute,
final Orientation orientation) {
return this.inputFieldBuilder
.stream()
.filter(b -> b.builderFor(attribute, orientation))
.findFirst()
.orElseThrow(() -> new NoSuchElementException("No InputFieldBuilder found for : " + attribute.type));
}
}

View file

@ -1,86 +1,86 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui.service.examconfig.impl;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputField;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@Lazy
@Component
@GuiProfile
public class LabelBuilder implements InputFieldBuilder {
private final WidgetFactory widgetFactory;
protected LabelBuilder(final WidgetFactory widgetFactory) {
this.widgetFactory = widgetFactory;
}
@Override
public boolean builderFor(
final ConfigurationAttribute attribute,
final Orientation orientation) {
if (attribute == null) {
return false;
}
return attribute.type == AttributeType.LABEL;
};
@Override
public InputField createInputField(
final Composite parent,
final ConfigurationAttribute attribute,
final ViewContext viewContext) {
final Label label = this.widgetFactory.labelLocalized(
parent,
ExamConfigurationService.attributeNameLocKey(attribute));
return new LabelField(
attribute,
viewContext.getOrientation(attribute.id),
label);
}
static final class LabelField extends AbstractInputField<Label> {
LabelField(
final ConfigurationAttribute attribute,
final Orientation orientation,
final Label control) {
super(attribute, orientation, control, null);
}
@Override
protected void setValueToControl(final String value) {
// Does Nothing, Label has no default value
}
@Override
public String getValue() {
return this.control.getText();
}
}
}
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui.service.examconfig.impl;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputField;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@Lazy
@Component
@GuiProfile
public class LabelBuilder implements InputFieldBuilder {
private final WidgetFactory widgetFactory;
protected LabelBuilder(final WidgetFactory widgetFactory) {
this.widgetFactory = widgetFactory;
}
@Override
public boolean builderFor(
final ConfigurationAttribute attribute,
final Orientation orientation) {
if (attribute == null) {
return false;
}
return attribute.type == AttributeType.LABEL;
}
@Override
public InputField createInputField(
final Composite parent,
final ConfigurationAttribute attribute,
final ViewContext viewContext) {
final Label label = this.widgetFactory.labelLocalized(
parent,
ExamConfigurationService.attributeNameLocKey(attribute));
return new LabelField(
attribute,
viewContext.getOrientation(attribute.id),
label);
}
static final class LabelField extends AbstractInputField<Label> {
LabelField(
final ConfigurationAttribute attribute,
final Orientation orientation,
final Label control) {
super(attribute, orientation, control, null);
}
@Override
protected void setValueToControl(final String value) {
// Does Nothing, Label has no default value
}
@Override
public String getValue() {
return this.control.getText();
}
}
}

View file

@ -104,7 +104,7 @@ public class PasswordFieldBuilder implements InputFieldBuilder {
final CharSequence pwd = passwordInput.getValue();
final CharSequence confirm = confirmInput.getValue();
if (passwordInputField.initValue != null && passwordInputField.initValue.equals(pwd)) {
if (passwordInputField.initValue != null && passwordInputField.initValue.contentEquals(pwd)) {
return;
}

View file

@ -1,75 +1,74 @@
/*
* 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.examconfig.impl;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.util.Tuple;
import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService;
public abstract class SelectionFieldBuilder {
protected List<Tuple<String>> getLocalizedResources(
final ConfigurationAttribute attribute,
final ViewContext viewContext) {
return getLocalizedRes(attribute, viewContext, false);
}
protected List<Tuple<String>> getLocalizedResourcesAsToolTip(
final ConfigurationAttribute attribute,
final ViewContext viewContext) {
return getLocalizedRes(attribute, viewContext, true);
}
private List<Tuple<String>> getLocalizedRes(
final ConfigurationAttribute attribute,
final ViewContext viewContext,
final boolean toolTipResources) {
if (attribute == null) {
return Collections.emptyList();
}
final String prefix =
(ConfigurationAttribute.hasDependency(
ConfigurationAttribute.DEPENDENCY_RESOURCE_LOC_TEXT_KEY,
attribute))
? ConfigurationAttribute.getDependencyValue(
ConfigurationAttribute.DEPENDENCY_RESOURCE_LOC_TEXT_KEY,
attribute) + "."
: ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + attribute.name + ".";
return Arrays.asList(StringUtils.split(
attribute.resources,
Constants.LIST_SEPARATOR))
.stream()
.map(value -> {
final String key = prefix + value + ((toolTipResources)
? ExamConfigurationService.TOOL_TIP_SUFFIX
: "");
final String text = viewContext.i18nSupport.getText(key, "");
return new Tuple<>(value, (StringUtils.isBlank(text))
? (toolTipResources)
? text
: value
: text);
})
.collect(Collectors.toList());
}
}
/*
* 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.examconfig.impl;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.util.Tuple;
import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService;
public abstract class SelectionFieldBuilder {
protected List<Tuple<String>> getLocalizedResources(
final ConfigurationAttribute attribute,
final ViewContext viewContext) {
return getLocalizedRes(attribute, viewContext, false);
}
protected List<Tuple<String>> getLocalizedResourcesAsToolTip(
final ConfigurationAttribute attribute,
final ViewContext viewContext) {
return getLocalizedRes(attribute, viewContext, true);
}
private List<Tuple<String>> getLocalizedRes(
final ConfigurationAttribute attribute,
final ViewContext viewContext,
final boolean toolTipResources) {
if (attribute == null) {
return Collections.emptyList();
}
final String prefix =
(ConfigurationAttribute.hasDependency(
ConfigurationAttribute.DEPENDENCY_RESOURCE_LOC_TEXT_KEY,
attribute))
? ConfigurationAttribute.getDependencyValue(
ConfigurationAttribute.DEPENDENCY_RESOURCE_LOC_TEXT_KEY,
attribute) + "."
: ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + attribute.name + ".";
return Arrays.stream(StringUtils.split(
attribute.resources,
Constants.LIST_SEPARATOR))
.map(value -> {
final String key = prefix + value + ((toolTipResources)
? ExamConfigurationService.TOOL_TIP_SUFFIX
: "");
final String text = viewContext.i18nSupport.getText(key, "");
return new Tuple<>(value, (StringUtils.isBlank(text))
? (toolTipResources)
? text
: value
: text);
})
.collect(Collectors.toList());
}
}

View file

@ -1,226 +1,224 @@
/*
* 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.examconfig.impl;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues.TableValue;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputField;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.impl.ModalInputDialog;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon;
@Lazy
@Component
@GuiProfile
public class TableFieldBuilder extends AbstractTableFieldBuilder {
private static final String TOOLTIP_SUFFIX = ".tooltip";
private static final String ADD_TOOLTIP_SUFFIX = ".add" + TOOLTIP_SUFFIX;
private static final String REMOVE_TOOLTIP_SUFFIX = ".remove" + TOOLTIP_SUFFIX;
protected TableFieldBuilder(
final RestService restService,
final WidgetFactory widgetFactory) {
super(restService, widgetFactory);
}
@Override
public boolean builderFor(
final ConfigurationAttribute attribute,
final Orientation orientation) {
if (attribute == null) {
return false;
}
return AttributeType.TABLE == attribute.type;
}
@Override
public InputField createInputField(
final Composite parent,
final ConfigurationAttribute attribute,
final ViewContext viewContext) {
final TableContext tableContext = createTableContext(attribute, viewContext);
final Table table = createTable(parent, tableContext);
for (final ConfigurationAttribute columnAttribute : tableContext.getColumnAttributes()) {
final TableColumn column = this.widgetFactory.tableColumnLocalized(
table,
new LocTextKey(ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX +
columnAttribute.name),
new LocTextKey(ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX +
columnAttribute.name +
TOOLTIP_SUFFIX));
column.setWidth(100);
column.setResizable(false);
column.setMoveable(false);
}
final TableInputField tableField = new TableInputField(
tableContext,
table);
if (!viewContext.readonly) {
TableColumn column = new TableColumn(table, SWT.NONE);
column.setImage(ImageIcon.ADD_BOX_WHITE.getImage(parent.getDisplay()));
column.setToolTipText(Utils.formatLineBreaks(viewContext.i18nSupport.getText(
ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX +
attribute.name +
ADD_TOOLTIP_SUFFIX,
"Add new")));
column.setWidth(20);
column.setResizable(false);
column.setMoveable(false);
column.addListener(SWT.Selection, event -> {
tableField.addRow();
});
column = new TableColumn(table, SWT.NONE);
column.setImage(ImageIcon.REMOVE_BOX_WHITE.getImage(parent.getDisplay()));
column.setToolTipText(Utils.formatLineBreaks(viewContext.i18nSupport.getText(
ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX +
attribute.name +
REMOVE_TOOLTIP_SUFFIX,
"Remove Selected")));
column.setWidth(20);
column.setResizable(false);
column.setMoveable(false);
column.addListener(SWT.Selection, event -> {
final int selectionIndex = table.getSelectionIndex();
if (selectionIndex >= 0) {
tableField.deleteRow(selectionIndex);
}
});
}
setSelectionListener(table, tableField);
return tableField;
}
static final class TableInputField extends AbstractTableInputField {
private final List<Map<Long, TableValue>> values;
TableInputField(
final TableContext tableContext,
final Table control) {
super(tableContext.attribute, tableContext.orientation, control, null, tableContext);
this.values = new ArrayList<>();
}
@Override
void initValue(final List<TableValue> tableValues) {
valuesFromIndexMap(this.values, createRowIndexMap(tableValues));
for (int i = 0; i < this.values.size(); i++) {
addTableRow(i, this.values.get(i));
}
}
private void deleteRow(final int selectionIndex) {
this.control.remove(selectionIndex);
this.values.remove(selectionIndex);
// send new values to web-service
this.tableContext.getValueChangeListener()
.tableChanged(extractTableValue(this.values));
}
private void addRow() {
final int index = this.values.size();
// create new values form default values
final Map<Long, TableValue> rowValues = this.tableContext.getRowAttributes()
.stream()
.map(attr -> new TableValue(attr.id, index, attr.defaultValue))
.collect(Collectors.toMap(
tv -> tv.attributeId,
Function.identity()));
this.values.add(rowValues);
addTableRow(this.values.size() - 1, rowValues);
this.control.layout();
// send new values to web-service
this.tableContext.getValueChangeListener()
.tableChanged(extractTableValue(this.values));
}
protected void addTableRow(final int index, final Map<Long, TableValue> rowValues) {
new TableItem(this.control, SWT.NONE);
applyTableRowValues(index);
}
@Override
protected void applyTableRowValues(final int index) {
final TableItem item = this.control.getItem(index);
final Map<Long, TableValue> rowValues = this.values.get(index);
int cellIndex = 0;
for (final ConfigurationAttribute attr : this.tableContext.getColumnAttributes()) {
if (rowValues.containsKey(attr.id)) {
final TableValue tableValue = rowValues.get(attr.id);
setValueToCell(this.tableContext, item, cellIndex, attr, tableValue);
}
cellIndex++;
}
}
@Override
protected void openForm(final int selectionIndex) {
final Map<Long, TableValue> rowValues = this.values.get(selectionIndex);
final TableRowFormBuilder builder = new TableRowFormBuilder(
this.tableContext,
rowValues,
selectionIndex);
new ModalInputDialog<Map<Long, TableValue>>(
this.control.getShell(),
this.tableContext.getWidgetFactory())
.setDialogWidth(600)
.setDialogHeight(550)
.open(
ExamConfigurationService.getTablePopupTitleKey(
this.attribute,
this.tableContext.getViewContext().i18nSupport),
(Consumer<Map<Long, TableValue>>) rowVals -> applyFormValues(
this.values,
rowVals,
selectionIndex),
() -> this.tableContext.getValueChangeListener()
.tableChanged(extractTableValue(this.values)),
builder);
}
}
}
/*
* 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.examconfig.impl;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues.TableValue;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputField;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.impl.ModalInputDialog;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon;
@Lazy
@Component
@GuiProfile
public class TableFieldBuilder extends AbstractTableFieldBuilder {
private static final String TOOLTIP_SUFFIX = ".tooltip";
private static final String ADD_TOOLTIP_SUFFIX = ".add" + TOOLTIP_SUFFIX;
private static final String REMOVE_TOOLTIP_SUFFIX = ".remove" + TOOLTIP_SUFFIX;
protected TableFieldBuilder(
final RestService restService,
final WidgetFactory widgetFactory) {
super(restService, widgetFactory);
}
@Override
public boolean builderFor(
final ConfigurationAttribute attribute,
final Orientation orientation) {
if (attribute == null) {
return false;
}
return AttributeType.TABLE == attribute.type;
}
@Override
public InputField createInputField(
final Composite parent,
final ConfigurationAttribute attribute,
final ViewContext viewContext) {
final TableContext tableContext = createTableContext(attribute, viewContext);
final Table table = createTable(parent, tableContext);
for (final ConfigurationAttribute columnAttribute : tableContext.getColumnAttributes()) {
final TableColumn column = this.widgetFactory.tableColumnLocalized(
table,
new LocTextKey(ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX +
columnAttribute.name),
new LocTextKey(ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX +
columnAttribute.name +
TOOLTIP_SUFFIX));
column.setWidth(100);
column.setResizable(false);
column.setMoveable(false);
}
final TableInputField tableField = new TableInputField(
tableContext,
table);
if (!viewContext.readonly) {
TableColumn column = new TableColumn(table, SWT.NONE);
column.setImage(ImageIcon.ADD_BOX_WHITE.getImage(parent.getDisplay()));
column.setToolTipText(Utils.formatLineBreaks(viewContext.i18nSupport.getText(
ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX +
attribute.name +
ADD_TOOLTIP_SUFFIX,
"Add new")));
column.setWidth(20);
column.setResizable(false);
column.setMoveable(false);
column.addListener(SWT.Selection, event -> tableField.addRow());
column = new TableColumn(table, SWT.NONE);
column.setImage(ImageIcon.REMOVE_BOX_WHITE.getImage(parent.getDisplay()));
column.setToolTipText(Utils.formatLineBreaks(viewContext.i18nSupport.getText(
ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX +
attribute.name +
REMOVE_TOOLTIP_SUFFIX,
"Remove Selected")));
column.setWidth(20);
column.setResizable(false);
column.setMoveable(false);
column.addListener(SWT.Selection, event -> {
final int selectionIndex = table.getSelectionIndex();
if (selectionIndex >= 0) {
tableField.deleteRow(selectionIndex);
}
});
}
setSelectionListener(table, tableField);
return tableField;
}
static final class TableInputField extends AbstractTableInputField {
private final List<Map<Long, TableValue>> values;
TableInputField(
final TableContext tableContext,
final Table control) {
super(tableContext.attribute, tableContext.orientation, control, null, tableContext);
this.values = new ArrayList<>();
}
@Override
void initValue(final List<TableValue> tableValues) {
valuesFromIndexMap(this.values, createRowIndexMap(tableValues));
for (int i = 0; i < this.values.size(); i++) {
addTableRow(i, this.values.get(i));
}
}
private void deleteRow(final int selectionIndex) {
this.control.remove(selectionIndex);
this.values.remove(selectionIndex);
// send new values to web-service
this.tableContext.getValueChangeListener()
.tableChanged(extractTableValue(this.values));
}
private void addRow() {
final int index = this.values.size();
// create new values form default values
final Map<Long, TableValue> rowValues = this.tableContext.getRowAttributes()
.stream()
.map(attr -> new TableValue(attr.id, index, attr.defaultValue))
.collect(Collectors.toMap(
tv -> tv.attributeId,
Function.identity()));
this.values.add(rowValues);
addTableRow(this.values.size() - 1, rowValues);
this.control.layout();
// send new values to web-service
this.tableContext.getValueChangeListener()
.tableChanged(extractTableValue(this.values));
}
protected void addTableRow(final int index, final Map<Long, TableValue> rowValues) {
new TableItem(this.control, SWT.NONE);
applyTableRowValues(index);
}
@Override
protected void applyTableRowValues(final int index) {
final TableItem item = this.control.getItem(index);
final Map<Long, TableValue> rowValues = this.values.get(index);
int cellIndex = 0;
for (final ConfigurationAttribute attr : this.tableContext.getColumnAttributes()) {
if (rowValues.containsKey(attr.id)) {
final TableValue tableValue = rowValues.get(attr.id);
setValueToCell(this.tableContext, item, cellIndex, attr, tableValue);
}
cellIndex++;
}
}
@Override
protected void openForm(final int selectionIndex) {
final Map<Long, TableValue> rowValues = this.values.get(selectionIndex);
final TableRowFormBuilder builder = new TableRowFormBuilder(
this.tableContext,
rowValues,
selectionIndex);
new ModalInputDialog<Map<Long, TableValue>>(
this.control.getShell(),
this.tableContext.getWidgetFactory())
.setDialogWidth(600)
.setDialogHeight(550)
.open(
ExamConfigurationService.getTablePopupTitleKey(
this.attribute,
this.tableContext.getViewContext().i18nSupport),
(Consumer<Map<Long, TableValue>>) _rowValues -> applyFormValues(
this.values,
_rowValues,
selectionIndex),
() -> this.tableContext.getValueChangeListener()
.tableChanged(extractTableValue(this.values)),
builder);
}
}
}

View file

@ -1,162 +1,160 @@
/*
* 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.examconfig.impl;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues.TableValue;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputField;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.ModalInputDialogComposer;
import ch.ethz.seb.sebserver.gui.service.page.PageService;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
public class TableRowFormBuilder implements ModalInputDialogComposer<Map<Long, TableValue>> {
private final TableContext tableContext;
private final Map<Long, TableValue> rowValues;
private final int listIndex;
private final String rowGroupId;
public TableRowFormBuilder(
final TableContext tableContext,
final Map<Long, TableValue> rowValues,
final int listIndex) {
this.tableContext = tableContext;
this.rowValues = rowValues;
this.listIndex = listIndex;
this.rowGroupId = null;
}
public TableRowFormBuilder(
final TableContext tableContext,
final Map<Long, TableValue> rowValues,
final String rowGroupId) {
this.tableContext = tableContext;
this.rowValues = rowValues;
this.listIndex = 0;
this.rowGroupId = rowGroupId;
}
@Override
public Supplier<Map<Long, TableValue>> compose(final Composite parent) {
final Composite grid = PageService.createManagedVScrolledComposite(
parent,
scrolledComposite -> {
final Composite result = this.tableContext
.getWidgetFactory()
.formGrid(scrolledComposite, 2);
final GridLayout layout = (GridLayout) result.getLayout();
layout.verticalSpacing = 0;
return result;
},
false);
final List<InputField> inputFields = new ArrayList<>();
for (final ConfigurationAttribute attribute : this.tableContext.getRowAttributes(this.rowGroupId)) {
createLabel(grid, attribute);
inputFields.add(createInputField(grid, attribute));
}
for (final InputField inputField : inputFields) {
final ConfigurationAttribute attribute = inputField.getAttribute();
this.tableContext.getValueChangeListener().notifyGUI(
this.tableContext.getViewContext(),
attribute,
new ConfigurationValue(
null,
null,
null,
attribute.id,
this.listIndex,
inputField.getValue()));
}
// when the pop-up gets closed we have to remove the input fields from the view context
grid.addDisposeListener(event -> {
this.tableContext.flushInputFields(this.rowValues.keySet());
});
return () -> inputFields.stream()
.map(field -> (field.hasError())
? this.rowValues.get(field.getAttribute().id)
: new TableValue(
field.getAttribute().id,
this.listIndex,
field.getValue()))
.collect(Collectors.toMap(
tv -> tv.attributeId,
Function.identity()));
}
private InputField createInputField(
final Composite parent,
final ConfigurationAttribute attribute) {
if (attribute.type == AttributeType.TABLE) {
throw new UnsupportedOperationException(
"Table type is currently not supported within a table row form view!");
}
final Orientation orientation = this.tableContext
.getOrientation(attribute.id);
final InputFieldBuilder inputFieldBuilder = this.tableContext
.getInputFieldBuilder(attribute, orientation);
final InputField inputField = inputFieldBuilder.createInputField(
parent,
attribute,
this.tableContext.getViewContext());
final TableValue initValue = this.rowValues.get(attribute.id);
inputField.initValue((initValue != null) ? initValue.value : null, this.listIndex);
// we have to register the input field within the ViewContext to receive error messages
this.tableContext.registerInputField(inputField);
return inputField;
}
private void createLabel(
final Composite parent,
final ConfigurationAttribute attribute) {
final LocTextKey locTextKey = new LocTextKey(
ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX +
attribute.name,
attribute.name);
final Label label = this.tableContext
.getWidgetFactory()
.labelLocalized(parent, locTextKey);
final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false);
gridData.verticalIndent = 4;
label.setLayoutData(gridData);
label.setData(RWT.CUSTOM_VARIANT, CustomVariant.TITLE_LABEL.key);
}
}
/*
* 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.examconfig.impl;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues.TableValue;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputField;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.ModalInputDialogComposer;
import ch.ethz.seb.sebserver.gui.service.page.PageService;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
public class TableRowFormBuilder implements ModalInputDialogComposer<Map<Long, TableValue>> {
private final TableContext tableContext;
private final Map<Long, TableValue> rowValues;
private final int listIndex;
private final String rowGroupId;
public TableRowFormBuilder(
final TableContext tableContext,
final Map<Long, TableValue> rowValues,
final int listIndex) {
this.tableContext = tableContext;
this.rowValues = rowValues;
this.listIndex = listIndex;
this.rowGroupId = null;
}
public TableRowFormBuilder(
final TableContext tableContext,
final Map<Long, TableValue> rowValues,
final String rowGroupId) {
this.tableContext = tableContext;
this.rowValues = rowValues;
this.listIndex = 0;
this.rowGroupId = rowGroupId;
}
@Override
public Supplier<Map<Long, TableValue>> compose(final Composite parent) {
final Composite grid = PageService.createManagedVScrolledComposite(
parent,
scrolledComposite -> {
final Composite result = this.tableContext
.getWidgetFactory()
.formGrid(scrolledComposite, 2);
final GridLayout layout = (GridLayout) result.getLayout();
layout.verticalSpacing = 0;
return result;
},
false);
final List<InputField> inputFields = new ArrayList<>();
for (final ConfigurationAttribute attribute : this.tableContext.getRowAttributes(this.rowGroupId)) {
createLabel(grid, attribute);
inputFields.add(createInputField(grid, attribute));
}
for (final InputField inputField : inputFields) {
final ConfigurationAttribute attribute = inputField.getAttribute();
this.tableContext.getValueChangeListener().notifyGUI(
this.tableContext.getViewContext(),
attribute,
new ConfigurationValue(
null,
null,
null,
attribute.id,
this.listIndex,
inputField.getValue()));
}
// when the pop-up gets closed we have to remove the input fields from the view context
grid.addDisposeListener(event -> this.tableContext.flushInputFields(this.rowValues.keySet()));
return () -> inputFields.stream()
.map(field -> (field.hasError())
? this.rowValues.get(field.getAttribute().id)
: new TableValue(
field.getAttribute().id,
this.listIndex,
field.getValue()))
.collect(Collectors.toMap(
tv -> tv.attributeId,
Function.identity()));
}
private InputField createInputField(
final Composite parent,
final ConfigurationAttribute attribute) {
if (attribute.type == AttributeType.TABLE) {
throw new UnsupportedOperationException(
"Table type is currently not supported within a table row form view!");
}
final Orientation orientation = this.tableContext
.getOrientation(attribute.id);
final InputFieldBuilder inputFieldBuilder = this.tableContext
.getInputFieldBuilder(attribute, orientation);
final InputField inputField = inputFieldBuilder.createInputField(
parent,
attribute,
this.tableContext.getViewContext());
final TableValue initValue = this.rowValues.get(attribute.id);
inputField.initValue((initValue != null) ? initValue.value : null, this.listIndex);
// we have to register the input field within the ViewContext to receive error messages
this.tableContext.registerInputField(inputField);
return inputField;
}
private void createLabel(
final Composite parent,
final ConfigurationAttribute attribute) {
final LocTextKey locTextKey = new LocTextKey(
ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX +
attribute.name,
attribute.name);
final Label label = this.tableContext
.getWidgetFactory()
.labelLocalized(parent, locTextKey);
final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false);
gridData.verticalIndent = 4;
label.setLayoutData(gridData);
label.setData(RWT.CUSTOM_VARIANT, CustomVariant.TITLE_LABEL.key);
}
}

View file

@ -1,250 +1,248 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui.service.examconfig.impl;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.View;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputField;
import ch.ethz.seb.sebserver.gui.service.examconfig.ValueChangeListener;
import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
public final class ViewContext {
private static final Logger log = LoggerFactory.getLogger(ViewContext.class);
private final Configuration configuration;
private final View view;
private final int rows;
final AttributeMapping attributeMapping;
final Map<Long, InputField> inputFieldMapping;
final ValueChangeListener valueChangeListener;
final I18nSupport i18nSupport;
final boolean readonly;
ViewContext(
final Configuration configuration,
final View view,
final int rows,
final AttributeMapping attributeContext,
final ValueChangeListener valueChangeListener,
final I18nSupport i18nSupport,
final boolean readonly) {
Objects.requireNonNull(configuration);
Objects.requireNonNull(view);
Objects.requireNonNull(attributeContext);
Objects.requireNonNull(valueChangeListener);
this.configuration = configuration;
this.view = view;
this.rows = rows;
this.attributeMapping = attributeContext;
this.inputFieldMapping = new HashMap<>();
this.valueChangeListener = valueChangeListener;
this.i18nSupport = i18nSupport;
this.readonly = readonly;
}
public I18nSupport getI18nSupport() {
return this.i18nSupport;
}
public Long getId() {
return this.view.id;
}
public String getName() {
return this.view.name;
}
public Long getConfigurationId() {
return this.configuration.id;
}
public Long getInstitutionId() {
return this.configuration.institutionId;
}
public int getColumns() {
return this.view.columns;
}
public int getRows() {
return this.rows;
}
public Configuration getConfiguration() {
return this.configuration;
}
public View getView() {
return this.view;
}
public Collection<ConfigurationAttribute> getChildAttributes(final Long id) {
return this.attributeMapping.childAttributeMapping.get(id);
}
public Collection<ConfigurationAttribute> getAttributes() {
return this.attributeMapping.getAttributes();
}
public ConfigurationAttribute getAttribute(final Long attributeId) {
return this.attributeMapping.getAttribute(attributeId);
}
public Long getAttributeIdByName(final String name) {
return this.attributeMapping.attributeNameIdMapping.get(name);
}
public ConfigurationAttribute getAttributeByName(final String name) {
final Long attributeId = this.attributeMapping.attributeNameIdMapping.get(name);
if (attributeId != null) {
return getAttribute(attributeId);
}
return null;
}
public Collection<Orientation> getOrientationsOfGroup(final ConfigurationAttribute attribute) {
return this.attributeMapping.getOrientationsOfGroup(attribute);
}
public Orientation getOrientation(final Long attributeId) {
return this.attributeMapping.getOrientation(attributeId);
}
public ValueChangeListener getValueChangeListener() {
return this.valueChangeListener;
}
public void disable(final String attributeName) {
disable(this.getAttributeIdByName(attributeName));
}
public void disable(final Long attributeId) {
final InputField inputField = this.inputFieldMapping.get(attributeId);
if (inputField == null) {
return;
}
inputField.disable(false);
}
public void enable(final String attributeName) {
enable(this.getAttributeIdByName(attributeName));
}
public void enable(final Long attributeId) {
final InputField inputField = this.inputFieldMapping.get(attributeId);
if (inputField == null) {
return;
}
inputField.enable(false);
}
public void disableGroup(final String attributeName) {
disableGroup(this.getAttributeIdByName(attributeName));
}
public void disableGroup(final Long attributeId) {
final InputField inputField = this.inputFieldMapping.get(attributeId);
if (inputField == null) {
return;
}
inputField.disable(true);
try {
this.attributeMapping.attributeGroupMapping
.get(inputField.getOrientation().groupId)
.stream()
.map(ConfigurationAttribute::getId)
.map(this.inputFieldMapping::get)
.forEach(InputField::setDefaultValue);
} catch (final Exception e) {
log.warn("Failed to send attribute value update to server: ", e);
}
}
public void enableGroup(final String attributeName) {
enableGroup(this.getAttributeIdByName(attributeName));
}
public void enableGroup(final Long attributeId) {
final InputField inputField = this.inputFieldMapping.get(attributeId);
if (inputField == null) {
return;
}
inputField.enable(true);
}
public void showError(final Long attributeId, final String errorMessage) {
final InputField inputField = this.inputFieldMapping.get(attributeId);
if (inputField == null) {
return;
}
inputField.showError(errorMessage);
}
public void clearError(final Long attributeId) {
final InputField inputField = this.inputFieldMapping.get(attributeId);
if (inputField == null) {
return;
}
inputField.clearError();
}
public void registerInputField(final InputField inputField) {
this.inputFieldMapping.put(
inputField.getAttribute().id,
inputField);
}
void setValuesToInputFields(final Collection<ConfigurationValue> values) {
this.inputFieldMapping
.values()
.stream()
.forEach(field -> {
final ConfigurationValue initValue = field.initValue(values);
if (initValue != null) {
this.valueChangeListener.notifyGUI(this, field.getAttribute(), initValue);
}
});
}
/** Removes all registered InputFields with the given attribute ids
*
* @param values Collection of attribute ids */
void flushInputFields(final Collection<Long> values) {
if (values == null) {
return;
}
values.stream()
.forEach(attrId -> this.inputFieldMapping.remove(attrId));
}
}
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui.service.examconfig.impl;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.View;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputField;
import ch.ethz.seb.sebserver.gui.service.examconfig.ValueChangeListener;
import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
public final class ViewContext {
private static final Logger log = LoggerFactory.getLogger(ViewContext.class);
private final Configuration configuration;
private final View view;
private final int rows;
final AttributeMapping attributeMapping;
final Map<Long, InputField> inputFieldMapping;
final ValueChangeListener valueChangeListener;
final I18nSupport i18nSupport;
final boolean readonly;
ViewContext(
final Configuration configuration,
final View view,
final int rows,
final AttributeMapping attributeContext,
final ValueChangeListener valueChangeListener,
final I18nSupport i18nSupport,
final boolean readonly) {
Objects.requireNonNull(configuration);
Objects.requireNonNull(view);
Objects.requireNonNull(attributeContext);
Objects.requireNonNull(valueChangeListener);
this.configuration = configuration;
this.view = view;
this.rows = rows;
this.attributeMapping = attributeContext;
this.inputFieldMapping = new HashMap<>();
this.valueChangeListener = valueChangeListener;
this.i18nSupport = i18nSupport;
this.readonly = readonly;
}
public I18nSupport getI18nSupport() {
return this.i18nSupport;
}
public Long getId() {
return this.view.id;
}
public String getName() {
return this.view.name;
}
public Long getConfigurationId() {
return this.configuration.id;
}
public Long getInstitutionId() {
return this.configuration.institutionId;
}
public int getColumns() {
return this.view.columns;
}
public int getRows() {
return this.rows;
}
public Configuration getConfiguration() {
return this.configuration;
}
public View getView() {
return this.view;
}
public Collection<ConfigurationAttribute> getChildAttributes(final Long id) {
return this.attributeMapping.childAttributeMapping.get(id);
}
public Collection<ConfigurationAttribute> getAttributes() {
return this.attributeMapping.getAttributes();
}
public ConfigurationAttribute getAttribute(final Long attributeId) {
return this.attributeMapping.getAttribute(attributeId);
}
public Long getAttributeIdByName(final String name) {
return this.attributeMapping.attributeNameIdMapping.get(name);
}
public ConfigurationAttribute getAttributeByName(final String name) {
final Long attributeId = this.attributeMapping.attributeNameIdMapping.get(name);
if (attributeId != null) {
return getAttribute(attributeId);
}
return null;
}
public Collection<Orientation> getOrientationsOfGroup(final ConfigurationAttribute attribute) {
return this.attributeMapping.getOrientationsOfGroup(attribute);
}
public Orientation getOrientation(final Long attributeId) {
return this.attributeMapping.getOrientation(attributeId);
}
public ValueChangeListener getValueChangeListener() {
return this.valueChangeListener;
}
public void disable(final String attributeName) {
disable(this.getAttributeIdByName(attributeName));
}
public void disable(final Long attributeId) {
final InputField inputField = this.inputFieldMapping.get(attributeId);
if (inputField == null) {
return;
}
inputField.disable(false);
}
public void enable(final String attributeName) {
enable(this.getAttributeIdByName(attributeName));
}
public void enable(final Long attributeId) {
final InputField inputField = this.inputFieldMapping.get(attributeId);
if (inputField == null) {
return;
}
inputField.enable(false);
}
public void disableGroup(final String attributeName) {
disableGroup(this.getAttributeIdByName(attributeName));
}
public void disableGroup(final Long attributeId) {
final InputField inputField = this.inputFieldMapping.get(attributeId);
if (inputField == null) {
return;
}
inputField.disable(true);
try {
this.attributeMapping.attributeGroupMapping
.get(inputField.getOrientation().groupId)
.stream()
.map(ConfigurationAttribute::getId)
.map(this.inputFieldMapping::get)
.forEach(InputField::setDefaultValue);
} catch (final Exception e) {
log.warn("Failed to send attribute value update to server: ", e);
}
}
public void enableGroup(final String attributeName) {
enableGroup(this.getAttributeIdByName(attributeName));
}
public void enableGroup(final Long attributeId) {
final InputField inputField = this.inputFieldMapping.get(attributeId);
if (inputField == null) {
return;
}
inputField.enable(true);
}
public void showError(final Long attributeId, final String errorMessage) {
final InputField inputField = this.inputFieldMapping.get(attributeId);
if (inputField == null) {
return;
}
inputField.showError(errorMessage);
}
public void clearError(final Long attributeId) {
final InputField inputField = this.inputFieldMapping.get(attributeId);
if (inputField == null) {
return;
}
inputField.clearError();
}
public void registerInputField(final InputField inputField) {
this.inputFieldMapping.put(
inputField.getAttribute().id,
inputField);
}
void setValuesToInputFields(final Collection<ConfigurationValue> values) {
this.inputFieldMapping
.values()
.forEach(field -> {
final ConfigurationValue initValue = field.initValue(values);
if (initValue != null) {
this.valueChangeListener.notifyGUI(this, field.getAttribute(), initValue);
}
});
}
/** Removes all registered InputFields with the given attribute ids
*
* @param values Collection of attribute ids */
void flushInputFields(final Collection<Long> values) {
if (values == null) {
return;
}
values.forEach(this.inputFieldMapping::remove);
}
}

View file

@ -1,213 +1,213 @@
/*
* 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.examconfig.impl;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder;
import ch.ethz.seb.sebserver.gui.service.examconfig.impl.CellFieldBuilderAdapter.GroupCellFieldBuilderAdapter;
public class ViewGridBuilder {
private static final Logger log = LoggerFactory.getLogger(ViewGridBuilder.class);
final ExamConfigurationService examConfigurationService;
final Composite parent;
final ViewContext viewContext;
private final CellFieldBuilderAdapter[][] grid;
private final GroupCellFieldBuilderAdapter groupBuilderAdapter;
private final Set<String> registeredGroups;
ViewGridBuilder(
final Composite parent,
final ViewContext viewContext,
final ExamConfigurationService examConfigurationService) {
this.examConfigurationService = examConfigurationService;
this.parent = parent;
this.viewContext = viewContext;
this.grid = new CellFieldBuilderAdapter[viewContext.getRows()][viewContext.getColumns()];
this.groupBuilderAdapter = null;
this.registeredGroups = new HashSet<>();
}
ViewGridBuilder(
final Composite parent,
final ViewContext viewContext,
final GroupCellFieldBuilderAdapter groupBuilderAdapter,
final ExamConfigurationService examConfigurationService) {
this.examConfigurationService = examConfigurationService;
this.parent = parent;
this.viewContext = viewContext;
this.groupBuilderAdapter = groupBuilderAdapter;
this.grid = new CellFieldBuilderAdapter[groupBuilderAdapter.height - 1][groupBuilderAdapter.width];
this.registeredGroups = null;
}
ViewGridBuilder add(final ConfigurationAttribute attribute) {
if (log.isDebugEnabled()) {
log.debug("Add SEB Configuration Attribute: " + attribute);
}
// ignore nested attributes here (if not propagated to show up in view)
if (attribute.parentId != null &&
!BooleanUtils.toBoolean(ConfigurationAttribute.getDependencyValue(
ConfigurationAttribute.DEPENDENCY_SHOW_IN_VIEW,
attribute))) {
return this;
}
final Orientation orientation = this.viewContext
.getOrientation(attribute.id);
// create group if this is not a group builder
if (this.groupBuilderAdapter == null && StringUtils.isNotBlank(orientation.groupId)) {
if (this.registeredGroups.contains(orientation.groupId)) {
return this;
}
final GroupCellFieldBuilderAdapter groupBuilder =
new GroupCellFieldBuilderAdapter(this.viewContext.getOrientationsOfGroup(attribute));
fillDummy(groupBuilder.x, groupBuilder.y, groupBuilder.width, groupBuilder.height);
this.grid[groupBuilder.y][groupBuilder.x] = groupBuilder;
this.registeredGroups.add(orientation.groupId);
return this;
}
// create single input field with label
final int xpos = orientation.xpos() + ((this.groupBuilderAdapter != null) ? -this.groupBuilderAdapter.x : 0);
final int ypos = orientation.ypos() + ((this.groupBuilderAdapter != null) ? -this.groupBuilderAdapter.y : 0);
if (orientation.width > 1 || orientation.height > 1) {
fillDummy(xpos, ypos, orientation.width, orientation.height);
}
final InputFieldBuilder inputFieldBuilder = this.examConfigurationService.getInputFieldBuilder(
attribute,
orientation);
this.grid[ypos][xpos] = CellFieldBuilderAdapter.fieldBuilderAdapter(
inputFieldBuilder,
attribute);
try {
switch (orientation.title) {
case RIGHT:
case RIGHT_SPAN: {
this.grid[ypos][xpos + 1] = CellFieldBuilderAdapter.labelBuilder(
attribute,
orientation);
break;
}
case LEFT:
case LEFT_SPAN: {
this.grid[ypos][xpos - 1] = CellFieldBuilderAdapter.labelBuilder(
attribute,
orientation);
// special case for password, also add confirm label
if (attribute.type == AttributeType.PASSWORD_FIELD) {
this.grid[ypos + 1][xpos - 1] = CellFieldBuilderAdapter.passwordConfirmLabel(
attribute,
orientation);
}
break;
}
case TOP: {
fillDummy(xpos, ypos - 1, orientation.width, 1);
this.grid[ypos - 1][xpos] = CellFieldBuilderAdapter.labelBuilder(
attribute,
orientation);
break;
}
default: {
// do nothing
break;
}
}
} catch (final ArrayIndexOutOfBoundsException e) {
log.error("Failed to set title as configured in: {} for attribute: {}", orientation, attribute, e);
}
return this;
}
void compose() {
if (log.isTraceEnabled()) {
log.trace("Compose grid view: \n" + gridToString());
}
// balance grid (optimize span and grab empty spaces for labels where applicable)
for (int y = 0; y < this.grid.length; y++) {
for (int x = 0; x < this.grid[y].length; x++) {
if (this.grid[y][x] != null) {
this.grid[y][x].balanceGrid(this.grid, x, y);
}
}
}
for (int y = 0; y < this.grid.length; y++) {
for (int x = 0; x < this.grid[y].length; x++) {
if (this.grid[y][x] == null) {
final Label empty = new Label(this.parent, SWT.LEFT);
final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false);
gridData.verticalIndent = 8;
empty.setLayoutData(gridData);
empty.setText(StringUtils.EMPTY /* "empty " + x + " " + y */);
} else {
this.grid[y][x].createCell(this);
}
}
}
}
private void fillDummy(final int x, final int y, final int width, final int height) {
final int upperBoundX = x + width;
final int upperBoundY = y + height;
for (int _y = y; _y < upperBoundY; _y++) {
for (int _x = x; _x < upperBoundX; _x++) {
if (_y < 0 || _x < 0 || _y >= this.grid.length || _x >= this.grid[_y].length) {
log.warn("Out of bounds: {} {}", _x, _y);
continue;
}
this.grid[_y][_x] = CellFieldBuilderAdapter.DUMMY_BUILDER_ADAPTER;
}
}
}
private String gridToString() {
final StringBuffer sb = new StringBuffer();
for (int i = 0; i < this.grid.length; i++) {
if (sb.length() > 0) {
sb.append(",\n");
}
sb.append(Arrays.toString(this.grid[i]));
}
return sb.toString();
}
}
/*
* 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.examconfig.impl;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder;
import ch.ethz.seb.sebserver.gui.service.examconfig.impl.CellFieldBuilderAdapter.GroupCellFieldBuilderAdapter;
public class ViewGridBuilder {
private static final Logger log = LoggerFactory.getLogger(ViewGridBuilder.class);
final ExamConfigurationService examConfigurationService;
final Composite parent;
final ViewContext viewContext;
private final CellFieldBuilderAdapter[][] grid;
private final GroupCellFieldBuilderAdapter groupBuilderAdapter;
private final Set<String> registeredGroups;
ViewGridBuilder(
final Composite parent,
final ViewContext viewContext,
final ExamConfigurationService examConfigurationService) {
this.examConfigurationService = examConfigurationService;
this.parent = parent;
this.viewContext = viewContext;
this.grid = new CellFieldBuilderAdapter[viewContext.getRows()][viewContext.getColumns()];
this.groupBuilderAdapter = null;
this.registeredGroups = new HashSet<>();
}
ViewGridBuilder(
final Composite parent,
final ViewContext viewContext,
final GroupCellFieldBuilderAdapter groupBuilderAdapter,
final ExamConfigurationService examConfigurationService) {
this.examConfigurationService = examConfigurationService;
this.parent = parent;
this.viewContext = viewContext;
this.groupBuilderAdapter = groupBuilderAdapter;
this.grid = new CellFieldBuilderAdapter[groupBuilderAdapter.height - 1][groupBuilderAdapter.width];
this.registeredGroups = null;
}
ViewGridBuilder add(final ConfigurationAttribute attribute) {
if (log.isDebugEnabled()) {
log.debug("Add SEB Configuration Attribute: " + attribute);
}
// ignore nested attributes here (if not propagated to show up in view)
if (attribute.parentId != null &&
!BooleanUtils.toBoolean(ConfigurationAttribute.getDependencyValue(
ConfigurationAttribute.DEPENDENCY_SHOW_IN_VIEW,
attribute))) {
return this;
}
final Orientation orientation = this.viewContext
.getOrientation(attribute.id);
// create group if this is not a group builder
if (this.groupBuilderAdapter == null && StringUtils.isNotBlank(orientation.groupId)) {
if (this.registeredGroups.contains(orientation.groupId)) {
return this;
}
final GroupCellFieldBuilderAdapter groupBuilder =
new GroupCellFieldBuilderAdapter(this.viewContext.getOrientationsOfGroup(attribute));
fillDummy(groupBuilder.x, groupBuilder.y, groupBuilder.width, groupBuilder.height);
this.grid[groupBuilder.y][groupBuilder.x] = groupBuilder;
this.registeredGroups.add(orientation.groupId);
return this;
}
// create single input field with label
final int xpos = orientation.xpos() + ((this.groupBuilderAdapter != null) ? -this.groupBuilderAdapter.x : 0);
final int ypos = orientation.ypos() + ((this.groupBuilderAdapter != null) ? -this.groupBuilderAdapter.y : 0);
if (orientation.width > 1 || orientation.height > 1) {
fillDummy(xpos, ypos, orientation.width, orientation.height);
}
final InputFieldBuilder inputFieldBuilder = this.examConfigurationService.getInputFieldBuilder(
attribute,
orientation);
this.grid[ypos][xpos] = CellFieldBuilderAdapter.fieldBuilderAdapter(
inputFieldBuilder,
attribute);
try {
switch (orientation.title) {
case RIGHT:
case RIGHT_SPAN: {
this.grid[ypos][xpos + 1] = CellFieldBuilderAdapter.labelBuilder(
attribute,
orientation);
break;
}
case LEFT:
case LEFT_SPAN: {
this.grid[ypos][xpos - 1] = CellFieldBuilderAdapter.labelBuilder(
attribute,
orientation);
// special case for password, also add confirm label
if (attribute.type == AttributeType.PASSWORD_FIELD) {
this.grid[ypos + 1][xpos - 1] = CellFieldBuilderAdapter.passwordConfirmLabel(
attribute,
orientation);
}
break;
}
case TOP: {
fillDummy(xpos, ypos - 1, orientation.width, 1);
this.grid[ypos - 1][xpos] = CellFieldBuilderAdapter.labelBuilder(
attribute,
orientation);
break;
}
default: {
// do nothing
break;
}
}
} catch (final ArrayIndexOutOfBoundsException e) {
log.error("Failed to set title as configured in: {} for attribute: {}", orientation, attribute, e);
}
return this;
}
void compose() {
if (log.isTraceEnabled()) {
log.trace("Compose grid view: \n" + gridToString());
}
// balance grid (optimize span and grab empty spaces for labels where applicable)
for (int y = 0; y < this.grid.length; y++) {
for (int x = 0; x < this.grid[y].length; x++) {
if (this.grid[y][x] != null) {
this.grid[y][x].balanceGrid(this.grid, x, y);
}
}
}
for (int y = 0; y < this.grid.length; y++) {
for (int x = 0; x < this.grid[y].length; x++) {
if (this.grid[y][x] == null) {
final Label empty = new Label(this.parent, SWT.LEFT);
final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false);
gridData.verticalIndent = 8;
empty.setLayoutData(gridData);
empty.setText(StringUtils.EMPTY /* "empty " + x + " " + y */);
} else {
this.grid[y][x].createCell(this);
}
}
}
}
private void fillDummy(final int x, final int y, final int width, final int height) {
final int upperBoundX = x + width;
final int upperBoundY = y + height;
for (int _y = y; _y < upperBoundY; _y++) {
for (int _x = x; _x < upperBoundX; _x++) {
if (_y < 0 || _x < 0 || _y >= this.grid.length || _x >= this.grid[_y].length) {
log.warn("Out of bounds: {} {}", _x, _y);
continue;
}
this.grid[_y][_x] = CellFieldBuilderAdapter.DUMMY_BUILDER_ADAPTER;
}
}
}
private String gridToString() {
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < this.grid.length; i++) {
if (sb.length() > 0) {
sb.append(",\n");
}
sb.append(Arrays.toString(this.grid[i]));
}
return sb.toString();
}
}

View file

@ -1,78 +1,74 @@
/*
* 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.examconfig.impl.rules;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.examconfig.ValueChangeRule;
import ch.ethz.seb.sebserver.gui.service.examconfig.impl.ViewContext;
@Lazy
@Service
@GuiProfile
public class BrowserViewModeRule implements ValueChangeRule {
private static final Logger log = LoggerFactory.getLogger(BrowserViewModeRule.class);
public static final String KEY_BROWSER_VIEW_MODE = "browserViewMode";
public static final String KEY_TOUCH_EXIT = "enableTouchExit";
public static final String KEY_MAIN_WINDOW_GROUP = "mainBrowserWindowWidth";
@Override
public boolean observesAttribute(final ConfigurationAttribute attribute) {
return KEY_BROWSER_VIEW_MODE.equals(attribute.name);
}
@Override
public void applyRule(
final ViewContext context,
final ConfigurationAttribute attribute,
final ConfigurationValue value) {
if (StringUtils.isBlank(value.value)) {
return;
}
try {
context.enable(KEY_TOUCH_EXIT);
context.enableGroup(KEY_MAIN_WINDOW_GROUP);
switch (Integer.parseInt(value.value)) {
case 0: {
context.disable(KEY_TOUCH_EXIT);
break;
}
case 1: {
context.disable(KEY_TOUCH_EXIT);
context.disableGroup(KEY_MAIN_WINDOW_GROUP);
break;
}
case 2: {
context.disableGroup(KEY_MAIN_WINDOW_GROUP);
break;
}
default: {
context.disable(KEY_TOUCH_EXIT);
break;
}
}
} catch (final Exception e) {
log.warn("Failed to apply rule: ", e);
}
}
}
/*
* 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.examconfig.impl.rules;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.examconfig.ValueChangeRule;
import ch.ethz.seb.sebserver.gui.service.examconfig.impl.ViewContext;
@Lazy
@Service
@GuiProfile
public class BrowserViewModeRule implements ValueChangeRule {
private static final Logger log = LoggerFactory.getLogger(BrowserViewModeRule.class);
public static final String KEY_BROWSER_VIEW_MODE = "browserViewMode";
public static final String KEY_TOUCH_EXIT = "enableTouchExit";
public static final String KEY_MAIN_WINDOW_GROUP = "mainBrowserWindowWidth";
@Override
public boolean observesAttribute(final ConfigurationAttribute attribute) {
return KEY_BROWSER_VIEW_MODE.equals(attribute.name);
}
@Override
public void applyRule(
final ViewContext context,
final ConfigurationAttribute attribute,
final ConfigurationValue value) {
if (StringUtils.isBlank(value.value)) {
return;
}
try {
context.enable(KEY_TOUCH_EXIT);
context.enableGroup(KEY_MAIN_WINDOW_GROUP);
switch (Integer.parseInt(value.value)) {
case 1: {
context.disable(KEY_TOUCH_EXIT);
context.disableGroup(KEY_MAIN_WINDOW_GROUP);
break;
}
case 2: {
context.disableGroup(KEY_MAIN_WINDOW_GROUP);
break;
}
default: {
context.disable(KEY_TOUCH_EXIT);
break;
}
}
} catch (final Exception e) {
log.warn("Failed to apply rule: ", e);
}
}
}

View file

@ -17,10 +17,10 @@ import ch.ethz.seb.sebserver.gbl.util.Utils;
public interface I18nSupport {
public static final String SUPPORTED_LANGUAGES_KEY = "sebserver.gui.supported.languages";
public static final String MULTILINGUAL_KEY = "sebserver.gui.multilingual";
public static final String FORMAL_LOCALE_KEY = "sebserver.gui.date.displayformat";
public static final String ATTR_CURRENT_SESSION_LOCALE = "CURRENT_SESSION_LOCALE";
String SUPPORTED_LANGUAGES_KEY = "sebserver.gui.supported.languages";
String MULTILINGUAL_KEY = "sebserver.gui.multilingual";
String FORMAL_LOCALE_KEY = "sebserver.gui.date.displayformat";
String ATTR_CURRENT_SESSION_LOCALE = "CURRENT_SESSION_LOCALE";
/** Get all supported languages as a collection of Locale
*
@ -40,7 +40,7 @@ public interface I18nSupport {
Locale getUsersFormatLocale();
/** Format a DateTime to a text format to display.
* This uses the date-format defined by either the attribute 'sebserver.gui.date.displayformat'
* This uses the date-format defined by either the attribute 'sebserver.gui.date.display format'
* or the Constants.DEFAULT_DISPLAY_DATE_FORMAT
*
* Adds time-zone offset information if the currents user time-zone is different form UTC
@ -50,7 +50,7 @@ public interface I18nSupport {
String formatDisplayDate(DateTime date);
/** Format a DateTime to a text format to display with additional time zone name at the end.
* This uses the date-format defined by either the attribute 'sebserver.gui.date.displayformat'
* This uses the date-format defined by either the attribute 'sebserver.gui.date.display format'
* or the Constants.DEFAULT_DISPLAY_DATE_FORMAT
*
* Adds time-zone offset information if the currents user time-zone is different form UTC
@ -62,19 +62,19 @@ public interface I18nSupport {
}
/** Format a time-stamp (milliseconds) to a text format to display.
* This uses the date-format defined by either the attribute 'sebserver.gui.date.displayformat'
* This uses the date-format defined by either the attribute 'sebserver.gui.date.display format'
* or the Constants.DEFAULT_DISPLAY_DATE_FORMAT
*
* Adds time-zone information if the currents user time-zone is different form UTC
*
* @param date the DateTime instance
* @param timestamp the unix-timestamp in milliseconds
* @return date formatted date String to display */
default String formatDisplayDate(final Long timestamp) {
return formatDisplayDate(Utils.toDateTimeUTC(timestamp));
}
/** Format a DateTime to a text format to display.
* This uses the date-format defined by either the attribute 'sebserver.gui.datetime.displayformat'
* This uses the date-format defined by either the attribute 'sebserver.gui.datetime.display format'
* or the Constants.DEFAULT_DISPLAY_DATE_TIME_FORMAT
*
* Adds time-zone information if the currents user time-zone is different form UTC
@ -84,19 +84,19 @@ public interface I18nSupport {
String formatDisplayDateTime(DateTime date);
/** Format a time-stamp (milliseconds) to a text format to display.
* This uses the date-format defined by either the attribute 'sebserver.gui.datetime.displayformat'
* This uses the date-format defined by either the attribute 'sebserver.gui.datetime.display format'
* or the Constants.DEFAULT_DISPLAY_DATE_TIME_FORMAT
*
* Adds time-zone information if the currents user time-zone is different form UTC
*
* @param date the DateTime instance
* @param timestamp the unix-timestamp in milliseconds
* @return date formatted date time String to display */
default String formatDisplayDateTime(final Long timestamp) {
return formatDisplayDateTime(Utils.toDateTimeUTC(timestamp));
}
/** Format a DateTime to a text format to display.
* This uses the date-format defined by either the attribute 'sebserver.gui.time.displayformat'
* This uses the date-format defined by either the attribute 'sebserver.gui.time.display format'
* or the Constants.DEFAULT_DISPLAY_TIME_FORMAT
*
* Adds time-zone information if the currents user time-zone is different form UTC
@ -106,12 +106,12 @@ public interface I18nSupport {
String formatDisplayTime(DateTime date);
/** Format a time-stamp (milliseconds) to a text format to display.
* This uses the date-format defined by either the attribute 'sebserver.gui.time.displayformat'
* This uses the date-format defined by either the attribute 'sebserver.gui.time.display format'
* or the Constants.DEFAULT_DISPLAY_TIME_FORMAT
*
* Adds time-zone information if the currents user time-zone is different form UTC
*
* @param date the DateTime instance
* @param timestamp the unix-timestamp in milliseconds
* @return date formatted time String to display */
default String formatDisplayTime(final Long timestamp) {
return formatDisplayTime(Utils.toDateTimeUTC(timestamp));

View file

@ -39,34 +39,116 @@ public interface PolyglotPageService {
* @param locale the Locale to set */
void setPageLocale(Composite root, Locale locale);
/** Used to inject a localised text within the given Control (Widget) that automatically gets changed on language
* change.
*
* @param imageUpload the Control instance
* @param locTextKey the localized text key to inject
*/
void injectI18n(ImageUploadSelection imageUpload, LocTextKey locTextKey);
/** Used to inject a localised text within the given Control (Widget) that automatically gets changed on language
* change.
*
* @param label the Control instance
* @param locTextKey the localized text key to inject
*/
void injectI18n(Label label, LocTextKey locTextKey);
/** Used to inject a localised text within the given Control (Widget) that automatically gets changed on language
* change.
*
* @param label the Control instance
* @param locTextKey the localized text key to inject
* @param locToolTipKey the localized text key for the tooltip to inject
*/
void injectI18n(Label label, LocTextKey locTextKey, LocTextKey locToolTipKey);
/** Used to inject a localised text within the given Control (Widget) that automatically gets changed on language
* change.
*
* @param group the Control instance
* @param locTextKey the localized text key to inject
* @param locTooltipKey the localized text key for the tooltip to inject
*/
void injectI18n(Group group, LocTextKey locTextKey, LocTextKey locTooltipKey);
/** Used to inject a localised text within the given Control (Widget) that automatically gets changed on language
* change.
*
* @param button the Control instance
* @param locTextKey the localized text key to inject
*/
void injectI18n(Button button, LocTextKey locTextKey);
/** Used to inject a localised text within the given Control (Widget) that automatically gets changed on language
* change.
*
* @param button the Control instance
* @param locTextKey the localized text key to inject
* @param locToolTipKey the localized text key for the tooltip to inject
*/
void injectI18n(Button button, LocTextKey locTextKey, LocTextKey locToolTipKey);
/** Used to inject a localised text within the given Control (Widget) that automatically gets changed on language
* change.
*
* @param tree the Control instance
*/
void injectI18n(Tree tree);
/** Used to inject a localised text within the given Control (Widget) that automatically gets changed on language
* change.
*
* @param treeItem the Control instance
* @param locTextKey the localized text key to inject
*/
void injectI18n(TreeItem treeItem, LocTextKey locTextKey);
/** Used to inject a localised text within the given Control (Widget) that automatically gets changed on language
* change.
*
* @param table the Control instance
*/
void injectI18n(Table table);
/** Used to inject a localised text within the given Control (Widget) that automatically gets changed on language
* change.
*
* @param tabFolder the Control instance
*/
void injectI18n(TabFolder tabFolder);
/** Used to inject a localised text within the given Control (Widget) that automatically gets changed on language
* change.
*
* @param tableColumn the Control instance
* @param locTextKey the localized text key to inject
* @param locTooltipKey the localized text key for the tooltip to inject
*/
void injectI18n(TableColumn tableColumn, LocTextKey locTextKey, LocTextKey locTooltipKey);
void injectI18n(TableItem tableItem, LocTextKey... locTextKey);
/** Used to inject a localised text within the given Control (Widget) that automatically gets changed on language
* change.
*
* @param tabItem the Control instance
* @param locTextKey the localized text key to inject
* @param locTooltipKey the localized text key for the tooltip to inject
*/
void injectI18n(TabItem tabItem, LocTextKey locTextKey, LocTextKey locTooltipKey);
/** Used to inject a localised tooltip text within the given Control (Widget) that automatically gets changed on
* language change.
*
* @param control the Control instance
* @param locTooltipKey the localized text key for the tooltip to inject
*/
void injectI18nTooltip(Control control, LocTextKey locTooltipKey);
/** Used to create the page language selector if needed
*
* @param composerCtx the PageContext
*/
void createLanguageSelector(PageContext composerCtx);
}

View file

@ -1,186 +1,185 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui.service.i18n.impl;
import java.util.Arrays;
import java.util.Collection;
import java.util.Locale;
import java.util.stream.Collectors;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.rap.rwt.RWT;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
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.remote.webservice.auth.CurrentUser;
@Lazy
@Service
@GuiProfile
public class I18nSupportImpl implements I18nSupport {
private static final Logger log = LoggerFactory.getLogger(I18nSupportImpl.class);
private final Locale defaultFormatLocale;
private final CurrentUser currentUser;
private final MessageSource messageSource;
private final Locale defaultLocale = Locale.ENGLISH;
private final Collection<Locale> supportedLanguages;
public I18nSupportImpl(
final CurrentUser currentUser,
final MessageSource messageSource,
final Environment environment) {
this.currentUser = currentUser;
this.messageSource = messageSource;
final String defaultForamtLocaleString = environment.getProperty(
FORMAL_LOCALE_KEY,
Constants.DEFAULT_LANG_CODE);
this.defaultFormatLocale = Locale.forLanguageTag(defaultForamtLocaleString);
final boolean multilingual = BooleanUtils.toBoolean(environment.getProperty(
MULTILINGUAL_KEY,
Constants.FALSE_STRING));
if (multilingual) {
final String languagesString = environment.getProperty(
SUPPORTED_LANGUAGES_KEY,
Locale.ENGLISH.getLanguage());
this.supportedLanguages = Utils.immutableCollectionOf(
Arrays.asList(StringUtils.split(languagesString, Constants.LIST_SEPARATOR))
.stream()
.map(s -> Locale.forLanguageTag(s))
.collect(Collectors.toList()));
} else {
this.supportedLanguages = Utils.immutableCollectionOf(Locale.ENGLISH);
}
}
@Override
public Collection<Locale> supportedLanguages() {
return this.supportedLanguages;
}
@Override
public Locale getUsersFormatLocale() {
// TODO here also a user based format locale can be verified on the future
return this.defaultFormatLocale;
}
@Override
public Locale getUsersLanguageLocale() {
// first session-locale if available
try {
final Locale sessionLocale = (Locale) RWT.getUISession()
.getHttpSession()
.getAttribute(ATTR_CURRENT_SESSION_LOCALE);
if (sessionLocale != null) {
return sessionLocale;
}
} catch (final IllegalStateException e) {
log.warn("Get current locale for session failed: {}", e.getMessage());
}
// second user-locale if available
if (this.currentUser.isAvailable()) {
return this.currentUser.get().language;
}
// last the default locale
return this.defaultLocale;
}
@Override
public String formatDisplayDate(final DateTime date) {
final String pattern = DateTimeFormat.patternForStyle("M-", getUsersFormatLocale());
return formatDisplayDate(date, DateTimeFormat.forPattern(pattern));
}
@Override
public String formatDisplayDateTime(final DateTime date) {
final String pattern = DateTimeFormat.patternForStyle("MS", getUsersFormatLocale());
return formatDisplayDate(date, DateTimeFormat.forPattern(pattern));
}
@Override
public String formatDisplayTime(final DateTime date) {
final String pattern = DateTimeFormat.patternForStyle("-S", getUsersFormatLocale());
return formatDisplayDate(date, DateTimeFormat.forPattern(pattern));
}
@Override
public String getUsersTimeZoneTitleSuffix() {
final UserInfo userInfo = this.currentUser.get();
if (userInfo.timeZone == null || userInfo.timeZone.equals(DateTimeZone.UTC)) {
return "";
} else {
return "(" + this.currentUser.get().timeZone.getID() + ")";
}
}
@Override
public String getText(final String key, final String def, final Object... args) {
return this.messageSource.getMessage(key, args, def, this.getUsersLanguageLocale());
}
@Override
public String getText(final String key, final Locale locale, final String def, final Object... args) {
return this.messageSource.getMessage(key, args, def, locale);
}
@Override
public boolean hasText(final LocTextKey key) {
if (key == null) {
return false;
}
return StringUtils.isNotBlank(getText(key.name, (String) null));
}
private String formatDisplayDate(final DateTime date, final DateTimeFormatter formatter) {
if (date == null) {
return Constants.EMPTY_NOTE;
}
DateTime dateUTC = date;
if (date.getZone() != DateTimeZone.UTC) {
log.warn("Date that has not UTC timezone used. "
+ "Reset to UTC timezone with any change on time instance for further processing");
dateUTC = date.withZone(DateTimeZone.UTC);
}
final UserInfo userInfo = this.currentUser.get();
if (userInfo != null && userInfo.timeZone != null && !userInfo.timeZone.equals(DateTimeZone.UTC)) {
return dateUTC.toString(formatter.withZone(userInfo.timeZone));
} else {
return dateUTC.toString(formatter);
}
}
}
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui.service.i18n.impl;
import java.util.Arrays;
import java.util.Collection;
import java.util.Locale;
import java.util.stream.Collectors;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.rap.rwt.RWT;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
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.remote.webservice.auth.CurrentUser;
@Lazy
@Service
@GuiProfile
public class I18nSupportImpl implements I18nSupport {
private static final Logger log = LoggerFactory.getLogger(I18nSupportImpl.class);
private final Locale defaultFormatLocale;
private final CurrentUser currentUser;
private final MessageSource messageSource;
private final Locale defaultLocale = Locale.ENGLISH;
private final Collection<Locale> supportedLanguages;
public I18nSupportImpl(
final CurrentUser currentUser,
final MessageSource messageSource,
final Environment environment) {
this.currentUser = currentUser;
this.messageSource = messageSource;
final String defaultFormatLocaleString = environment.getProperty(
FORMAL_LOCALE_KEY,
Constants.DEFAULT_LANG_CODE);
this.defaultFormatLocale = Locale.forLanguageTag(defaultFormatLocaleString);
final boolean multilingual = BooleanUtils.toBoolean(environment.getProperty(
MULTILINGUAL_KEY,
Constants.FALSE_STRING));
if (multilingual) {
final String languagesString = environment.getProperty(
SUPPORTED_LANGUAGES_KEY,
Locale.ENGLISH.getLanguage());
this.supportedLanguages = Utils.immutableCollectionOf(
Arrays.stream(StringUtils.split(languagesString, Constants.LIST_SEPARATOR))
.map(Locale::forLanguageTag)
.collect(Collectors.toList()));
} else {
this.supportedLanguages = Utils.immutableCollectionOf(Locale.ENGLISH);
}
}
@Override
public Collection<Locale> supportedLanguages() {
return this.supportedLanguages;
}
@Override
public Locale getUsersFormatLocale() {
// TODO here also a user based format locale can be verified on the future
return this.defaultFormatLocale;
}
@Override
public Locale getUsersLanguageLocale() {
// first session-locale if available
try {
final Locale sessionLocale = (Locale) RWT.getUISession()
.getHttpSession()
.getAttribute(ATTR_CURRENT_SESSION_LOCALE);
if (sessionLocale != null) {
return sessionLocale;
}
} catch (final IllegalStateException e) {
log.warn("Get current locale for session failed: {}", e.getMessage());
}
// second user-locale if available
if (this.currentUser.isAvailable()) {
return this.currentUser.get().language;
}
// last the default locale
return this.defaultLocale;
}
@Override
public String formatDisplayDate(final DateTime date) {
final String pattern = DateTimeFormat.patternForStyle("M-", getUsersFormatLocale());
return formatDisplayDate(date, DateTimeFormat.forPattern(pattern));
}
@Override
public String formatDisplayDateTime(final DateTime date) {
final String pattern = DateTimeFormat.patternForStyle("MS", getUsersFormatLocale());
return formatDisplayDate(date, DateTimeFormat.forPattern(pattern));
}
@Override
public String formatDisplayTime(final DateTime date) {
final String pattern = DateTimeFormat.patternForStyle("-S", getUsersFormatLocale());
return formatDisplayDate(date, DateTimeFormat.forPattern(pattern));
}
@Override
public String getUsersTimeZoneTitleSuffix() {
final UserInfo userInfo = this.currentUser.get();
if (userInfo.timeZone == null || userInfo.timeZone.equals(DateTimeZone.UTC)) {
return "";
} else {
return "(" + this.currentUser.get().timeZone.getID() + ")";
}
}
@Override
public String getText(final String key, final String def, final Object... args) {
return this.messageSource.getMessage(key, args, def, this.getUsersLanguageLocale());
}
@Override
public String getText(final String key, final Locale locale, final String def, final Object... args) {
return this.messageSource.getMessage(key, args, def, locale);
}
@Override
public boolean hasText(final LocTextKey key) {
if (key == null) {
return false;
}
return StringUtils.isNotBlank(getText(key.name, (String) null));
}
private String formatDisplayDate(final DateTime date, final DateTimeFormatter formatter) {
if (date == null) {
return Constants.EMPTY_NOTE;
}
DateTime dateUTC = date;
if (date.getZone() != DateTimeZone.UTC) {
log.warn("Date that has not UTC timezone used. "
+ "Reset to UTC timezone with any change on time instance for further processing");
dateUTC = date.withZone(DateTimeZone.UTC);
}
final UserInfo userInfo = this.currentUser.get();
if (userInfo != null && userInfo.timeZone != null && !userInfo.timeZone.equals(DateTimeZone.UTC)) {
return dateUTC.toString(formatter.withZone(userInfo.timeZone));
} else {
return dateUTC.toString(formatter);
}
}
}

View file

@ -165,18 +165,6 @@ public final class PolyglotPageServiceImpl implements PolyglotPageService {
}
}
@Override
public void injectI18n(final TableItem tableItem, final LocTextKey... locTextKey) {
if (locTextKey == null) {
return;
}
tableItem.setData(POLYGLOT_ITEM_TEXT_DATA_KEY, locTextKey);
for (int i = 0; i < locTextKey.length; i++) {
tableItem.setText(i, this.i18nSupport.getText(locTextKey[i]));
}
}
@Override
public void injectI18n(final TabItem tabItem, final LocTextKey locTextKey, final LocTextKey locTooltipKey) {
tabItem.setData(POLYGLOT_ITEM_TEXT_DATA_KEY, locTextKey);

View file

@ -1,202 +1,202 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui.service.page.impl;
import java.util.Collection;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.MessageBox;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
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.PageService;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.IllegalUserSessionStateException;
import ch.ethz.seb.sebserver.gui.widget.Message;
@Lazy
@Service
@GuiProfile
public class ComposerServiceImpl implements ComposerService {
private static final Logger log = LoggerFactory.getLogger(ComposerServiceImpl.class);
private final Class<? extends PageDefinition> loginPageType = DefaultLoginPage.class;
private final Class<? extends PageDefinition> mainPageType = DefaultMainPage.class;
final AuthorizationContextHolder authorizationContextHolder;
private final I18nSupport i18nSupport;
private final Map<String, TemplateComposer> composer;
private final Map<String, PageDefinition> pages;
public ComposerServiceImpl(
final AuthorizationContextHolder authorizationContextHolder,
final I18nSupport i18nSupport,
final Collection<TemplateComposer> composer,
final Collection<PageDefinition> pageDefinitions) {
this.authorizationContextHolder = authorizationContextHolder;
this.i18nSupport = i18nSupport;
this.composer = composer
.stream()
.collect(Collectors.toMap(
comp -> comp.getClass().getName(),
Function.identity()));
this.pages = pageDefinitions
.stream()
.collect(Collectors.toMap(
page -> page.getClass().getName(),
Function.identity()));
}
@Override
public PageDefinition mainPage() {
return this.pages.get(this.mainPageType.getName());
}
@Override
public PageDefinition loginPage() {
return this.pages.get(this.loginPageType.getName());
}
@Override
public boolean validate(final String composerName, final PageContext pageContext) {
if (!this.composer.containsKey(composerName)) {
return false;
}
return this.composer
.get(composerName)
.validate(pageContext);
}
@Override
public void compose(
final Class<? extends TemplateComposer> composerType,
final PageContext pageContext) {
if (composerType != null && pageContext != null) {
compose(composerType.getName(), pageContext);
}
}
@Override
public void compose(
final String name,
final PageContext pageContext) {
// Check first if there is still a valid authorization context
if (!this.authorizationContextHolder.getAuthorizationContext().isValid()) {
return;
}
if (!this.composer.containsKey(name)) {
log.error("No TemplateComposer with name: " + name + " found. Check Spring confiuration and beans");
return;
}
final TemplateComposer composer = this.composer.get(name);
if (composer.validate(pageContext)) {
PageService.clearComposite(pageContext.getParent());
try {
composer.compose(pageContext);
PageService.updateScrolledComposite(pageContext.getParent());
} catch (final IllegalUserSessionStateException e) {
log.warn("Illegal user session state detected... ceanup user session and forward to login page.");
pageContext.forwardToLoginPage();
final MessageBox logoutSuccess = new Message(
pageContext.getShell(),
this.i18nSupport.getText("sebserver.logout"),
this.i18nSupport.getText("sebserver.logout.invalid-session.message"),
SWT.ICON_INFORMATION,
this.i18nSupport);
logoutSuccess.open(null);
return;
} catch (final RuntimeException e) {
log.warn("Failed to compose: {}, pageContext: {}", name, pageContext, e);
pageContext.notifyError(new LocTextKey("sebserver.error.unexpected"), e);
} catch (final Exception e) {
log.error("Failed to compose: {}, pageContext: {}", name, pageContext, e);
}
try {
pageContext.getParent().layout();
} catch (final Exception e) {
log.warn("Failed to layout new composition: {}, pageContext: {}", name, pageContext, e);
}
} else {
log.error(
"Invalid or missing mandatory attributes to handle compose request of ViewComposer: {} pageContext: {}",
name,
pageContext);
}
}
@Override
public void composePage(
final PageDefinition pageDefinition,
final Composite root) {
compose(
pageDefinition.composer(),
pageDefinition.applyPageContext(createPageContext(root)));
}
@Override
public void composePage(
final Class<? extends PageDefinition> pageType,
final Composite root) {
final String pageName = pageType.getName();
if (!this.pages.containsKey(pageName)) {
log.error("Unknown page with name: {}", pageName);
return;
}
final PageDefinition pageDefinition = this.pages.get(pageName);
compose(
pageDefinition.composer(),
pageDefinition.applyPageContext(createPageContext(root)));
}
@Override
public void loadLoginPage(final Composite parent) {
composePage(this.loginPageType, parent);
}
@Override
public void loadMainPage(final Composite parent) {
composePage(this.mainPageType, parent);
}
private PageContext createPageContext(final Composite root) {
return new PageContextImpl(this.i18nSupport, this, root, root, null);
}
}
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui.service.page.impl;
import java.util.Collection;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.MessageBox;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
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.PageService;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.IllegalUserSessionStateException;
import ch.ethz.seb.sebserver.gui.widget.Message;
@Lazy
@Service
@GuiProfile
public class ComposerServiceImpl implements ComposerService {
private static final Logger log = LoggerFactory.getLogger(ComposerServiceImpl.class);
private final Class<? extends PageDefinition> loginPageType = DefaultLoginPage.class;
private final Class<? extends PageDefinition> mainPageType = DefaultMainPage.class;
final AuthorizationContextHolder authorizationContextHolder;
private final I18nSupport i18nSupport;
private final Map<String, TemplateComposer> composer;
private final Map<String, PageDefinition> pages;
public ComposerServiceImpl(
final AuthorizationContextHolder authorizationContextHolder,
final I18nSupport i18nSupport,
final Collection<TemplateComposer> composer,
final Collection<PageDefinition> pageDefinitions) {
this.authorizationContextHolder = authorizationContextHolder;
this.i18nSupport = i18nSupport;
this.composer = composer
.stream()
.collect(Collectors.toMap(
comp -> comp.getClass().getName(),
Function.identity()));
this.pages = pageDefinitions
.stream()
.collect(Collectors.toMap(
page -> page.getClass().getName(),
Function.identity()));
}
@Override
public PageDefinition mainPage() {
return this.pages.get(this.mainPageType.getName());
}
@Override
public PageDefinition loginPage() {
return this.pages.get(this.loginPageType.getName());
}
@Override
public boolean validate(final String composerName, final PageContext pageContext) {
if (!this.composer.containsKey(composerName)) {
return false;
}
return this.composer
.get(composerName)
.validate(pageContext);
}
@Override
public void compose(
final Class<? extends TemplateComposer> composerType,
final PageContext pageContext) {
if (composerType != null && pageContext != null) {
compose(composerType.getName(), pageContext);
}
}
@Override
public void compose(
final String name,
final PageContext pageContext) {
// Check first if there is still a valid authorization context
if (!this.authorizationContextHolder.getAuthorizationContext().isValid()) {
return;
}
if (!this.composer.containsKey(name)) {
log.error("No TemplateComposer with name: " + name + " found. Check Spring configuration and beans");
return;
}
final TemplateComposer composer = this.composer.get(name);
if (composer.validate(pageContext)) {
PageService.clearComposite(pageContext.getParent());
try {
composer.compose(pageContext);
PageService.updateScrolledComposite(pageContext.getParent());
} catch (final IllegalUserSessionStateException e) {
log.warn("Illegal user session state detected... cleanup user session and forward to login page.");
pageContext.forwardToLoginPage();
final MessageBox logoutSuccess = new Message(
pageContext.getShell(),
this.i18nSupport.getText("sebserver.logout"),
this.i18nSupport.getText("sebserver.logout.invalid-session.message"),
SWT.ICON_INFORMATION,
this.i18nSupport);
logoutSuccess.open(null);
return;
} catch (final RuntimeException e) {
log.warn("Failed to compose: {}, pageContext: {}", name, pageContext, e);
pageContext.notifyError(new LocTextKey("sebserver.error.unexpected"), e);
} catch (final Exception e) {
log.error("Failed to compose: {}, pageContext: {}", name, pageContext, e);
}
try {
pageContext.getParent().layout();
} catch (final Exception e) {
log.warn("Failed to layout new composition: {}, pageContext: {}", name, pageContext, e);
}
} else {
log.error(
"Invalid or missing mandatory attributes to handle compose request of ViewComposer: {} pageContext: {}",
name,
pageContext);
}
}
@Override
public void composePage(
final PageDefinition pageDefinition,
final Composite root) {
compose(
pageDefinition.composer(),
pageDefinition.applyPageContext(createPageContext(root)));
}
@Override
public void composePage(
final Class<? extends PageDefinition> pageType,
final Composite root) {
final String pageName = pageType.getName();
if (!this.pages.containsKey(pageName)) {
log.error("Unknown page with name: {}", pageName);
return;
}
final PageDefinition pageDefinition = this.pages.get(pageName);
compose(
pageDefinition.composer(),
pageDefinition.applyPageContext(createPageContext(root)));
}
@Override
public void loadLoginPage(final Composite parent) {
composePage(this.loginPageType, parent);
}
@Override
public void loadMainPage(final Composite parent) {
composePage(this.mainPageType, parent);
}
private PageContext createPageContext(final Composite root) {
return new PageContextImpl(this.i18nSupport, this, root, root, null);
}
}