From 6584d6dbfea55a506eab3b4380566c986da2065d Mon Sep 17 00:00:00 2001 From: anhefti Date: Wed, 12 Feb 2020 13:48:06 +0100 Subject: [PATCH] tooltips, table selection actions, activation, mandatory --- .../ch/ethz/seb/sebserver/gbl/Constants.java | 4 +- .../ch/ethz/seb/sebserver/gbl/util/Utils.java | 8 ++ .../sebserver/gui/content/IndicatorForm.java | 3 +- .../gui/content/InstitutionForm.java | 3 +- .../gui/content/InstitutionList.java | 42 ++------ .../gui/content/action/ActionDefinition.java | 4 +- .../gui/content/action/ActionPane.java | 1 + .../seb/sebserver/gui/form/FieldBuilder.java | 40 +++++++- .../examconfig/impl/TableFieldBuilder.java | 17 ++-- .../examconfig/impl/TextFieldBuilder.java | 4 +- .../i18n/impl/PolyglotPageServiceImpl.java | 11 ++- .../gui/service/page/PageService.java | 59 +++++++++++ .../page/impl/ComposerServiceImpl.java | 1 - .../sebserver/gui/table/ColumnDefinition.java | 4 +- .../seb/sebserver/gui/table/EntityTable.java | 16 ++- .../gui/widget/FileUploadSelection.java | 5 +- .../gui/widget/ImageUploadSelection.java | 8 +- .../gui/widget/MultiSelectionCheckbox.java | 3 +- .../sebserver/gui/widget/RadioSelection.java | 3 +- .../sebserver/gui/widget/WidgetFactory.java | 14 +-- src/main/resources/messages.properties | 93 ++++++++++-------- src/main/resources/static/css/sebserver.css | 10 +- .../resources/static/images/mandatory.png | Bin 0 -> 678 bytes 23 files changed, 230 insertions(+), 123 deletions(-) create mode 100644 src/main/resources/static/images/mandatory.png diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/Constants.java b/src/main/java/ch/ethz/seb/sebserver/gbl/Constants.java index cd5fd0c9..b1ec8883 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/Constants.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/Constants.java @@ -48,8 +48,8 @@ public final class Constants { public static final Character PERCENTAGE = '%'; public static final Character SLASH = '/'; public static final Character BACKSLASH = '\\'; - public static final Character QUOTE = '"'; - public static final Character DOUBLE_QUOTE = '\''; + public static final Character QUOTE = '\''; + public static final Character DOUBLE_QUOTE = '"'; public static final Character COMMA = ','; public static final Character PIPE = '|'; public static final Character AMPERSAND = '&'; diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java index 4247689e..6c6aa1bf 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java @@ -328,6 +328,14 @@ public final class Utils { : null; } + public static String formatLineBreaks(final String text) { + if (text == null) { + return null; + } + + return text.replace("
", "\n"); + } + public static final String encodeFormURL_UTF_8(final String value) { if (StringUtils.isBlank(value)) { return value; diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/IndicatorForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/IndicatorForm.java index ba8509cf..51994ff9 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/IndicatorForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/IndicatorForm.java @@ -21,6 +21,7 @@ import ch.ethz.seb.sebserver.gbl.model.exam.Exam; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator; import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; import ch.ethz.seb.sebserver.gui.form.Form; import ch.ethz.seb.sebserver.gui.form.FormBuilder; @@ -107,7 +108,7 @@ public class IndicatorForm implements TemplateComposer { final boolean typeSet = indicator.type != null; final String typeDescription = (typeSet) - ? this.i18nSupport.getText(INDICATOR_TYPE_DESC_PREFIX + indicator.type.name) + ? Utils.formatLineBreaks(this.i18nSupport.getText(INDICATOR_TYPE_DESC_PREFIX + indicator.type.name)) : Constants.EMPTY_NOTE; // new PageContext with actual EntityKey diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionForm.java index b8d69605..ff4412e7 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionForm.java @@ -109,7 +109,8 @@ public class InstitutionForm implements TemplateComposer { .addField(FormBuilder.text( Domain.INSTITUTION.ATTR_NAME, FORM_NAME_TEXT_KEY, - institution.name)) + institution.name) + .mandatory(!isReadonly)) .addField(FormBuilder.text( Domain.INSTITUTION.ATTR_URL_SUFFIX, FORM_URL_SUFFIX_TEXT_KEY, diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionList.java index fa6b9310..096ba1cc 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/InstitutionList.java @@ -8,28 +8,23 @@ package ch.ethz.seb.sebserver.gui.content; -import java.util.Set; -import java.util.function.Consumer; - +import org.apache.commons.lang3.StringUtils; import org.eclipse.swt.widgets.Composite; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; -import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.institution.Institution; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; -import ch.ethz.seb.sebserver.gbl.util.Tuple; import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; import ch.ethz.seb.sebserver.gui.service.page.PageContext; import ch.ethz.seb.sebserver.gui.service.page.PageService; import ch.ethz.seb.sebserver.gui.service.page.PageService.PageActionBuilder; 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.impl.PageAction; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitutionPage; @@ -83,7 +78,7 @@ public class InstitutionList implements TemplateComposer { this.activityFilter = new TableFilterAttribute( CriteriaType.SINGLE_SELECTION, Institution.FILTER_ATTR_ACTIVE, - Constants.TRUE_STRING, + StringUtils.EMPTY, this.pageService.getResourceService()::activityResources); } @@ -122,7 +117,14 @@ public class InstitutionList implements TemplateComposer { .withDefaultAction(pageActionBuilder .newAction(ActionDefinition.INSTITUTION_VIEW_FROM_LIST) .create()) - .withSelectionListener(getSelectionPublisher(pageContext)) + .withSelectionListener(this.pageService.getSelectionPublisher( + ActionDefinition.INSTITUTION_TOGGLE_ACTIVITY, + ActionDefinition.INSTITUTION_ACTIVATE, + ActionDefinition.INSTITUTION_DEACTIVATE, + pageContext, + ActionDefinition.INSTITUTION_VIEW_FROM_LIST, + ActionDefinition.INSTITUTION_MODIFY_FROM_LIST, + ActionDefinition.INSTITUTION_TOGGLE_ACTIVITY)) .compose(pageContext.copyOf(content)); // propagate content actions to action-pane @@ -153,28 +155,4 @@ public class InstitutionList implements TemplateComposer { .publishIf(() -> instGrant.m() && table.hasAnyContent(), false); } - private final Consumer> getSelectionPublisher(final PageContext pageContext) { - return rows -> { - this.pageService.firePageEvent(new ActionActivationEvent( - false, - ActionDefinition.INSTITUTION_VIEW_FROM_LIST, - ActionDefinition.INSTITUTION_MODIFY_FROM_LIST, - ActionDefinition.INSTITUTION_TOGGLE_ACTIVITY), - pageContext); - if (!rows.isEmpty()) { - this.pageService.firePageEvent(new ActionActivationEvent( - true, - new Tuple<>( - ActionDefinition.INSTITUTION_TOGGLE_ACTIVITY, - rows.iterator().next().active - ? ActionDefinition.INSTITUTION_DEACTIVATE - : ActionDefinition.INSTITUTION_ACTIVATE), - ActionDefinition.INSTITUTION_VIEW_FROM_LIST, - ActionDefinition.INSTITUTION_MODIFY_FROM_LIST, - ActionDefinition.INSTITUTION_TOGGLE_ACTIVITY), - pageContext); - } - }; - } - } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java index c2f9d1dd..5af58e3b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java @@ -54,7 +54,7 @@ public enum ActionDefinition { PageStateDefinitionImpl.INSTITUTION_VIEW, ActionCategory.FORM), INSTITUTION_SAVE_AND_ACTIVATE( - new LocTextKey("sebserver.institution.action.activate"), + new LocTextKey("sebserver.form.action.save.activate"), ImageIcon.ACTIVE, PageStateDefinitionImpl.INSTITUTION_VIEW, ActionCategory.FORM), @@ -70,7 +70,7 @@ public enum ActionDefinition { ActionCategory.FORM), INSTITUTION_TOGGLE_ACTIVITY( new LocTextKey("sebserver.overall.action.toggle-activity"), - ImageIcon.TOGGLE_OFF, + ImageIcon.SWITCH, PageStateDefinitionImpl.INSTITUTION_LIST, ActionCategory.INSTITUTION_LIST), diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionPane.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionPane.java index 9068c313..d03d738b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionPane.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionPane.java @@ -131,6 +131,7 @@ public class ActionPane implements TemplateComposer { actionItem.setForeground(null); } else { actionItem.setForeground(new Color(parent.getDisplay(), new RGBA(150, 150, 150, 50))); + ActionPane.this.pageService.getPolyglotPageService().injectI18n(actionItem, ad.title); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/form/FieldBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/form/FieldBuilder.java index 814b4fd0..36756331 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/form/FieldBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/form/FieldBuilder.java @@ -24,6 +24,8 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant; public abstract class FieldBuilder { + public static final LocTextKey MANDATORY_TEXT_KEY = new LocTextKey("sebserver.form.mandatory"); + public static final String TOOLTIP_KEY_SUFFIX_LABEL = ".tooltip"; public static final String TOOLTIP_KEY_SUFFIX_LEFT = ".tooltip.left"; public static final String TOOLTIP_KEY_SUFFIX_RIGHT = ".tooltip.right"; @@ -36,9 +38,11 @@ public abstract class FieldBuilder { boolean readonly = false; boolean visible = true; String defaultLabel = null; + boolean isMandatory = false; final String name; final LocTextKey label; + final LocTextKey tooltipLabel; final LocTextKey tooltipKeyLeft; final LocTextKey tooltipKeyRight; final T value; @@ -47,6 +51,7 @@ public abstract class FieldBuilder { this.name = name; this.label = label; this.value = value; + this.tooltipLabel = (label != null) ? new LocTextKey(label.name + TOOLTIP_KEY_SUFFIX_LABEL) : null; this.tooltipKeyLeft = (label != null) ? new LocTextKey(label.name + TOOLTIP_KEY_SUFFIX_LEFT) : null; this.tooltipKeyRight = (label != null) ? new LocTextKey(label.name + TOOLTIP_KEY_SUFFIX_RIGHT) : null; } @@ -61,6 +66,16 @@ public abstract class FieldBuilder { return this; } + public FieldBuilder mandatory() { + this.isMandatory = true; + return this; + } + + public FieldBuilder mandatory(final boolean mandatory) { + this.isMandatory = mandatory; + return this; + } + public FieldBuilder withInputSpan(final int span) { this.spanInput = span; return this; @@ -108,7 +123,7 @@ public abstract class FieldBuilder { } final Composite infoGrid = new Composite(parent, SWT.NONE); - final GridLayout gridLayout = new GridLayout(3, false); + final GridLayout gridLayout = new GridLayout(4, false); gridLayout.verticalSpacing = 0; gridLayout.marginHeight = 0; gridLayout.marginWidth = 0; @@ -128,14 +143,26 @@ public abstract class FieldBuilder { info.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false)); } + final boolean hasLabelTooltip = (fieldBuilder.tooltipLabel != null && + StringUtils.isNotBlank(builder.i18nSupport.getText(fieldBuilder.tooltipLabel, ""))); + final Label label = labelLocalized( builder.widgetFactory, infoGrid, fieldBuilder.label, fieldBuilder.defaultLabel, + (hasLabelTooltip) ? fieldBuilder.tooltipLabel : null, 1, fieldBuilder.titleValign); + if (fieldBuilder.isMandatory) { + final Label mandatory = builder.widgetFactory.imageButton( + WidgetFactory.ImageIcon.MANDATORY, + infoGrid, + MANDATORY_TEXT_KEY); + mandatory.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false)); + } + if (fieldBuilder.tooltipKeyRight != null && StringUtils.isNotBlank(builder.i18nSupport.getText(fieldBuilder.tooltipKeyRight, ""))) { @@ -156,7 +183,7 @@ public abstract class FieldBuilder { final String defaultText, final int hspan) { - return labelLocalized(widgetFactory, parent, locTextKey, defaultText, hspan, SWT.CENTER); + return labelLocalized(widgetFactory, parent, locTextKey, defaultText, null, hspan, SWT.CENTER); } public static final Label labelLocalized( @@ -164,13 +191,18 @@ public abstract class FieldBuilder { final Composite parent, final LocTextKey locTextKey, final String defaultText, + final LocTextKey tooltipTextKey, final int hspan, final int verticalAlignment) { + final LocTextKey labelKey = StringUtils.isNotBlank(defaultText) + ? new LocTextKey(defaultText) + : locTextKey; + final Label label = widgetFactory.labelLocalized( parent, - locTextKey, - (StringUtils.isNotBlank(defaultText) ? defaultText : locTextKey.name)); + labelKey, + tooltipTextKey); final GridData gridData = new GridData(SWT.LEFT, verticalAlignment, false, false, hspan, 1); gridData.heightHint = FormBuilder.FORM_ROW_HEIGHT; label.setLayoutData(gridData); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TableFieldBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TableFieldBuilder.java index ab88be27..923b7442 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TableFieldBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TableFieldBuilder.java @@ -28,6 +28,7 @@ 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; @@ -41,8 +42,9 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon; @GuiProfile public class TableFieldBuilder extends AbstractTableFieldBuilder { - private static final String ADD_TOOLTIP_SUFFIX = ".add.tooltip"; - private static final String REMOVE_TOOLTIP_SUFFIX = ".remove.tooltip"; + 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, @@ -79,7 +81,7 @@ public class TableFieldBuilder extends AbstractTableFieldBuilder { columnAttribute.name), new LocTextKey(ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + columnAttribute.name + - ".tootltip")); + TOOLTIP_SUFFIX)); column.setWidth(100); column.setResizable(false); column.setMoveable(false); @@ -88,15 +90,14 @@ public class TableFieldBuilder extends AbstractTableFieldBuilder { 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(viewContext.i18nSupport.getText( + column.setToolTipText(Utils.formatLineBreaks(viewContext.i18nSupport.getText( ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + attribute.name + ADD_TOOLTIP_SUFFIX, - "Add new")); + "Add new"))); column.setWidth(20); column.setResizable(false); column.setMoveable(false); @@ -107,11 +108,11 @@ public class TableFieldBuilder extends AbstractTableFieldBuilder { column = new TableColumn(table, SWT.NONE); column.setImage(ImageIcon.REMOVE_BOX_WHITE.getImage(parent.getDisplay())); - column.setToolTipText(viewContext.i18nSupport.getText( + column.setToolTipText(Utils.formatLineBreaks(viewContext.i18nSupport.getText( ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + attribute.name + REMOVE_TOOLTIP_SUFFIX, - "Remove Selected")); + "Remove Selected"))); column.setWidth(20); column.setResizable(false); column.setMoveable(false); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TextFieldBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TextFieldBuilder.java index 8b0a7ed3..fa853f88 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TextFieldBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TextFieldBuilder.java @@ -25,6 +25,7 @@ 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.gbl.util.Utils; import ch.ethz.seb.sebserver.gui.form.FieldBuilder; import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService; import ch.ethz.seb.sebserver.gui.service.examconfig.InputField; @@ -91,7 +92,8 @@ public class TextFieldBuilder implements InputFieldBuilder { attribute, i18nSupport); if (toolTipKey != null) { - final Consumer updateFunction = t -> t.setToolTipText(i18nSupport.getText(toolTipKey)); + final Consumer updateFunction = + t -> t.setToolTipText(Utils.formatLineBreaks(i18nSupport.getText(toolTipKey))); text.setData( PolyglotPageService.POLYGLOT_ITEM_TOOLTIP_DATA_KEY, updateFunction); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java index 3716f59e..b37c1b37 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/i18n/impl/PolyglotPageServiceImpl.java @@ -30,6 +30,7 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; 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.i18n.PolyglotPageService; @@ -116,7 +117,7 @@ public final class PolyglotPageServiceImpl implements PolyglotPageService { b.setText(this.i18nSupport.getText(locTextKey)); } if (locToolTipKey != null) { - b.setToolTipText(this.i18nSupport.getText(locToolTipKey)); + b.setToolTipText(Utils.formatLineBreaks(this.i18nSupport.getText(locToolTipKey))); } }; button.setData(POLYGLOT_WIDGET_FUNCTION_KEY, buttonFunction); @@ -160,7 +161,7 @@ public final class PolyglotPageServiceImpl implements PolyglotPageService { if (this.i18nSupport.hasText(locTooltipKey)) { tableColumn.setData(POLYGLOT_ITEM_TOOLTIP_DATA_KEY, locTooltipKey); - tableColumn.setToolTipText(this.i18nSupport.getText(locTooltipKey)); + tableColumn.setToolTipText(Utils.formatLineBreaks(this.i18nSupport.getText(locTooltipKey))); } } @@ -183,7 +184,7 @@ public final class PolyglotPageServiceImpl implements PolyglotPageService { if (this.i18nSupport.hasText(locTooltipKey)) { tabItem.setData(POLYGLOT_ITEM_TOOLTIP_DATA_KEY, locTooltipKey); - tabItem.setToolTipText(this.i18nSupport.getText(locTooltipKey)); + tabItem.setToolTipText(Utils.formatLineBreaks(this.i18nSupport.getText(locTooltipKey))); } } @@ -215,7 +216,7 @@ public final class PolyglotPageServiceImpl implements PolyglotPageService { label.setText(i18nSupport.getText(locTextKey)); } if (locToolTipKey != null) { - label.setToolTipText(i18nSupport.getText(locToolTipKey)); + label.setToolTipText(Utils.formatLineBreaks(i18nSupport.getText(locToolTipKey))); } }; } @@ -230,7 +231,7 @@ public final class PolyglotPageServiceImpl implements PolyglotPageService { group.setText(i18nSupport.getText(locTextKey)); } if (locToolTipKey != null) { - group.setToolTipText(i18nSupport.getText(locToolTipKey, StringUtils.EMPTY)); + group.setToolTipText(Utils.formatLineBreaks(i18nSupport.getText(locToolTipKey, StringUtils.EMPTY))); } }; } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/PageService.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/PageService.java index 889a1525..3d81e1d7 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/PageService.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/PageService.java @@ -34,6 +34,7 @@ import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.Page; import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.gbl.util.Tuple; import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; import ch.ethz.seb.sebserver.gui.form.FormBuilder; import ch.ethz.seb.sebserver.gui.service.ResourceService; @@ -41,6 +42,7 @@ import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; import ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService; import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys; +import ch.ethz.seb.sebserver.gui.service.page.event.ActionActivationEvent; import ch.ethz.seb.sebserver.gui.service.page.event.PageEvent; import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; import ch.ethz.seb.sebserver.gui.service.page.impl.PageState; @@ -150,6 +152,63 @@ public interface PageService { }; } + /** Use this to get an table selection action publisher that processes the action + * activation on table selection. + * + * @param pageContext the current PageContext + * @param actionDefinitions list of action definitions that activity should be toggled on table selection + * @return the selection publisher that handles this defines action activation on table selection */ + default Consumer> getSelectionPublisher( + final PageContext pageContext, + final ActionDefinition... actionDefinitions) { + + return rows -> { + firePageEvent( + new ActionActivationEvent(!rows.isEmpty(), actionDefinitions), + pageContext); + }; + } + + /** Use this to get an table selection action publisher that processes the action + * activation on table selection. + *

+ * This additional has the ability to define a entity activity action that is toggles in + * case of the selected entity + * + * @param toggle the base entity activity action definition + * @param activate the entity activation action definition + * @param deactivate the entity deactivation action definition + * @param pageContext the current PageContext + * @param actionDefinitions list of action definitions that activity should be toggled on table selection + * @return the selection publisher that handles this defines action activation on table selection */ + default Consumer> getSelectionPublisher( + final ActionDefinition toggle, + final ActionDefinition activate, + final ActionDefinition deactivate, + final PageContext pageContext, + final ActionDefinition... actionDefinitions) { + + return rows -> { + + if (!rows.isEmpty()) { + firePageEvent( + new ActionActivationEvent( + true, + new Tuple<>( + toggle, + rows.iterator().next().isActive() + ? deactivate + : activate), + actionDefinitions), + pageContext); + } else { + firePageEvent( + new ActionActivationEvent(false, actionDefinitions), + pageContext); + } + }; + } + /** Publishes a given PageEvent to the current page tree * This goes through the page-tree and collects all listeners the are listen to * the specified page event type. diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ComposerServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ComposerServiceImpl.java index e6e73877..75f02e0e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ComposerServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/ComposerServiceImpl.java @@ -40,7 +40,6 @@ public class ComposerServiceImpl implements ComposerService { private static final Logger log = LoggerFactory.getLogger(ComposerServiceImpl.class); - // TODO configurable private final Class loginPageType = DefaultLoginPage.class; private final Class mainPageType = DefaultMainPage.class; diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/table/ColumnDefinition.java b/src/main/java/ch/ethz/seb/sebserver/gui/table/ColumnDefinition.java index 3cf8305c..da4a52b6 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/table/ColumnDefinition.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/table/ColumnDefinition.java @@ -19,6 +19,8 @@ import ch.ethz.seb.sebserver.gui.table.TableFilter.CriteriaType; public final class ColumnDefinition { + private static final String TOOLTIP_TEXT_KEY_SUFFIX = ".tooltip"; + final String columnName; final LocTextKey displayName; final Function valueSupplier; @@ -55,7 +57,7 @@ public final class ColumnDefinition { this.columnName = columnName; this.displayName = displayName; - this.tooltip = tooltip; + this.tooltip = (tooltip != null) ? tooltip : new LocTextKey(displayName.name + TOOLTIP_TEXT_KEY_SUFFIX); this.widthProportion = widthProportion; this.valueSupplier = valueSupplier; this.filterAttribute = filterAttribute; diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/table/EntityTable.java b/src/main/java/ch/ethz/seb/sebserver/gui/table/EntityTable.java index d9789f9a..256ea49f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/table/EntityTable.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/table/EntityTable.java @@ -64,6 +64,8 @@ public class EntityTable { private static final Logger log = LoggerFactory.getLogger(EntityTable.class); + private static final LocTextKey DEFAULT_SORT_COLUMN_TOOLTIP_KEY = + new LocTextKey("sebserver.table.column.sort.default.tooltip"); private static final String COLUMN_DEFINITION = "COLUMN_DEFINITION"; private static final String TABLE_ROW_DATA = "TABLE_ROW_DATA"; private static final int HEADER_HEIGHT = 40; @@ -425,11 +427,23 @@ public class EntityTable { } private void createTableColumns() { + final String sortText = this.i18nSupport.getText(DEFAULT_SORT_COLUMN_TOOLTIP_KEY, ""); + for (final ColumnDefinition column : this.columns) { + + final LocTextKey _tooltip = column.getTooltip(); + final LocTextKey tooltip = (_tooltip != null && this.i18nSupport.hasText(_tooltip)) + ? (column.isSortable()) + ? new LocTextKey(_tooltip.name, sortText) + : new LocTextKey(_tooltip.name, "") + : (column.isSortable()) + ? DEFAULT_SORT_COLUMN_TOOLTIP_KEY + : null; + final TableColumn tableColumn = this.widgetFactory.tableColumnLocalized( this.table, column.displayName, - column.getTooltip()); + tooltip); tableColumn.addListener(SWT.Resize, this::adaptColumnWidthChange); tableColumn.setData(COLUMN_DEFINITION, column); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/FileUploadSelection.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/FileUploadSelection.java index 411986a6..0784f730 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/FileUploadSelection.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/FileUploadSelection.java @@ -31,6 +31,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ch.ethz.seb.sebserver.gbl.Constants; +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; @@ -82,7 +83,7 @@ public class FileUploadSelection extends Composite { this.fileUpload = new FileUpload(this, SWT.NONE); this.fileUpload.setImage(WidgetFactory.ImageIcon.IMPORT.getImage(parent.getDisplay())); this.fileUpload.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false)); - this.fileUpload.setToolTipText(this.i18nSupport.getText(PLEASE_SELECT_TEXT)); + this.fileUpload.setToolTipText(Utils.formatLineBreaks(this.i18nSupport.getText(PLEASE_SELECT_TEXT))); this.inputReceiver = new InputReceiver(); this.uploadHandler = new FileUploadHandler(this.inputReceiver); @@ -148,7 +149,7 @@ public class FileUploadSelection extends Composite { this.fileName.setText(this.i18nSupport.getText(PLEASE_SELECT_TEXT)); } if (!this.readonly) { - this.fileUpload.setToolTipText(this.i18nSupport.getText(PLEASE_SELECT_TEXT)); + this.fileUpload.setToolTipText(Utils.formatLineBreaks(this.i18nSupport.getText(PLEASE_SELECT_TEXT))); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/ImageUploadSelection.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/ImageUploadSelection.java index 1e546534..4a40feb4 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/ImageUploadSelection.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/ImageUploadSelection.java @@ -38,6 +38,7 @@ import org.eclipse.swt.widgets.Composite; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; import ch.ethz.seb.sebserver.gui.service.push.ServerPushContext; import ch.ethz.seb.sebserver.gui.service.push.ServerPushService; @@ -77,6 +78,7 @@ public final class ImageUploadSelection extends Composite { gridLayout.horizontalSpacing = 0; gridLayout.marginHeight = 0; gridLayout.marginWidth = 0; + gridLayout.marginLeft = 0; gridLayout.verticalSpacing = 0; super.setLayout(gridLayout); @@ -87,7 +89,9 @@ public final class ImageUploadSelection extends Composite { if (!readonly) { this.fileUpload = new FileUpload(this, SWT.NONE); this.fileUpload.setImage(WidgetFactory.ImageIcon.IMPORT.getImage(parent.getDisplay())); - this.fileUpload.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false)); + final GridData gridData = new GridData(SWT.LEFT, SWT.TOP, false, false); + gridData.horizontalIndent = 0; + this.fileUpload.setLayoutData(gridData); final FileUploadHandler uploadHandler = new FileUploadHandler(new ImageReceiver()); this.fileUpload.addListener(SWT.Selection, event -> { @@ -128,7 +132,7 @@ public final class ImageUploadSelection extends Composite { public void setSelectionText(final String text) { if (this.fileUpload != null) { - this.fileUpload.setToolTipText(text); + this.fileUpload.setToolTipText(Utils.formatLineBreaks(text)); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCheckbox.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCheckbox.java index cadc32d2..950330e6 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCheckbox.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCheckbox.java @@ -24,6 +24,7 @@ import org.eclipse.swt.widgets.Listener; import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.util.Tuple; +import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.gui.service.page.PageService; public final class MultiSelectionCheckbox extends Composite implements Selection { @@ -83,7 +84,7 @@ public final class MultiSelectionCheckbox extends Composite implements Selection .forEach(tuple -> { final Button button = this.checkboxes.get(tuple._1); if (button != null) { - button.setToolTipText(tuple._2); + button.setToolTipText(Utils.formatLineBreaks(tuple._2)); } }); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/RadioSelection.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/RadioSelection.java index f93b2304..08e1617b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/RadioSelection.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/RadioSelection.java @@ -21,6 +21,7 @@ import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Listener; import ch.ethz.seb.sebserver.gbl.util.Tuple; +import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.gui.service.page.PageService; public final class RadioSelection extends Composite implements Selection { @@ -80,7 +81,7 @@ public final class RadioSelection extends Composite implements Selection { .forEach(tuple -> { final Button button = this.radioButtons.get(tuple._1); if (button != null) { - button.setToolTipText(tuple._2); + button.setToolTipText(Utils.formatLineBreaks(tuple._2)); } }); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java index 0e01437a..3f78c8f6 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java @@ -50,6 +50,7 @@ import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.Threshold; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gbl.util.Tuple; +import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; @@ -74,6 +75,7 @@ public class WidgetFactory { public enum ImageIcon { MAXIMIZE("maximize.png"), MINIMIZE("minimize.png"), + MANDATORY("mandatory.png"), ADD("add.png"), REMOVE("remove.png"), ADD_BOX("add_box.png"), @@ -327,16 +329,6 @@ public class WidgetFactory { return label; } - public Label labelLocalized(final Composite parent, final LocTextKey locTextKey, final String defaultText) { - final Label label = new Label(parent, SWT.NONE); - if (this.i18nSupport.hasText(locTextKey)) { - this.polyglotPageService.injectI18n(label, locTextKey); - } else { - label.setText(defaultText); - } - return label; - } - public Label labelLocalized(final Composite parent, final LocTextKey locTextKey) { final Label label = new Label(parent, SWT.NONE); this.polyglotPageService.injectI18n(label, locTextKey); @@ -637,7 +629,7 @@ public class WidgetFactory { try { ss.applyNewMapping(itemsSupplier.get()); if (toolTipTextKey != null) { - ss.setToolTipText(this.i18nSupport.getText(toolTipTextKey)); + ss.setToolTipText(Utils.formatLineBreaks(this.i18nSupport.getText(toolTipTextKey))); } if (itemsToolTipSupplier != null) { ss.applyToolTipsForItems(itemsToolTipSupplier.get()); diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index 7658ab51..989988fe 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -9,7 +9,7 @@ sebserver.overall.about.markup= For security reasons it\'s recommended to disable this option if you don\'t use any plugin/Flash content. sebserver.examconfig.props.label.enableJavaScript=Enable JavaScript -sebserver.examconfig.props.label.enableJavaScript.tooltip=Enables JavaScript.\n Please note that most modern web-sites need JavaScript for full functionality. +sebserver.examconfig.props.label.enableJavaScript.tooltip=Enables JavaScript. Please note that most modern web-sites need JavaScript for full functionality. sebserver.examconfig.props.label.enableJava=Enable Java -sebserver.examconfig.props.label.enableJava.tooltip=Enables Java applets.\n Note: Only applets with the highest Java security level will run in SEB. +sebserver.examconfig.props.label.enableJava.tooltip=Enables Java applets. Note: Only applets with the highest Java security level will run in SEB. sebserver.examconfig.props.label.blockPopUpWindows=Block pop-up windows -sebserver.examconfig.props.label.blockPopUpWindows.tooltip=Disables pop-up windows\n (often advertisement) opened by JavaScript without an user action such as a button click. +sebserver.examconfig.props.label.blockPopUpWindows.tooltip=Disables pop-up windows (often advertisement) opened by JavaScript without an user action such as a button click. sebserver.examconfig.props.label.allowVideoCapture=Allow video capture (webcam) sebserver.examconfig.props.label.allowVideoCapture.tooltip=Allow web applications to access camera sebserver.examconfig.props.label.allowAudioCapture=Allow audio capture (microphone) sebserver.examconfig.props.label.allowAudioCapture.tooltip=Allow web applications to access microphone sebserver.examconfig.props.label.allowBrowsingBackForward=Allow navigating back/forward in exam -sebserver.examconfig.props.label.allowBrowsingBackForward.tooltip=Disabling browsing to previously visited pages may increase security,\n because browsing back might allow to leave an exam +sebserver.examconfig.props.label.allowBrowsingBackForward.tooltip=Disabling browsing to previously visited pages may increase security, because browsing back might allow to leave an exam sebserver.examconfig.props.label.newBrowserWindowNavigation=Allow navigating in additional windows sebserver.examconfig.props.label.browserWindowAllowReload=Allow reload exam sebserver.examconfig.props.label.browserWindowAllowReload.tooltip=Allow reload in the exam window with F5 reload button (if displayed) @@ -697,7 +704,7 @@ sebserver.examconfig.props.label.newBrowserWindowShowReloadWarning.tooltip=User sebserver.examconfig.props.label.removeBrowserProfile=Remove profile (Win) sebserver.examconfig.props.label.removeBrowserProfile.tooltip=Remove XULRunner browser profile (containing caches and also local storage) when quitting SEB sebserver.examconfig.props.label.removeLocalStorage=Disable local storage (Mac) -sebserver.examconfig.props.label.removeLocalStorage.tooltip=If your web application uses local storage, you have to be sure data is saved encrypted\n and removed when no longer needed as SEB doesn't remove local storage +sebserver.examconfig.props.label.removeLocalStorage.tooltip=If your web application uses local storage, you have to be sure data is saved encrypted and removed when no longer needed as SEB doesn't remove local storage sebserver.examconfig.props.label.browserUserAgent=Suffix to be added to any user agent sebserver.examconfig.props.group.userAgentDesktop=User agent for desktop mode @@ -705,7 +712,7 @@ sebserver.examconfig.props.label.browserUserAgentWinDesktopMode.0=Desktop defaul sebserver.examconfig.props.label.browserUserAgentWinDesktopMode.0.tooltip=Zoom whole web pages using Ctrl-Mousewheel (Win) sebserver.examconfig.props.label.browserUserAgentWinDesktopMode.1=Custom sebserver.examconfig.props.label.browserUserAgentWinDesktopMode.1.tooltip=Zoom only text on web pages using Ctrl-Mousewheel (Win) -sebserver.examconfig.props.label.browserUserAgentWinDesktopModeCustom.tooltip=Custom desktop user agent string\n(SEB appends its version number automatically) +sebserver.examconfig.props.label.browserUserAgentWinDesktopModeCustom.tooltip=Custom desktop user agent string(SEB appends its version number automatically) sebserver.examconfig.props.group.userAgentTouch=User agent for touch/table mode sebserver.examconfig.props.label.browserUserAgentWinTouchMode.0=Touch default @@ -719,28 +726,28 @@ sebserver.examconfig.props.label.browserUserAgentMac.1=Custom sebserver.examconfig.props.label.browserUserAgentMac.1.tooltip=Zoom only text on web pages using Ctrl-Mousewheel (Win) sebserver.examconfig.props.label.enableSebBrowser=Enable SEB with browser window -sebserver.examconfig.props.label.enableSebBrowser.tooltip=Disable this to start another application in kiosk mode\n(for example a virtual desktop infrastructure client) +sebserver.examconfig.props.label.enableSebBrowser.tooltip=Disable this to start another application in kiosk mode(for example a virtual desktop infrastructure client) sebserver.examconfig.props.label.browserWindowTitleSuffix=Suffix to be added to every browser window sebserver.examconfig.props.label.allowDownUploads=Allow downloading and uploading files (Mac) -sebserver.examconfig.props.label.allowDownUpload.tooltip=Usually to be used with permitted third party applications\n for which you want to provide files to be down-loaded. +sebserver.examconfig.props.label.allowDownUpload.tooltip=Usually to be used with permitted third party applications for which you want to provide files to be down-loaded. sebserver.examconfig.props.label.downloadDirectoryWin=Download directory (Win) sebserver.examconfig.props.label.downloadDirectoryOSX=Download directory (Mac) sebserver.examconfig.props.label.openDownloads=Open files after downloading (Mac) sebserver.examconfig.props.label.chooseFileToUploadPolicy=Choose file to upload (Mac) -sebserver.examconfig.props.label.chooseFileToUploadPolicy.tooltip=SEB can let users choose the file to upload or automatically use the same file which was down-loaded before.\nIf not found, a file requester or an error is presented depending on this setting. +sebserver.examconfig.props.label.chooseFileToUploadPolicy.tooltip=SEB can let users choose the file to upload or automatically use the same file which was down-loaded before.If not found, a file requester or an error is presented depending on this setting. sebserver.examconfig.props.label.chooseFileToUploadPolicy.0=manually with file requester sebserver.examconfig.props.label.chooseFileToUploadPolicy.1=by attempting to upload the same file downloaded before sebserver.examconfig.props.label.chooseFileToUploadPolicy.2=by only allowing to upload the same file downloaded before sebserver.examconfig.props.label.downloadPDFFiles=Download and open PDF files instead of displaying them inline (Mac) -sebserver.examconfig.props.label.downloadPDFFiles.tooltip=PDF files will not be displayed by SEB but downloaded and openend (if "Open files after downloading" is active!)\n by the application set in Finder (usually Preview or Adobe Acrobat). +sebserver.examconfig.props.label.downloadPDFFiles.tooltip=PDF files will not be displayed by SEB but downloaded and openend (if "Open files after downloading" is active!) by the application set in Finder (usually Preview or Adobe Acrobat). sebserver.examconfig.props.label.allowPDFPlugIn=Allow using Acrobat Reader PDF plugin (insecure! Mac only) -sebserver.examconfig.props.label.allowPDFPlugIn.tooltip=The Adobe Acrobat Reader browser plugin should only be used on secured managed Mac computers,\n at it allows limited access the file system and unlimited to cloud services +sebserver.examconfig.props.label.allowPDFPlugIn.tooltip=The Adobe Acrobat Reader browser plugin should only be used on secured managed Mac computers, at it allows limited access the file system and unlimited to cloud services sebserver.examconfig.props.label.downloadAndOpenSebConfig=Download and open SEB Config Files sebserver.examconfig.props.label.downloadAndOpenSebConfig.tooltip=Download and open .seb config files regardless if downloading and opening other file types is allowed. sebserver.examconfig.props.group.quitLink=Link to quit SEB after exam -sebserver.examconfig.props.label.quitURL=Place this quit link to the 'feedback' page displayed after an exam was successfully finished.\n Clicking that link will quit SEB without having to enter the quit password. +sebserver.examconfig.props.label.quitURL=Place this quit link to the 'feedback' page displayed after an exam was successfully finished. Clicking that link will quit SEB without having to enter the quit password. sebserver.examconfig.props.label.quitURLConfirm=Ask user to confirm quitting sebserver.examconfig.props.group.backToStart=Back to Start Button @@ -753,7 +760,7 @@ sebserver.examconfig.props.label.restartExamPasswordProtected=Protect back to st sebserver.examconfig.props.label.restartExamPasswordProtected.tooltip=The quit/restart password (if set) must be entered when the back to start button was pressed. sebserver.examconfig.props.label.allowSwitchToApplications=Allow switching to third party application (Mac) -sebserver.examconfig.props.label.allowSwitchToApplications.tooltip=Decreases security of the kiosk mode by allowing process switcher (Cmd+Tab).\n The blacked out background of SEB also doesn't cover some alerts and modal windows in this mode. +sebserver.examconfig.props.label.allowSwitchToApplications.tooltip=Decreases security of the kiosk mode by allowing process switcher (Cmd+Tab). The blacked out background of SEB also doesn't cover some alerts and modal windows in this mode. sebserver.examconfig.props.label.allowFlashFullscreen=Allow Flash to switch to fullscreen mode (Mac) sebserver.examconfig.props.label.permittedProcesses.add.tooltip=Add permitted process sebserver.examconfig.props.label.permittedProcesses.remove.tooltip=Remove selected permitted process @@ -766,11 +773,11 @@ sebserver.examconfig.props.label.permittedProcesses.os.tooltip=Indicates on whic sebserver.examconfig.props.label.permittedProcesses.os.0=OS X sebserver.examconfig.props.label.permittedProcesses.os.1=Win sebserver.examconfig.props.label.permittedProcesses.title=Title -sebserver.examconfig.props.label.permittedProcesses.title.tooltip=Application title which is displayed in the application chooser.\n Background processes don't have a title, because they can't be selected by users. +sebserver.examconfig.props.label.permittedProcesses.title.tooltip=Application title which is displayed in the application chooser. Background processes don't have a title, because they can't be selected by users. sebserver.examconfig.props.label.permittedProcesses.description=Description -sebserver.examconfig.props.label.permittedProcesses.description.tooltip=Optional, should explain what kind of process this is,\n because this might not be obvious only from the executable's name. +sebserver.examconfig.props.label.permittedProcesses.description.tooltip=Optional, should explain what kind of process this is, because this might not be obvious only from the executable's name. sebserver.examconfig.props.label.permittedProcesses.executable=Executable -sebserver.examconfig.props.label.permittedProcesses.executable.tooltip=File name of the executable, which should not contain any parts of a file system path,\n only the filename of the exe file (like calc.exe). +sebserver.examconfig.props.label.permittedProcesses.executable.tooltip=File name of the executable, which should not contain any parts of a file system path, only the filename of the exe file (like calc.exe). sebserver.examconfig.props.label.permittedProcesses.originalName=Original Name sebserver.examconfig.props.label.permittedProcesses.allowedExecutables=Window handling process sebserver.examconfig.props.label.permittedProcesses.path=Path @@ -780,13 +787,13 @@ sebserver.examconfig.props.label.permittedProcesses.arguments.argument=Argument sebserver.examconfig.props.label.permittedProcesses.arguments.addAction=Add new argument sebserver.examconfig.props.label.permittedProcesses.arguments.removeAction=Remove this argument sebserver.examconfig.props.label.permittedProcesses.identifier=Identifier -sebserver.examconfig.props.label.permittedProcesses.identifier.tooltip=(Sub) string in the title of the main window of a tricky third party application (Java, Acrobat etc.).\n Mac OS X: Bundle identifier of the process in reverse domain notation. +sebserver.examconfig.props.label.permittedProcesses.identifier.tooltip=(Sub) string in the title of the main window of a tricky third party application (Java, Acrobat etc.). Mac OS X: Bundle identifier of the process in reverse domain notation. sebserver.examconfig.props.label.permittedProcesses.iconInTaskbar=Icon in taskbar -sebserver.examconfig.props.label.permittedProcesses.iconInTaskbar.tooltip=Show icon of permitted application in task bar\n (not possible when 'run in background' is enabled). +sebserver.examconfig.props.label.permittedProcesses.iconInTaskbar.tooltip=Show icon of permitted application in task bar (not possible when 'run in background' is enabled). sebserver.examconfig.props.label.permittedProcesses.autostart=Autostart sebserver.examconfig.props.label.permittedProcesses.autostart.tooltip=Start the process automatically together with SEB. sebserver.examconfig.props.label.permittedProcesses.runInBackground=Allow running in background -sebserver.examconfig.props.label.permittedProcesses.runInBackground.tooltip=Allow the permitted process to already be running when SEB starts.\n Such a process can't have an icon in the task bar. +sebserver.examconfig.props.label.permittedProcesses.runInBackground.tooltip=Allow the permitted process to already be running when SEB starts. Such a process can't have an icon in the task bar. sebserver.examconfig.props.label.permittedProcesses.allowUserToChooseApp=Allow user to select location of application sebserver.examconfig.props.label.permittedProcesses.strongKill=Force quit (risk of data loss) sebserver.examconfig.props.label.permittedProcesses.strongKill.tooltip=Terminate process in a not-nice way, which may cause data loss if the application had unsaved data @@ -801,15 +808,15 @@ sebserver.examconfig.props.label.prohibitedProcesses.os=OS sebserver.examconfig.props.label.prohibitedProcesses.os.0=OS X sebserver.examconfig.props.label.prohibitedProcesses.os.1=Win sebserver.examconfig.props.label.prohibitedProcesses.description=Description -sebserver.examconfig.props.label.prohibitedProcesses.description.tooltip=Optional, to explain what kind of process this is,\n because this might not be obvious only from the executable's name. +sebserver.examconfig.props.label.prohibitedProcesses.description.tooltip=Optional, to explain what kind of process this is, because this might not be obvious only from the executable's name. sebserver.examconfig.props.label.prohibitedProcesses.executable=Executable -sebserver.examconfig.props.label.prohibitedProcesses.executable.tooltip=File name of the executable, which should not contain any parts of a file system path,\n only the filename of the exe file (like calc.exe). +sebserver.examconfig.props.label.prohibitedProcesses.executable.tooltip=File name of the executable, which should not contain any parts of a file system path, only the filename of the exe file (like calc.exe). sebserver.examconfig.props.label.prohibitedProcesses.originalName=Original Name sebserver.examconfig.props.label.prohibitedProcesses.originalName.tooltip=Original file name (optional) sebserver.examconfig.props.label.prohibitedProcesses.identifier=Identifier -sebserver.examconfig.props.label.prohibitedProcesses.identifier.tooltip=Title of the main window of a Java third party application.\n Mac OS X: Bundle identifier of the process in reverse domain notation. +sebserver.examconfig.props.label.prohibitedProcesses.identifier.tooltip=Title of the main window of a Java third party application. Mac OS X: Bundle identifier of the process in reverse domain notation. sebserver.examconfig.props.label.prohibitedProcesses.strongKill=Force quit (risk of data loss) -sebserver.examconfig.props.label.prohibitedProcesses.strongKill.tooltip=Terminate process in a not-nice way,\n which may cause data loss if the application had unsaved data +sebserver.examconfig.props.label.prohibitedProcesses.strongKill.tooltip=Terminate process in a not-nice way, which may cause data loss if the application had unsaved data sebserver.examconfig.props.group.urlFilter=Filter sebserver.examconfig.props.label.URLFilterEnable=Activate URL Filtering @@ -969,7 +976,7 @@ sebserver.examconfig.props.label.insideSebEnableLogOff.tooltip=Activates the but sebserver.examconfig.props.label.insideSebEnableShutDown=Enable Shut down sebserver.examconfig.props.label.insideSebEnableShutDown.tooltip=Activates the button "Shutdown" sebserver.examconfig.props.label.insideSebEnableEaseOfAccess=Enable Ease of Access -sebserver.examconfig.props.label.insideSebEnableEaseOfAccess.tooltip=Shows options when the button "Ease of Access" in the lower left corner is clicked,\nwhich offers help e.g. to visually or aurally handicapped persons, like the Magnifier Glass. +sebserver.examconfig.props.label.insideSebEnableEaseOfAccess.tooltip=Shows options when the button "Ease of Access" in the lower left corner is clicked,which offers help e.g. to visually or aurally handicapped persons, like the Magnifier Glass. sebserver.examconfig.props.label.insideSebEnableVmWareClientShade=Enable VMware Client Shade sebserver.examconfig.props.label.insideSebEnableVmWareClientShade.tooltip=Activates the "Shade" bar at the upper edge of a virtual desktop, if existent. If you're not using VMware, this setting doesn't have any effect. sebserver.examconfig.props.label.insideSebEnableNetworkConnectionSelector=Enable network connection selector diff --git a/src/main/resources/static/css/sebserver.css b/src/main/resources/static/css/sebserver.css index 4a2020ac..63430a45 100644 --- a/src/main/resources/static/css/sebserver.css +++ b/src/main/resources/static/css/sebserver.css @@ -577,6 +577,8 @@ FileUpload:pressed { border: none; border-radius: 0px; text-shadow: none; + margin: 0px 0px 0px 0px; + padding: 0px 0px 0px 0px; } @@ -778,15 +780,15 @@ TabItem:selected:hover:first { Widget-ToolTip { - padding: 10px 5px 10px 5px; - background-color: #82be1e; + padding: 10px 10px 10px 10px; + background-color: #D3D9DB; border: 1px solid #3C5A0F; - border-radius: 2px 2px 2px 2px; + border-radius: 1px 1px 1px 1px; color: #4a4a4a; opacity: 1; animation: fadeIn 200ms linear, fadeOut 600ms ease-out; box-shadow: 3px 4px 2px rgba(0, 0, 0, 0.3); - text-align: center; + text-align: left; } Widget-ToolTip-Pointer { diff --git a/src/main/resources/static/images/mandatory.png b/src/main/resources/static/images/mandatory.png new file mode 100644 index 0000000000000000000000000000000000000000..0d144ea86a24842c4b595ec87519fad77817c06b GIT binary patch literal 678 zcmV;X0$KfuP)EX>4Tx04R}tkv&MmKpe$iQ>8^(6zm}4kfAzR5EXHhDi*;)X)CnqU~=gfG-*gu zTpR`0f`cE6RRVO2K4+Pb8jWx?vG-5YKE{ zI_G`j2rEkp@j3ChK^G)`eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{004eTL_t(I%k9xI4nYABMbU#q6!xGJq7a3~9__#uv>H?@ zEs4$oEC2=JX_WH$iSplMy16eic~le