SEBSERV-30 IndicatorList

This commit is contained in:
anhefti 2019-03-27 11:44:28 +01:00
parent 100bf77bd4
commit 7ccacfcb73
40 changed files with 606 additions and 191 deletions

View file

@ -36,17 +36,17 @@ public final class Constants {
.forPattern(DEFAULT_DATE_TIME_FORMAT) .forPattern(DEFAULT_DATE_TIME_FORMAT)
.withZoneUTC(); .withZoneUTC();
/** Date-Time formatter without milliseconds using UTC time-zone. Pattern is yyyy-MM-dd HH:mm:ss */ // /** Date-Time formatter without milliseconds using UTC time-zone. Pattern is yyyy-MM-dd HH:mm:ss */
// TODO check if this works with DEFAULT_DATE_TIME_FORMAT // // TODO check if this works with DEFAULT_DATE_TIME_FORMAT
@Deprecated // @Deprecated
public static final DateTimeFormatter DATE_TIME_PATTERN_UTC_NO_MILLIS = DateTimeFormat // public static final DateTimeFormatter DATE_TIME_PATTERN_UTC_NO_MILLIS = DateTimeFormat
.forPattern("yyyy-MM-dd HH:mm:ss") // .forPattern("yyyy-MM-dd HH:mm:ss")
.withZoneUTC(); // .withZoneUTC();
//
/** Date-Time formatter with milliseconds using UTC time-zone. Pattern is yyyy-MM-dd HH:mm:ss.S */ // /** Date-Time formatter with milliseconds using UTC time-zone. Pattern is yyyy-MM-dd HH:mm:ss.S */
@Deprecated // @Deprecated
public static final DateTimeFormatter DATE_TIME_PATTERN_UTC_MILLIS = DateTimeFormat // public static final DateTimeFormatter DATE_TIME_PATTERN_UTC_MILLIS = DateTimeFormat
.forPattern("yyyy-MM-dd HH:mm:ss.S") // .forPattern("yyyy-MM-dd HH:mm:ss.S")
.withZoneUTC(); // .withZoneUTC();
} }

View file

@ -113,7 +113,7 @@ public final class Utils {
public static Result<Long> dateTimeStringToTimestamp(final String startTime) { public static Result<Long> dateTimeStringToTimestamp(final String startTime) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> {
return DateTime.parse(startTime, Constants.DATE_TIME_PATTERN_UTC_NO_MILLIS).getMillis(); return DateTime.parse(startTime, Constants.STANDARD_DATE_TIME_FORMATTER).getMillis();
}); });
} }
@ -143,11 +143,7 @@ public final class Utils {
return null; return null;
} }
if (dateString.contains(".")) { return DateTime.parse(dateString, Constants.STANDARD_DATE_TIME_FORMATTER);
return DateTime.parse(dateString, Constants.DATE_TIME_PATTERN_UTC_MILLIS);
} else {
return DateTime.parse(dateString, Constants.DATE_TIME_PATTERN_UTC_NO_MILLIS);
}
} }
public static Long toMilliSeconds(final String dateString) { public static Long toMilliSeconds(final String dateString) {

View file

@ -42,6 +42,7 @@ import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.page.action.Action; import ch.ethz.seb.sebserver.gui.service.page.action.Action;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent; import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.DeleteIndicator;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicators; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicators;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExam; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExam;
@ -113,6 +114,7 @@ public class ExamForm implements TemplateComposer {
// new PageContext with actual EntityKey // new PageContext with actual EntityKey
final PageContext formContext = pageContext.withEntityKey(exam.getEntityKey()); final PageContext formContext = pageContext.withEntityKey(exam.getEntityKey());
// the default page layout with title // the default page layout with title
final LocTextKey titleKey = new LocTextKey( final LocTextKey titleKey = new LocTextKey(
importFromQuizData importFromQuizData
@ -270,11 +272,13 @@ public class ExamForm implements TemplateComposer {
.publishIf(() -> modifyGrant) .publishIf(() -> modifyGrant)
.createAction(ActionDefinition.EXAM_INDICATOR_MODIFY_FROM_LIST) .createAction(ActionDefinition.EXAM_INDICATOR_MODIFY_FROM_LIST)
.withParentEntityKey(entityKey)
.withSelect(indicatorTable::getSelection, Action::applySingleSelection, emptySelectionTextKey) .withSelect(indicatorTable::getSelection, Action::applySingleSelection, emptySelectionTextKey)
.publishIf(() -> modifyGrant && indicatorTable.hasAnyContent()) .publishIf(() -> modifyGrant && indicatorTable.hasAnyContent())
.createAction(ActionDefinition.EXAM_INDICATOR_DELETE_FROM_LIST) .createAction(ActionDefinition.EXAM_INDICATOR_DELETE_FROM_LIST)
.withSelect(indicatorTable::getSelection, Action::applySingleSelection, emptySelectionTextKey) .withEntityKey(entityKey)
.withSelect(indicatorTable::getSelection, this::deleteSelectedIndicator, emptySelectionTextKey)
.publishIf(() -> modifyGrant && indicatorTable.hasAnyContent()); .publishIf(() -> modifyGrant && indicatorTable.hasAnyContent());
// TODO List of attached SEB Configurations // TODO List of attached SEB Configurations
@ -283,6 +287,15 @@ public class ExamForm implements TemplateComposer {
} }
private Action deleteSelectedIndicator(final Action action) {
final EntityKey indicatorKey = action.getSingleSelection();
this.resourceService.getRestService()
.getBuilder(DeleteIndicator.class)
.withURIVariable(API.PARAM_MODEL_ID, indicatorKey.modelId)
.call();
return action;
}
private Result<Exam> getExistingExam(final EntityKey entityKey, final RestService restService) { private Result<Exam> getExistingExam(final EntityKey entityKey, final RestService restService) {
return restService.getBuilder(GetExam.class) return restService.getBuilder(GetExam.class)
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
@ -330,7 +343,7 @@ public class ExamForm implements TemplateComposer {
if (importFromQuizData) { if (importFromQuizData) {
final PageContext pageContext = action.pageContext(); final PageContext pageContext = action.pageContext();
final Action activityHomeAction = pageContext.createAction(ActionDefinition.QUIZ_DISCOVERY_VIEW_LIST); final Action activityHomeAction = pageContext.createAction(ActionDefinition.QUIZ_DISCOVERY_VIEW_LIST);
action.pageContext().publishPageEvent(new ActionEvent(activityHomeAction, false)); action.pageContext().firePageEvent(new ActionEvent(activityHomeAction, false));
return activityHomeAction; return activityHomeAction;
} }

View file

@ -19,14 +19,12 @@ import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam; 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.Indicator;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
import ch.ethz.seb.sebserver.gui.form.FormBuilder; import ch.ethz.seb.sebserver.gui.form.FormBuilder;
import ch.ethz.seb.sebserver.gui.form.FormHandle; import ch.ethz.seb.sebserver.gui.form.FormHandle;
import ch.ethz.seb.sebserver.gui.form.PageFormService; import ch.ethz.seb.sebserver.gui.form.PageFormService;
import ch.ethz.seb.sebserver.gui.service.ResourceService; import ch.ethz.seb.sebserver.gui.service.ResourceService;
import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.PageContext; import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
@ -36,7 +34,6 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicator; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicator;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.NewIndicator; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.NewIndicator;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveIndicator; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveIndicator;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@Lazy @Lazy
@ -46,9 +43,6 @@ public class IndicatorForm implements TemplateComposer {
private static final Logger log = LoggerFactory.getLogger(IndicatorForm.class); private static final Logger log = LoggerFactory.getLogger(IndicatorForm.class);
private final static LocTextKey listTitleKey =
new LocTextKey("sebserver.exam.indicator.thresholds.list.title");
private final PageFormService pageFormService; private final PageFormService pageFormService;
private final ResourceService resourceService; private final ResourceService resourceService;
@ -62,12 +56,8 @@ public class IndicatorForm implements TemplateComposer {
@Override @Override
public void compose(final PageContext pageContext) { public void compose(final PageContext pageContext) {
final CurrentUser currentUser = this.resourceService.getCurrentUser();
final RestService restService = this.resourceService.getRestService(); final RestService restService = this.resourceService.getRestService();
final WidgetFactory widgetFactory = this.pageFormService.getWidgetFactory(); final WidgetFactory widgetFactory = this.pageFormService.getWidgetFactory();
final I18nSupport i18nSupport = this.resourceService.getI18nSupport();
final UserInfo user = currentUser.get();
final EntityKey entityKey = pageContext.getEntityKey(); final EntityKey entityKey = pageContext.getEntityKey();
final EntityKey parentEntityKey = pageContext.getParentEntityKey(); final EntityKey parentEntityKey = pageContext.getParentEntityKey();
final boolean isNew = entityKey == null; final boolean isNew = entityKey == null;
@ -134,9 +124,13 @@ public class IndicatorForm implements TemplateComposer {
(indicator.type != null) ? indicator.type.name() : null, (indicator.type != null) ? indicator.type.name() : null,
this.resourceService::indicatorTypeResources)) this.resourceService::indicatorTypeResources))
.addField(FormBuilder.colorSelection( .addField(FormBuilder.colorSelection(
Domain.INDICATOR.ATTR_TYPE, Domain.INDICATOR.ATTR_COLOR,
"sebserver.exam.indicator.form.color", "sebserver.exam.indicator.form.color",
indicator.defaultColor)) indicator.defaultColor))
.addField(FormBuilder.thresholdList(
Domain.THRESHOLD.REFERENCE_NAME,
"sebserver.exam.indicator.form.thresholds",
indicator.getThresholds()))
.buildFor((isNew) .buildFor((isNew)
? restService.getRestCall(NewIndicator.class) ? restService.getRestCall(NewIndicator.class)
: restService.getRestCall(SaveIndicator.class)); : restService.getRestCall(SaveIndicator.class));
@ -144,7 +138,7 @@ public class IndicatorForm implements TemplateComposer {
// propagate content actions to action-pane // propagate content actions to action-pane
formContext.clearEntityKeys() formContext.clearEntityKeys()
.createAction(ActionDefinition.EXAM_INDICATOR_SAVE) .createAction(ActionDefinition.EXAM_INDICATOR_SAVE)
.withParentEntityKey(parentEntityKey) .withEntityKey(parentEntityKey)
.withExec(formHandle::processFormSave) .withExec(formHandle::processFormSave)
.publishIf(() -> !isReadonly) .publishIf(() -> !isReadonly)

View file

@ -174,6 +174,7 @@ public class InstitutionForm implements TemplateComposer {
.publishIf(() -> writeGrant && isReadonly && !institution.isActive()) .publishIf(() -> writeGrant && isReadonly && !institution.isActive())
.createAction(ActionDefinition.INSTITUTION_SAVE) .createAction(ActionDefinition.INSTITUTION_SAVE)
.withEntityKey(entityKey)
.withExec(formHandle::processFormSave) .withExec(formHandle::processFormSave)
.publishIf(() -> !isReadonly) .publishIf(() -> !isReadonly)

View file

@ -200,6 +200,7 @@ public class LmsSetupForm implements TemplateComposer {
.publishIf(() -> writeGrant && readonly && institutionActive && !lmsSetup.isActive()) .publishIf(() -> writeGrant && readonly && institutionActive && !lmsSetup.isActive())
.createAction(ActionDefinition.LMS_SETUP_SAVE) .createAction(ActionDefinition.LMS_SETUP_SAVE)
.withEntityKey(entityKey)
.withExec(formHandle::processFormSave) .withExec(formHandle::processFormSave)
.publishIf(() -> !readonly) .publishIf(() -> !readonly)

View file

@ -66,13 +66,13 @@ public class LoginPage implements TemplateComposer {
final Label name = this.widgetFactory.labelLocalized(loginGroup, "sebserver.login.username"); final Label name = this.widgetFactory.labelLocalized(loginGroup, "sebserver.login.username");
name.setLayoutData(new GridData(300, -1)); name.setLayoutData(new GridData(300, -1));
name.setAlignment(SWT.BOTTOM); name.setAlignment(SWT.BOTTOM);
final Text loginName = new Text(loginGroup, SWT.LEFT | SWT.BORDER); final Text loginName = this.widgetFactory.textInput(loginGroup);
loginName.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false)); loginName.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false));
GridData gridData = new GridData(SWT.FILL, SWT.TOP, false, false); GridData gridData = new GridData(SWT.FILL, SWT.TOP, false, false);
gridData.verticalIndent = 10; gridData.verticalIndent = 10;
final Label pwd = this.widgetFactory.labelLocalized(loginGroup, "sebserver.login.pwd"); final Label pwd = this.widgetFactory.labelLocalized(loginGroup, "sebserver.login.pwd");
pwd.setLayoutData(gridData); pwd.setLayoutData(gridData);
final Text loginPassword = new Text(loginGroup, SWT.LEFT | SWT.PASSWORD | SWT.BORDER); final Text loginPassword = this.widgetFactory.passwordInput(loginGroup);
loginPassword.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false)); loginPassword.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false));
final Button button = this.widgetFactory.buttonLocalized(loginGroup, "sebserver.login.login"); final Button button = this.widgetFactory.buttonLocalized(loginGroup, "sebserver.login.login");

View file

@ -212,6 +212,7 @@ public class UserAccountForm implements TemplateComposer {
.publishIf(() -> writeGrant && readonly && institutionActive && !userAccount.isActive()) .publishIf(() -> writeGrant && readonly && institutionActive && !userAccount.isActive())
.createAction(ActionDefinition.USER_ACCOUNT_SAVE) .createAction(ActionDefinition.USER_ACCOUNT_SAVE)
.withEntityKey(entityKey)
.withExec(action -> { .withExec(action -> {
final Action postChanges = formHandle.processFormSave(action); final Action postChanges = formHandle.processFormSave(action);
if (ownAccount) { if (ownAccount) {

View file

@ -175,7 +175,15 @@ public class ActionPane implements TemplateComposer {
new ArrayList<>(this.actionTrees.entrySet()) new ArrayList<>(this.actionTrees.entrySet())
.stream() .stream()
.forEach(entry -> { .forEach(entry -> {
if (entry.getValue().isDisposed()) { final Control c = entry.getValue();
// of tree is already disposed.. remove it
if (c.isDisposed()) {
this.actionTrees.remove(entry.getKey());
}
// check access from current thread
try {
c.getBounds();
} catch (final Exception e) {
this.actionTrees.remove(entry.getKey()); this.actionTrees.remove(entry.getKey());
} }
}); });

View file

@ -162,7 +162,7 @@ public class ActivitiesPane implements TemplateComposer {
if (mainPageState.action == null) { if (mainPageState.action == null) {
mainPageState.action = getActivitySelection(navigation.getItem(0)); mainPageState.action = getActivitySelection(navigation.getItem(0));
} }
pageContext.publishPageEvent( pageContext.firePageEvent(
new ActionEvent(mainPageState.action, false)); new ActionEvent(mainPageState.action, false));
navigation.select(navigation.getItem(0)); navigation.select(navigation.getItem(0));
@ -177,7 +177,7 @@ public class ActivitiesPane implements TemplateComposer {
final Action action = getActivitySelection(treeItem); final Action action = getActivitySelection(treeItem);
if (mainPageState.action.definition != action.definition) { if (mainPageState.action.definition != action.definition) {
mainPageState.action = action; mainPageState.action = action;
composerCtx.publishPageEvent( composerCtx.firePageEvent(
new ActionEvent(action, true)); new ActionEvent(action, true));
} }
} }

View file

@ -10,7 +10,7 @@ package ch.ethz.seb.sebserver.gui.form;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;
public abstract class FieldBuilder { public abstract class FieldBuilder<T> {
int spanLabel = -1; int spanLabel = -1;
int spanInput = -1; int spanInput = -1;
int spanEmptyCell = -1; int spanEmptyCell = -1;
@ -22,55 +22,55 @@ public abstract class FieldBuilder {
final String name; final String name;
final String label; final String label;
final String value; final T value;
protected FieldBuilder(final String name, final String label, final String value) { protected FieldBuilder(final String name, final String label, final T value) {
this.name = name; this.name = name;
this.label = label; this.label = label;
this.value = value; this.value = value;
} }
public FieldBuilder withLabelSpan(final int span) { public FieldBuilder<T> withLabelSpan(final int span) {
this.spanLabel = span; this.spanLabel = span;
return this; return this;
} }
public FieldBuilder withInputSpan(final int span) { public FieldBuilder<T> withInputSpan(final int span) {
this.spanInput = span; this.spanInput = span;
return this; return this;
} }
public FieldBuilder withEmptyCellSpan(final int span) { public FieldBuilder<T> withEmptyCellSpan(final int span) {
this.spanEmptyCell = span; this.spanEmptyCell = span;
return this; return this;
} }
public FieldBuilder withEmptyCellSeparation(final boolean separation) { public FieldBuilder<T> withEmptyCellSeparation(final boolean separation) {
this.autoEmptyCellSeparation = separation; this.autoEmptyCellSeparation = separation;
return this; return this;
} }
public FieldBuilder withGroup(final String group) { public FieldBuilder<T> withGroup(final String group) {
this.group = group; this.group = group;
return this; return this;
} }
public FieldBuilder withCondition(final BooleanSupplier condition) { public FieldBuilder<T> withCondition(final BooleanSupplier condition) {
this.condition = condition; this.condition = condition;
return this; return this;
} }
public FieldBuilder readonly(final boolean readonly) { public FieldBuilder<T> readonly(final boolean readonly) {
this.readonly = readonly; this.readonly = readonly;
return this; return this;
} }
public FieldBuilder visibleIf(final boolean visible) { public FieldBuilder<T> visibleIf(final boolean visible) {
this.visible = visible; this.visible = visible;
return this; return this;
} }
public FieldBuilder readonlyIf(final BooleanSupplier readonly) { public FieldBuilder<T> readonlyIf(final BooleanSupplier readonly) {
this.readonly = readonly != null && readonly.getAsBoolean(); this.readonly = readonly != null && readonly.getAsBoolean();
return this; return this;
} }

View file

@ -8,6 +8,7 @@
package ch.ethz.seb.sebserver.gui.form; package ch.ethz.seb.sebserver.gui.form;
import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
@ -30,10 +31,12 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.JSONMapper; import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.Threshold;
import ch.ethz.seb.sebserver.gbl.util.Tuple; import ch.ethz.seb.sebserver.gbl.util.Tuple;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.FormBinding; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.FormBinding;
import ch.ethz.seb.sebserver.gui.widget.ImageUpload; import ch.ethz.seb.sebserver.gui.widget.ImageUpload;
import ch.ethz.seb.sebserver.gui.widget.Selection; import ch.ethz.seb.sebserver.gui.widget.Selection;
import ch.ethz.seb.sebserver.gui.widget.ThresholdList;
public final class Form implements FormBinding { public final class Form implements FormBinding {
@ -69,7 +72,7 @@ public final class Form implements FormBinding {
for (final Map.Entry<String, List<FormFieldAccessor>> entry : this.formFields.entrySet()) { for (final Map.Entry<String, List<FormFieldAccessor>> entry : this.formFields.entrySet()) {
entry.getValue() entry.getValue()
.stream() .stream()
.forEach(ffa -> appendFormUrlEncodedValue(buffer, entry.getKey(), ffa.getValue())); .forEach(ffa -> appendFormUrlEncodedValue(buffer, entry.getKey(), ffa.getStringValue()));
} }
return buffer.toString(); return buffer.toString();
@ -106,6 +109,10 @@ public final class Form implements FormBinding {
this.formFields.add(name, createAccessor(label, field)); this.formFields.add(name, createAccessor(label, field));
} }
public void putField(final String name, final Label label, final ThresholdList field) {
this.formFields.add(name, createAccessor(label, field));
}
public void putField( public void putField(
final String name, final String name,
final Label label, final Label label,
@ -174,7 +181,7 @@ public final class Form implements FormBinding {
for (final Map.Entry<String, List<FormFieldAccessor>> entry : this.formFields.entrySet()) { for (final Map.Entry<String, List<FormFieldAccessor>> entry : this.formFields.entrySet()) {
entry.getValue() entry.getValue()
.stream() .stream()
.filter(ffa -> StringUtils.isNoneBlank(ffa.getValue())) .filter(ffa -> StringUtils.isNoneBlank(ffa.getStringValue()))
.forEach(ffa -> ffa.putJsonValue(entry.getKey(), this.objectRoot)); .forEach(ffa -> ffa.putJsonValue(entry.getKey(), this.objectRoot));
} }
} }
@ -183,14 +190,12 @@ public final class Form implements FormBinding {
//@formatter:off //@formatter:off
private FormFieldAccessor createAccessor(final Label label, final Label field) { private FormFieldAccessor createAccessor(final Label label, final Label field) {
return new FormFieldAccessor(label, field) { return new FormFieldAccessor(label, field) {
@Override public String getValue() { return null; } @Override public String getStringValue() { return null; }
@Override public void setValue(final String value) { field.setText(value); }
}; };
} }
private FormFieldAccessor createAccessor(final Label label, final Text text) { private FormFieldAccessor createAccessor(final Label label, final Text text) {
return new FormFieldAccessor(label, text) { return new FormFieldAccessor(label, text) {
@Override public String getValue() { return text.getText(); } @Override public String getStringValue() { return text.getText(); }
@Override public void setValue(final String value) { text.setText(value); }
}; };
} }
private FormFieldAccessor createAccessor(final Label label, final Selection selection) { private FormFieldAccessor createAccessor(final Label label, final Selection selection) {
@ -207,14 +212,30 @@ public final class Form implements FormBinding {
final BiConsumer<Tuple<String>, ObjectNode> jsonValueAdapter) { final BiConsumer<Tuple<String>, ObjectNode> jsonValueAdapter) {
return new FormFieldAccessor(label, selection.adaptToControl(), jsonValueAdapter) { return new FormFieldAccessor(label, selection.adaptToControl(), jsonValueAdapter) {
@Override public String getValue() { return selection.getSelectionValue(); } @Override public String getStringValue() { return selection.getSelectionValue(); }
@Override public void setValue(final String value) { selection.select(value); } };
}
private FormFieldAccessor createAccessor(final Label label, final ThresholdList thresholdList) {
return new FormFieldAccessor(label, thresholdList) {
@Override public String getStringValue() {
return ThresholdListBuilder
.thresholdsToFormURLEncodedStringValue(thresholdList.getThresholds());
}
@Override
public void putJsonValue(final String key, final ObjectNode objectRoot) {
final Collection<Threshold> thresholds = thresholdList.getThresholds();
if (thresholds == null || thresholds.isEmpty()) {
return;
}
final ArrayNode array = Form.this.jsonMapper.valueToTree(thresholds);
objectRoot.putArray(key).addAll(array);
}
}; };
} }
private FormFieldAccessor createAccessor(final Label label, final ImageUpload imageUpload) { private FormFieldAccessor createAccessor(final Label label, final ImageUpload imageUpload) {
return new FormFieldAccessor(label, imageUpload) { return new FormFieldAccessor(label, imageUpload) {
@Override public String getValue() { return imageUpload.getImageBase64(); } @Override public String getStringValue() { return imageUpload.getImageBase64(); }
@Override public void setValue(final String value) { imageUpload.setImageBase64(value); }
}; };
} }
//@formatter:on //@formatter:on
@ -224,23 +245,38 @@ public final class Form implements FormBinding {
* Checks first if the value String is a comma separated list. If true, splits values * Checks first if the value String is a comma separated list. If true, splits values
* and adds every value within the same name mapping to the string buffer * and adds every value within the same name mapping to the string buffer
*/ */
private void appendFormUrlEncodedValue(final StringBuffer buffer, final String name, final String value) { private static void appendFormUrlEncodedValue(final StringBuffer buffer, final String name, final String value) {
if (StringUtils.isBlank(value)) {
return;
}
final String[] split = StringUtils.split(value, Constants.LIST_SEPARATOR_CHAR); final String[] split = StringUtils.split(value, Constants.LIST_SEPARATOR_CHAR);
if (split != null) { for (int i = 0; i < split.length; i++) {
for (int i = 0; i < split.length; i++) { if (StringUtils.isBlank(split[i])) {
if (StringUtils.isNoneBlank(split[i])) { continue;
if (buffer.length() > 0) { }
buffer.append(Constants.FORM_URL_ENCODED_SEPARATOR);
} if (buffer.length() > 0) {
buffer.append(name) buffer.append(Constants.FORM_URL_ENCODED_SEPARATOR);
.append(Constants.FORM_URL_ENCODED_NAME_VALUE_SEPARATOR) }
.append(split[i]);
} // check of the string value is a name-value pair. If true, use the specified name an value
// otherwise use the general name given within this method call and
if (split[i].contains(Constants.FORM_URL_ENCODED_NAME_VALUE_SEPARATOR)) {
final String[] nameValue = StringUtils.split(split[i], Constants.FORM_URL_ENCODED_NAME_VALUE_SEPARATOR);
buffer.append(nameValue[0])
.append(Constants.FORM_URL_ENCODED_NAME_VALUE_SEPARATOR)
.append(nameValue[1]);
} else {
buffer.append(name)
.append(Constants.FORM_URL_ENCODED_NAME_VALUE_SEPARATOR)
.append(split[i]);
} }
} }
} }
private static final void adaptCommaSeparatedStringToJsonArray(final Tuple<String> tuple, private static final void adaptCommaSeparatedStringToJsonArray(
final Tuple<String> tuple,
final ObjectNode jsonNode) { final ObjectNode jsonNode) {
if (StringUtils.isNoneBlank(tuple._2)) { if (StringUtils.isNoneBlank(tuple._2)) {
final ArrayNode arrayNode = jsonNode.putArray(tuple._1); final ArrayNode arrayNode = jsonNode.putArray(tuple._1);
@ -280,17 +316,15 @@ public final class Form implements FormBinding {
} }
} }
public abstract String getValue(); public abstract String getStringValue();
public abstract void setValue(String value);
public void setVisible(final boolean visible) { public void setVisible(final boolean visible) {
this.label.setVisible(visible); this.label.setVisible(visible);
this.control.setVisible(visible); this.control.setVisible(visible);
} }
public final void putJsonValue(final String key, final ObjectNode objectRoot) { public void putJsonValue(final String key, final ObjectNode objectRoot) {
this.jsonValueAdapter.accept(new Tuple<>(key, getValue()), objectRoot); this.jsonValueAdapter.accept(new Tuple<>(key, getStringValue()), objectRoot);
} }
public void setError(final String errorTooltip) { public void setError(final String errorTooltip) {

View file

@ -8,6 +8,7 @@
package ch.ethz.seb.sebserver.gui.form; package ch.ethz.seb.sebserver.gui.form;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;
import java.util.function.Supplier; import java.util.function.Supplier;
@ -26,6 +27,7 @@ import org.slf4j.LoggerFactory;
import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.JSONMapper; import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.Threshold;
import ch.ethz.seb.sebserver.gbl.util.Tuple; import ch.ethz.seb.sebserver.gbl.util.Tuple;
import ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService; 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.PageContext;
@ -39,6 +41,7 @@ public class FormBuilder {
private static final Logger log = LoggerFactory.getLogger(FormBuilder.class); private static final Logger log = LoggerFactory.getLogger(FormBuilder.class);
final WidgetFactory widgetFactory; final WidgetFactory widgetFactory;
final JSONMapper jsonMapper;
private final PolyglotPageService polyglotPageService; private final PolyglotPageService polyglotPageService;
public final PageContext pageContext; public final PageContext pageContext;
public final Composite formParent; public final Composite formParent;
@ -58,6 +61,7 @@ public class FormBuilder {
final int rows) { final int rows) {
this.widgetFactory = widgetFactory; this.widgetFactory = widgetFactory;
this.jsonMapper = jsonMapper;
this.polyglotPageService = polyglotPageService; this.polyglotPageService = polyglotPageService;
this.pageContext = pageContext; this.pageContext = pageContext;
this.form = new Form(jsonMapper); this.form = new Form(jsonMapper);
@ -145,7 +149,7 @@ public class FormBuilder {
return this; return this;
} }
public FormBuilder addField(final FieldBuilder template) { public FormBuilder addField(final FieldBuilder<?> template) {
if (template.condition == null || template.condition.getAsBoolean()) { if (template.condition == null || template.condition.getAsBoolean()) {
template.spanLabel = (template.spanLabel < 0) ? this.defaultSpanLabel : template.spanLabel; template.spanLabel = (template.spanLabel < 0) ? this.defaultSpanLabel : template.spanLabel;
template.spanInput = (template.spanInput < 0) ? this.defaultSpanInput : template.spanInput; template.spanInput = (template.spanInput < 0) ? this.defaultSpanInput : template.spanInput;
@ -224,6 +228,14 @@ public class FormBuilder {
return new SelectionFieldBuilder(Selection.Type.COLOR, name, label, value, null); return new SelectionFieldBuilder(Selection.Type.COLOR, name, label, value, null);
} }
public static ThresholdListBuilder thresholdList(
final String name,
final String label,
final Collection<Threshold> value) {
return new ThresholdListBuilder(name, label, value);
}
public static ImageUploadFieldBuilder imageUpload(final String name, final String label, final String value) { public static ImageUploadFieldBuilder imageUpload(final String name, final String label, final String value) {
return new ImageUploadFieldBuilder(name, label, value); return new ImageUploadFieldBuilder(name, label, value);
} }

View file

@ -82,17 +82,19 @@ public class FormHandle<T extends Entity> {
* go to the read-only-view of the specified form to indicate a successful form post * go to the read-only-view of the specified form to indicate a successful form post
* or stay within the edit-mode of the form and indicate errors or field validation messages * or stay within the edit-mode of the form and indicate errors or field validation messages
* to the user on error case. * to the user on error case.
* *
* @param postResult The form post result * @param postResult The form post result
* @param action the action that was applied with the form post * @param action the action that was applied with the form post
* @return the new Action that was used to stay on page or go the read-only-view of the form */ * @return the new Action that was used to stay on page or go the read-only-view of the form */
public Action handleFormPost(final Result<T> postResult, final Action action) { public Action handleFormPost(final Result<T> postResult, final Action action) {
return postResult return postResult
.map(result -> { .map(result -> {
final Action resultAction = action.createNew() Action resultAction = action.createNew()
.withAttribute(AttributeKeys.READ_ONLY, "true") .withAttribute(AttributeKeys.READ_ONLY, "true");
.withEntityKey(result.getEntityKey()); if (resultAction.getEntityKey() == null) {
action.pageContext().publishPageEvent(new ActionEvent(resultAction, false)); resultAction = resultAction.withEntityKey(result.getEntityKey());
}
action.pageContext().firePageEvent(new ActionEvent(resultAction, false));
return resultAction; return resultAction;
}) })
.onErrorDo(this::handleError) .onErrorDo(this::handleError)

View file

@ -15,7 +15,7 @@ import org.eclipse.swt.widgets.Label;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.widget.ImageUpload; import ch.ethz.seb.sebserver.gui.widget.ImageUpload;
public final class ImageUploadFieldBuilder extends FieldBuilder { public final class ImageUploadFieldBuilder extends FieldBuilder<String> {
ImageUploadFieldBuilder(final String name, final String label, final String value) { ImageUploadFieldBuilder(final String name, final String label, final String value) {
super(name, label, value); super(name, label, value);

View file

@ -30,7 +30,7 @@ import ch.ethz.seb.sebserver.gui.widget.Selection;
import ch.ethz.seb.sebserver.gui.widget.Selection.Type; import ch.ethz.seb.sebserver.gui.widget.Selection.Type;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant; import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
public final class SelectionFieldBuilder extends FieldBuilder { public final class SelectionFieldBuilder extends FieldBuilder<String> {
final Supplier<List<Tuple<String>>> itemsSupplier; final Supplier<List<Tuple<String>>> itemsSupplier;
Consumer<Form> selectionListener = null; Consumer<Form> selectionListener = null;

View file

@ -13,9 +13,10 @@ import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.Text;
public final class TextFieldBuilder extends FieldBuilder { public final class TextFieldBuilder extends FieldBuilder<String> {
boolean isPassword = false; boolean isPassword = false;
boolean isNumber = false;
TextFieldBuilder(final String name, final String label, final String value) { TextFieldBuilder(final String name, final String label, final String value) {
super(name, label, value); super(name, label, value);
@ -26,6 +27,11 @@ public final class TextFieldBuilder extends FieldBuilder {
return this; return this;
} }
public TextFieldBuilder asNumber() {
this.isNumber = true;
return this;
}
@Override @Override
void build(final FormBuilder builder) { void build(final FormBuilder builder) {
if (this.isPassword && builder.readonly) { if (this.isPassword && builder.readonly) {
@ -38,11 +44,11 @@ public final class TextFieldBuilder extends FieldBuilder {
builder.valueLabel(builder.formParent, this.value, this.spanInput)); builder.valueLabel(builder.formParent, this.value, this.spanInput));
builder.setFieldVisible(this.visible, this.name); builder.setFieldVisible(this.visible, this.name);
} else { } else {
final Text textInput = new Text(builder.formParent, (this.isPassword) final Text textInput = (this.isNumber)
? SWT.LEFT | SWT.BORDER | SWT.PASSWORD ? builder.widgetFactory.numberInput(builder.formParent, null)
: SWT.LEFT | SWT.BORDER); : builder.widgetFactory.textInput(builder.formParent, this.isPassword);
final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false, this.spanInput, 1); final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false, this.spanInput, 1);
gridData.heightHint = 15;
textInput.setLayoutData(gridData); textInput.setLayoutData(gridData);
if (this.value != null) { if (this.value != null) {
textInput.setText(this.value); textInput.setText(this.value);

View file

@ -0,0 +1,62 @@
/*
* 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.form;
import java.util.Collection;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Label;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.Threshold;
import ch.ethz.seb.sebserver.gui.widget.ThresholdList;
public class ThresholdListBuilder extends FieldBuilder<Collection<Threshold>> {
protected ThresholdListBuilder(
final String name,
final String label,
final Collection<Threshold> value) {
super(name, label, value);
}
@Override
void build(final FormBuilder builder) {
final Label lab = builder.labelLocalized(builder.formParent, this.label, this.spanLabel);
if (builder.readonly || this.readonly) {
} else {
final ThresholdList thresholdList = builder.widgetFactory.thresholdList(
builder.formParent,
this.value);
final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false, this.spanInput, 1);
thresholdList.setLayoutData(gridData);
builder.form.putField(this.name, lab, thresholdList);
builder.setFieldVisible(this.visible, this.name);
}
}
public static final String thresholdsToFormURLEncodedStringValue(final Collection<Threshold> thresholds) {
if (thresholds == null || thresholds.isEmpty()) {
return null;
}
return StringUtils.join(thresholds.stream()
.map(t -> String.valueOf(t.getValue()) + Constants.FORM_URL_ENCODED_NAME_VALUE_SEPARATOR + t.getColor())
.collect(Collectors.toList()),
Constants.LIST_SEPARATOR);
}
}

View file

@ -155,7 +155,7 @@ public interface PageContext {
* the specified page event type. * the specified page event type.
* *
* @param event the concrete PageEvent instance */ * @param event the concrete PageEvent instance */
<T extends PageEvent> void publishPageEvent(T event); <T extends PageEvent> void firePageEvent(T event);
Action createAction(ActionDefinition actionDefinition); Action createAction(ActionDefinition actionDefinition);

View file

@ -72,7 +72,7 @@ public final class Action implements Runnable {
try { try {
final Action executedAction = this.exec.apply(this); final Action executedAction = this.exec.apply(this);
this.pageContext.publishPageEvent(new ActionEvent(executedAction, false)); this.pageContext.firePageEvent(new ActionEvent(executedAction, false));
} catch (final PageMessageException pme) { } catch (final PageMessageException pme) {
Action.this.pageContext.publishPageMessage(pme); Action.this.pageContext.publishPageMessage(pme);
@ -183,7 +183,7 @@ public final class Action implements Runnable {
} }
public PageContext publish() { public PageContext publish() {
this.pageContext.publishPageEvent(new ActionPublishEvent(this)); this.pageContext.firePageEvent(new ActionPublishEvent(this));
return this.originalPageContext; return this.originalPageContext;
} }
@ -233,7 +233,7 @@ public final class Action implements Runnable {
if (action.getEntityKey() == null) { if (action.getEntityKey() == null) {
final PageContext pageContext = action.pageContext(); final PageContext pageContext = action.pageContext();
final Action activityHomeAction = pageContext.createAction(action.definition.activityAlias); final Action activityHomeAction = pageContext.createAction(action.definition.activityAlias);
action.pageContext.publishPageEvent(new ActionEvent(activityHomeAction, false)); action.pageContext.firePageEvent(new ActionEvent(activityHomeAction, false));
return activityHomeAction; return activityHomeAction;
} }

View file

@ -219,7 +219,7 @@ public class PageContextImpl implements PageContext {
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T extends PageEvent> void publishPageEvent(final T event) { public <T extends PageEvent> void firePageEvent(final T event) {
final Class<? extends PageEvent> typeClass = event.getClass(); final Class<? extends PageEvent> typeClass = event.getClass();
final List<PageEventListener<T>> listeners = new ArrayList<>(); final List<PageEventListener<T>> listeners = new ArrayList<>();
ComposerService.traversePageTree( ComposerService.traversePageTree(

View file

@ -52,6 +52,7 @@ public abstract class RestCall<T> {
GET_DEPENDENCIES, GET_DEPENDENCIES,
NEW, NEW,
SAVE, SAVE,
DELETE,
ACTIVATION_ACTIVATE, ACTIVATION_ACTIVATE,
ACTIVATION_DEACTIVATE ACTIVATION_DEACTIVATE
} }

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.type.TypeReference;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class DeleteIndicator extends RestCall<EntityProcessingReport> {
protected DeleteIndicator() {
super(new TypeKey<>(
CallType.DELETE,
EntityType.INDICATOR,
new TypeReference<EntityProcessingReport>() {
}),
HttpMethod.DELETE,
MediaType.APPLICATION_JSON_UTF8,
API.EXAM_INDICATOR_ENDPOINT + API.MODEL_ID_VAR_PATH_SEGMENT);
}
}

View file

@ -229,7 +229,7 @@ public class TableFilter<ROW extends Entity> {
@Override @Override
FilterComponent build(final Composite parent) { FilterComponent build(final Composite parent) {
this.textInput = new Text(parent, SWT.LEFT | SWT.BORDER); this.textInput = TableFilter.this.entityTable.widgetFactory.textInput(parent, false);
this.textInput.setLayoutData(this.rowData); this.textInput.setLayoutData(this.rowData);
return this; return this;
} }
@ -321,7 +321,7 @@ public class TableFilter<ROW extends Entity> {
.withMonthOfYear(this.selector.getMonth()) .withMonthOfYear(this.selector.getMonth())
.withDayOfMonth(this.selector.getDay()); .withDayOfMonth(this.selector.getDay());
return date.toString(Constants.DATE_TIME_PATTERN_UTC_NO_MILLIS); return date.toString(Constants.STANDARD_DATE_TIME_FORMATTER);
} else { } else {
return null; return null;
} }

View file

@ -44,6 +44,7 @@ public class ColorSelection extends Composite implements Selection {
gridLayout.marginLeft = 0; gridLayout.marginLeft = 0;
gridLayout.marginHeight = 0; gridLayout.marginHeight = 0;
gridLayout.marginWidth = 0; gridLayout.marginWidth = 0;
gridLayout.horizontalSpacing = 0;
setLayout(gridLayout); setLayout(gridLayout);
this.colorDialog = new ColorDialog(this.getShell(), SWT.NONE); this.colorDialog = new ColorDialog(this.getShell(), SWT.NONE);

View file

@ -47,7 +47,9 @@ public class MultiSelectionCombo extends Composite implements Selection {
private final List<Tuple<Control>> selectionControls = new ArrayList<>(); private final List<Tuple<Control>> selectionControls = new ArrayList<>();
private final List<Tuple<String>> selectedValues = new ArrayList<>(); private final List<Tuple<String>> selectedValues = new ArrayList<>();
private final Map<String, String> mapping = new HashMap<>(); private final Map<String, String> mapping = new HashMap<>();
//private final List<Tuple<String>> mapping = new ArrayList<>();
private final GridData comboCell;
private final GridData actionCell;
MultiSelectionCombo(final Composite parent, final WidgetFactory widgetFactory) { MultiSelectionCombo(final Composite parent, final WidgetFactory widgetFactory) {
super(parent, SWT.NONE); super(parent, SWT.NONE);
@ -57,22 +59,23 @@ public class MultiSelectionCombo extends Composite implements Selection {
gridLayout.marginLeft = 0; gridLayout.marginLeft = 0;
gridLayout.marginHeight = 0; gridLayout.marginHeight = 0;
gridLayout.marginWidth = 0; gridLayout.marginWidth = 0;
gridLayout.horizontalSpacing = 0;
setLayout(gridLayout); setLayout(gridLayout);
this.addListener(SWT.Resize, this::adaptColumnWidth); this.addListener(SWT.Resize, this::adaptColumnWidth);
this.combo = new Combo(this, SWT.NONE); this.combo = new Combo(this, SWT.NONE);
final GridData comboCell = new GridData(SWT.FILL, SWT.CENTER, true, false); this.comboCell = new GridData(SWT.FILL, SWT.CENTER, true, false);
this.combo.setLayoutData(comboCell); this.combo.setLayoutData(this.comboCell);
final Label imageButton = widgetFactory.imageButton( final Label imageButton = widgetFactory.imageButton(
ImageIcon.ADD_BOX, ImageIcon.ADD_BOX,
this, this,
new LocTextKey("Add"), new LocTextKey("Add"),
this::addComboSelection); this::addComboSelection);
final GridData actionCell = new GridData(SWT.LEFT, SWT.CENTER, true, false); this.actionCell = new GridData(SWT.LEFT, SWT.CENTER, true, false);
actionCell.widthHint = ACTION_COLUMN_WIDTH; this.actionCell.widthHint = ACTION_COLUMN_WIDTH;
imageButton.setLayoutData(actionCell); imageButton.setLayoutData(this.actionCell);
} }
@Override @Override
@ -205,8 +208,7 @@ public class MultiSelectionCombo extends Composite implements Selection {
private void adaptColumnWidth(final Event event) { private void adaptColumnWidth(final Event event) {
try { try {
final int currentTableWidth = this.getClientArea().width; final int currentTableWidth = this.getClientArea().width;
final GridData comboCell = (GridData) this.combo.getLayoutData(); this.comboCell.widthHint = currentTableWidth - ACTION_COLUMN_WIDTH;
comboCell.widthHint = currentTableWidth - ACTION_COLUMN_WIDTH;
this.layout(); this.layout();
} catch (final Exception e) { } catch (final Exception e) {
log.warn("Failed to adaptColumnWidth: ", e); log.warn("Failed to adaptColumnWidth: ", e);

View file

@ -1,6 +1,6 @@
/* /*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * 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/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
@ -8,6 +8,202 @@
package ch.ethz.seb.sebserver.gui.widget; package ch.ethz.seb.sebserver.gui.widget;
public class ThresholdList { import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
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.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.Threshold;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.widget.Selection.Type;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.CustomVariant;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon;
public class ThresholdList extends Composite {
private static final Logger log = LoggerFactory.getLogger(ThresholdList.class);
private static final long serialVersionUID = -2305091471607040280L;
private static final int ACTION_COLUMN_WIDTH = 20;
private static final LocTextKey valueTextKey = new LocTextKey("sebserver.exam.indicator.thresholds.list.value");
private static final LocTextKey colorTextKey = new LocTextKey("sebserver.exam.indicator.thresholds.list.color");
private static final LocTextKey addTextKey = new LocTextKey("sebserver.exam.indicator.thresholds.list.add");
private static final LocTextKey removeTextKey = new LocTextKey("sebserver.exam.indicator.thresholds.list.remove");
private final WidgetFactory widgetFactory;
private final List<Entry> thresholds = new ArrayList<>();
private final GridData valueCell;
private final GridData colorCell;
private final GridData actionCell;
ThresholdList(final Composite parent, final WidgetFactory widgetFactory) {
super(parent, SWT.NONE);
this.widgetFactory = widgetFactory;
final GridLayout gridLayout = new GridLayout(3, false);
gridLayout.verticalSpacing = 1;
gridLayout.marginLeft = 0;
gridLayout.marginHeight = 0;
gridLayout.marginWidth = 0;
gridLayout.horizontalSpacing = 0;
setLayout(gridLayout);
this.addListener(SWT.Resize, this::adaptColumnWidth);
final Label valueTitle = widgetFactory.labelLocalized(this, CustomVariant.TITLE_LABEL, valueTextKey);
this.valueCell = new GridData(SWT.FILL, SWT.CENTER, true, false);
valueTitle.setLayoutData(this.valueCell);
final Label colorTitle = widgetFactory.labelLocalized(this, CustomVariant.TITLE_LABEL, colorTextKey);
this.colorCell = new GridData(SWT.FILL, SWT.CENTER, true, false);
colorTitle.setLayoutData(this.colorCell);
final Label imageButton = widgetFactory.imageButton(
ImageIcon.ADD_BOX,
this,
addTextKey,
this::addThreshold);
this.actionCell = new GridData(SWT.LEFT, SWT.CENTER, true, false);
imageButton.setLayoutData(this.actionCell);
}
public void setThresholds(final Collection<Threshold> thresholds) {
clearList();
if (thresholds != null) {
thresholds
.stream()
.forEach(this::addThreshold);
}
}
public Collection<Threshold> getThresholds() {
removeInvalidListEntries();
return this.thresholds
.stream()
.map(entry -> new Threshold(entry.getValue(), entry.getColor()))
.collect(Collectors.toList());
}
private void removeInvalidListEntries() {
this.thresholds
.stream()
.filter(entry -> entry.getValue() == null || StringUtils.isBlank(entry.getColor()))
.collect(Collectors.toList())
.stream()
.forEach(entry -> removeThreshold(entry));
}
private void clearList() {
this.thresholds.stream()
.forEach(e -> e.dispose());
this.thresholds.clear();
}
private void addThreshold(final Event event) {
addThreshold((Threshold) null);
}
private void addThreshold(final Threshold threshold) {
final Text valueInput = this.widgetFactory.numberInput(this, s -> Double.parseDouble(s));
final GridData valueCell = new GridData(SWT.FILL, SWT.CENTER, true, false);
valueInput.setLayoutData(valueCell);
final Selection selector = this.widgetFactory.selectionLocalized(Type.COLOR, this, null);
final GridData selectorCell = new GridData(SWT.FILL, SWT.CENTER, true, false);
selectorCell.horizontalIndent = 2;
selector.adaptToControl().setLayoutData(selectorCell);
final Label imageButton = this.widgetFactory.imageButton(
ImageIcon.REMOVE_BOX,
this,
removeTextKey,
null);
final GridData actionCell = new GridData(SWT.FILL, SWT.CENTER, true, false);
imageButton.setLayoutData(actionCell);
if (threshold != null) {
if (threshold.value != null) {
valueInput.setText(threshold.value.toString());
}
if (threshold.color != null) {
selector.select(threshold.color);
}
}
final Entry entry = new Entry(valueInput, selector, imageButton);
this.thresholds.add(entry);
this.getParent().layout();
}
private void removeThreshold(final Entry entry) {
if (this.thresholds.remove(entry)) {
entry.dispose();
}
this.getParent().layout();
}
private void adaptColumnWidth(final Event event) {
try {
// TODO
final int currentTableWidth = this.getClientArea().width;
final int dynWidth = currentTableWidth - ACTION_COLUMN_WIDTH;
final int colWidth = dynWidth / 2;
this.valueCell.widthHint = colWidth;
this.colorCell.widthHint = colWidth;
this.layout();
} catch (final Exception e) {
log.warn("Failed to adaptColumnWidth: ", e);
}
}
private final class Entry {
final Text valueInput;
final Selection colorSelector;
final Label removeButton;
Entry(final Text valueInput, final Selection colorSelector, final Label removeButton) {
super();
this.valueInput = valueInput;
this.colorSelector = colorSelector;
this.removeButton = removeButton;
removeButton.addListener(SWT.MouseDown, event -> removeThreshold(this));
}
void dispose() {
this.valueInput.dispose();
this.colorSelector.adaptToControl().dispose();
this.removeButton.dispose();
}
Double getValue() {
if (this.valueInput == null || StringUtils.isBlank(this.valueInput.getText())) {
return null;
}
return Double.parseDouble(this.valueInput.getText());
}
String getColor() {
if (this.colorSelector == null) {
return null;
}
return this.colorSelector.getSelectionValue();
}
}
} }

View file

@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.gui.widget;
import static ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService.*; import static ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService.*;
import java.io.InputStream; import java.io.InputStream;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -30,6 +31,7 @@ import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem; import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem; import org.eclipse.swt.widgets.TreeItem;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -39,6 +41,7 @@ import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.model.Page; import ch.ethz.seb.sebserver.gbl.model.Page;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.Threshold;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gbl.util.Tuple; import ch.ethz.seb.sebserver.gbl.util.Tuple;
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
@ -242,6 +245,35 @@ public class WidgetFactory {
return labelLocalized; return labelLocalized;
} }
public Text textInput(final Composite content) {
return textInput(content, false);
}
public Text passwordInput(final Composite content) {
return textInput(content, true);
}
public Text textInput(final Composite content, final boolean password) {
return new Text(content, (password)
? SWT.LEFT | SWT.BORDER | SWT.PASSWORD
: SWT.LEFT | SWT.BORDER);
}
public Text numberInput(final Composite content, final Consumer<String> numberCheck) {
final Text numberInput = new Text(content, SWT.RIGHT | SWT.BORDER);
if (numberCheck != null) {
numberInput.addListener(SWT.Verify, event -> {
final String value = event.text;
try {
numberCheck.accept(value);
} catch (final Exception e) {
event.doit = false;
}
});
}
return numberInput;
}
public Tree treeLocalized(final Composite parent, final int style) { public Tree treeLocalized(final Composite parent, final int style) {
final Tree tree = new Tree(parent, style); final Tree tree = new Tree(parent, style);
this.injectI18n(tree); this.injectI18n(tree);
@ -343,6 +375,14 @@ public class WidgetFactory {
return selection; return selection;
} }
public ThresholdList thresholdList(final Composite parent, final Collection<Threshold> values) {
final ThresholdList thresholdList = new ThresholdList(parent, this);
if (values != null) {
thresholdList.setThresholds(values);
}
return thresholdList;
}
public ImageUpload imageUploadLocalized( public ImageUpload imageUploadLocalized(
final Composite parent, final Composite parent,
final LocTextKey locTextKey, final LocTextKey locTextKey,

View file

@ -21,17 +21,26 @@ import org.apache.ibatis.type.JdbcType;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.DateTimeZone; import org.joda.time.DateTimeZone;
import org.joda.time.LocalDateTime; import org.joda.time.LocalDateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import ch.ethz.seb.sebserver.gbl.Constants;
/** Joda DateTime resolver for MyBatis TIMESTAMP to DateTime conversion and vis versa. This is used to convert MyBatis /** Joda DateTime resolver for MyBatis TIMESTAMP to DateTime conversion and vis versa. This is used to convert MyBatis
* TIMESTAMP type to Joda-Time's DateTime * TIMESTAMP type to Joda-Time's DateTime
* *
* NOTE: The TIMESTAMP is always stored and read in UTC time-zone. */ * NOTE: The TIMESTAMP is always stored and read in UTC time-zone. */
public class JodaTimeTypeResolver extends BaseTypeHandler<DateTime> { public class JodaTimeTypeResolver extends BaseTypeHandler<DateTime> {
static final DateTimeFormatter DATE_TIME_PATTERN_UTC_NO_MILLIS = DateTimeFormat
.forPattern("yyyy-MM-dd HH:mm:ss")
.withZoneUTC();
/** Date-Time formatter with milliseconds using UTC time-zone. Pattern is yyyy-MM-dd HH:mm:ss.S */
static final DateTimeFormatter DATE_TIME_PATTERN_UTC_MILLIS = DateTimeFormat
.forPattern("yyyy-MM-dd HH:mm:ss.S")
.withZoneUTC();
private static final Logger log = LoggerFactory.getLogger(JodaTimeTypeResolver.class); private static final Logger log = LoggerFactory.getLogger(JodaTimeTypeResolver.class);
@Override @Override
@ -68,7 +77,7 @@ public class JodaTimeTypeResolver extends BaseTypeHandler<DateTime> {
return getDateTime(supplier.get()); return getDateTime(supplier.get());
} catch (final Exception e) { } catch (final Exception e) {
log.error("while trying to parse LocalDateTime; value: " + dateFormattedString + " format: " log.error("while trying to parse LocalDateTime; value: " + dateFormattedString + " format: "
+ Constants.DATE_TIME_PATTERN_UTC_NO_MILLIS, e); + DATE_TIME_PATTERN_UTC_NO_MILLIS, e);
throw new RuntimeException("Failed to parse date-time from SQL string: " + dateFormattedString, e); throw new RuntimeException("Failed to parse date-time from SQL string: " + dateFormattedString, e);
} }
} }
@ -87,7 +96,7 @@ public class JodaTimeTypeResolver extends BaseTypeHandler<DateTime> {
// NOTE: This create a DateTime in UTC time.zone with no time-zone-offset. // NOTE: This create a DateTime in UTC time.zone with no time-zone-offset.
final LocalDateTime localDateTime = LocalDateTime.parse( final LocalDateTime localDateTime = LocalDateTime.parse(
dateFormattedString, dateFormattedString,
Constants.DATE_TIME_PATTERN_UTC_NO_MILLIS); DATE_TIME_PATTERN_UTC_NO_MILLIS);
final DateTime dateTime = localDateTime.toDateTime(DateTimeZone.UTC); final DateTime dateTime = localDateTime.toDateTime(DateTimeZone.UTC);
return dateTime; return dateTime;

View file

@ -8,7 +8,6 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.dao; package ch.ethz.seb.sebserver.webservice.servicelayer.dao;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
@ -21,7 +20,7 @@ import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
import ch.ethz.seb.sebserver.gbl.model.institution.SebClientConfig; import ch.ethz.seb.sebserver.gbl.model.institution.SebClientConfig;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.JodaTimeTypeResolver; import ch.ethz.seb.sebserver.gbl.util.Utils;
/** A Map containing various filter criteria from a certain API request. /** A Map containing various filter criteria from a certain API request.
* This is used as a data object that can be used to collect API request parameter * This is used as a data object that can be used to collect API request parameter
@ -76,27 +75,15 @@ public class FilterMap extends POSTMapper {
} }
public DateTime getQuizFromTime() { public DateTime getQuizFromTime() {
final String value = getString(QuizData.FILTER_ATTR_START_TIME); return Utils.toDateTime(getString(QuizData.FILTER_ATTR_START_TIME));
if (StringUtils.isBlank(value)) {
return null;
}
return JodaTimeTypeResolver.getDateTime(value);
} }
public DateTime getExamFromTime() { public DateTime getExamFromTime() {
final String value = getString(Exam.FILTER_ATTR_FROM); return Utils.toDateTime(getString(Exam.FILTER_ATTR_FROM));
if (StringUtils.isBlank(value)) {
return null;
}
return JodaTimeTypeResolver.getDateTime(value);
} }
public DateTime getSebClientConfigFromTime() { public DateTime getSebClientConfigFromTime() {
final String value = getString(SebClientConfig.FILTER_ATTR_FROM); return Utils.toDateTime(getString(SebClientConfig.FILTER_ATTR_FROM));
if (StringUtils.isBlank(value)) {
return null;
}
return JodaTimeTypeResolver.getDateTime(value);
} }
public Long getLmsSetupId() { public Long getLmsSetupId() {

View file

@ -187,7 +187,7 @@ public class ExamDAOImpl implements ExamDAO {
BooleanUtils.toIntegerObject(exam.active)); BooleanUtils.toIntegerObject(exam.active));
this.examRecordMapper.updateByPrimaryKeySelective(newRecord); this.examRecordMapper.updateByPrimaryKeySelective(newRecord);
return this.examRecordMapper.selectByPrimaryKey(exam.id); return this.examRecordMapper.selectByPrimaryKey(examRecord.getId());
} }
final ExamRecord examRecord = new ExamRecord( final ExamRecord examRecord = new ExamRecord(

View file

@ -12,6 +12,7 @@ import static org.mybatis.dynamic.sql.SqlBuilder.*;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.function.Predicate; import java.util.function.Predicate;
@ -23,6 +24,7 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType;
import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
@ -217,6 +219,10 @@ public class IndicatorDAOImpl implements IndicatorDAO {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public Set<EntityKey> getDependencies(final BulkAction bulkAction) { public Set<EntityKey> getDependencies(final BulkAction bulkAction) {
if (bulkAction.type == BulkActionType.ACTIVATE || bulkAction.type == BulkActionType.DEACTIVATE) {
return Collections.emptySet();
}
final Set<EntityKey> examEntities = (bulkAction.sourceType == EntityType.EXAM) final Set<EntityKey> examEntities = (bulkAction.sourceType == EntityType.EXAM)
? bulkAction.sources ? bulkAction.sources
: bulkAction.extractKeys(EntityType.EXAM); : bulkAction.extractKeys(EntityType.EXAM);

View file

@ -57,25 +57,25 @@ final class MockupLmsAPITemplate implements LmsAPITemplate {
this.mockups = new ArrayList<>(); this.mockups = new ArrayList<>();
this.mockups.add(new QuizData( this.mockups.add(new QuizData(
"quiz1", institutionId, lmsSetupId, lmsType, "Demo Quiz 1", "Demo Quit Mockup", "quiz1", institutionId, lmsSetupId, lmsType, "Demo Quiz 1", "Demo Quit Mockup",
"2020-01-01T09:00:00", "2021-01-01T09:00:00", "http://lms.mockup.com/api/")); "2020-01-01T09:00:00Z", "2021-01-01T09:00:00Z", "http://lms.mockup.com/api/"));
this.mockups.add(new QuizData( this.mockups.add(new QuizData(
"quiz2", institutionId, lmsSetupId, lmsType, "Demo Quiz 2", "Demo Quit Mockup", "quiz2", institutionId, lmsSetupId, lmsType, "Demo Quiz 2", "Demo Quit Mockup",
"2020-01-01T09:00:00", "2021-01-01T09:00:00", "http://lms.mockup.com/api/")); "2020-01-01T09:00:00Z", "2021-01-01T09:00:00Z", "http://lms.mockup.com/api/"));
this.mockups.add(new QuizData( this.mockups.add(new QuizData(
"quiz3", institutionId, lmsSetupId, lmsType, "Demo Quiz 3", "Demo Quit Mockup", "quiz3", institutionId, lmsSetupId, lmsType, "Demo Quiz 3", "Demo Quit Mockup",
"2018-07-30T09:00:00", "2018-08-01T00:00:00", "http://lms.mockup.com/api/")); "2018-07-30T09:00:00Z", "2018-08-01T00:00:00Z", "http://lms.mockup.com/api/"));
this.mockups.add(new QuizData( this.mockups.add(new QuizData(
"quiz4", institutionId, lmsSetupId, lmsType, "Demo Quiz 4", "Demo Quit Mockup", "quiz4", institutionId, lmsSetupId, lmsType, "Demo Quiz 4", "Demo Quit Mockup",
"2018-01-01T00:00:00", "2019-01-01T00:00:00", "http://lms.mockup.com/api/")); "2018-01-01T00:00:00Z", "2019-01-01T00:00:00Z", "http://lms.mockup.com/api/"));
this.mockups.add(new QuizData( this.mockups.add(new QuizData(
"quiz5", institutionId, lmsSetupId, lmsType, "Demo Quiz 5", "Demo Quit Mockup", "quiz5", institutionId, lmsSetupId, lmsType, "Demo Quiz 5", "Demo Quit Mockup",
"2018-01-01T09:00:00", "2021-01-01T09:00:00", "http://lms.mockup.com/api/")); "2018-01-01T09:00:00Z", "2021-01-01T09:00:00Z", "http://lms.mockup.com/api/"));
this.mockups.add(new QuizData( this.mockups.add(new QuizData(
"quiz6", institutionId, lmsSetupId, lmsType, "Demo Quiz 6", "Demo Quit Mockup", "quiz6", institutionId, lmsSetupId, lmsType, "Demo Quiz 6", "Demo Quit Mockup",
"2018-01-01T09:00:00", "2021-01-01T09:00:00", "http://lms.mockup.com/api/")); "2018-01-01T09:00:00Z", "2021-01-01T09:00:00Z", "http://lms.mockup.com/api/"));
this.mockups.add(new QuizData( this.mockups.add(new QuizData(
"quiz7", institutionId, lmsSetupId, lmsType, "Demo Quiz 7", "Demo Quit Mockup", "quiz7", institutionId, lmsSetupId, lmsType, "Demo Quiz 7", "Demo Quit Mockup",
"2018-01-01T09:00:00", "2021-01-01T09:00:00", "http://lms.mockup.com/api/")); "2018-01-01T09:00:00Z", "2021-01-01T09:00:00Z", "http://lms.mockup.com/api/"));
} }
@Override @Override

View file

@ -109,34 +109,6 @@ public class UserAccountController extends ActivatableEntityController<UserInfo,
.collect(Collectors.toList())); .collect(Collectors.toList()));
} }
private UserInfo checkPasswordChange(final UserInfo info, final PasswordChange passwordChange) {
final SEBServerUser currentUser = this.userDAO.sebServerUserByUsername(this.authorization
.getUserService()
.getCurrentUser().getUsername())
.getOrThrow();
if (!this.userPasswordEncoder.matches(passwordChange.getPassword(), currentUser.getPassword())) {
throw new APIMessageException(APIMessage.fieldValidationError(
new FieldError(
"passwordChange",
PasswordChange.ATTR_NAME_PASSWORD,
"user:oldPassword:password.wrong")));
}
if (!passwordChange.newPasswordMatch()) {
throw new APIMessageException(APIMessage.fieldValidationError(
new FieldError(
"passwordChange",
PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD,
"user:retypedNewPassword:password.mismatch")));
}
return info;
}
@Override @Override
protected Result<UserMod> validForCreate(final UserMod userInfo) { protected Result<UserMod> validForCreate(final UserMod userInfo) {
return super.validForCreate(userInfo) return super.validForCreate(userInfo)
@ -219,4 +191,31 @@ public class UserAccountController extends ActivatableEntityController<UserInfo,
}); });
} }
private UserInfo checkPasswordChange(final UserInfo info, final PasswordChange passwordChange) {
final SEBServerUser currentUser = this.userDAO.sebServerUserByUsername(this.authorization
.getUserService()
.getCurrentUser().getUsername())
.getOrThrow();
if (!this.userPasswordEncoder.matches(passwordChange.getPassword(), currentUser.getPassword())) {
throw new APIMessageException(APIMessage.fieldValidationError(
new FieldError(
"passwordChange",
PasswordChange.ATTR_NAME_PASSWORD,
"user:oldPassword:password.wrong")));
}
if (!passwordChange.newPasswordMatch()) {
throw new APIMessageException(APIMessage.fieldValidationError(
new FieldError(
"passwordChange",
PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD,
"user:retypedNewPassword:password.mismatch")));
}
return info;
}
} }

View file

@ -169,7 +169,7 @@ sebserver.lmssetup.action.list.view=View LMS Setup
sebserver.lmssetup.action.list.modify=Edit LMS Setup sebserver.lmssetup.action.list.modify=Edit LMS Setup
sebserver.lmssetup.action.modify=Edit sebserver.lmssetup.action.modify=Edit
sebserver.lmssetup.action.test=Test Setup sebserver.lmssetup.action.test=Test Setup
sebserver.lmssetup.action.test.ok=Successfully connect to the LMSs course API sebserver.lmssetup.action.test.ok=Successfully connected to the course API
sebserver.lmssetup.action.test.tokenRequestError=The API access was denied: {0} sebserver.lmssetup.action.test.tokenRequestError=The API access was denied: {0}
sebserver.lmssetup.action.test.quizRequestError=Unable to request courses or quizzes from the course API of the LMS. {0} sebserver.lmssetup.action.test.quizRequestError=Unable to request courses or quizzes from the course API of the LMS. {0}
sebserver.lmssetup.action.test.missingParameter=There is one or more missing connection parameter.<br/>Please check the connection parameter for this LMS Setup sebserver.lmssetup.action.test.missingParameter=There is one or more missing connection parameter.<br/>Please check the connection parameter for this LMS Setup
@ -227,15 +227,15 @@ sebserver.exam.list.empty=No Exams has been found. Please adapt the filter or im
sebserver.exam.action.list=Exam sebserver.exam.action.list=Exam
sebserver.exam.action.list.view=View Exam sebserver.exam.action.list.view=View Exam
sebserver.exam.action.list.modify=Edit Exam sebserver.exam.action.list.modify=Edit Exam
sebserver.exam.action.modify=Edit sebserver.exam.action.modify=Edit Exam
sebserver.exam.action.import=Import From Quizzes sebserver.exam.action.import=Import From Quizzes
sebserver.exam.action.save=Save sebserver.exam.action.save=Save Exam
sebserver.exam.action.activate=Activate sebserver.exam.action.activate=Activate Exam
sebserver.exam.action.deactivate=Deactivate sebserver.exam.action.deactivate=Deactivate Exam
sebserver.exam.info.pleaseSelect=Please Select an Exam first sebserver.exam.info.pleaseSelect=Please Select an Exam first
sebserver.exam.form.title.import=Create From Quiz sebserver.exam.form.title.import=New Exam
sebserver.exam.form.title=Exam sebserver.exam.form.title=Exam
sebserver.exam.form.lmssetup=LMS Setup sebserver.exam.form.lmssetup=LMS Setup
sebserver.exam.form.quizid=Quiz Identifier sebserver.exam.form.quizid=Quiz Identifier
@ -266,7 +266,8 @@ sebserver.exam.indicator.type.ERROR_COUNT=Error Count
sebserver.exam.indicator.info.pleaseSelect=Please Select an Indicator first sebserver.exam.indicator.info.pleaseSelect=Please Select an Indicator first
sebserver.exam.indicator.action.list.new=New Indicator sebserver.exam.indicator.action.list.new=New Indicator
sebserver.exam.indicator.action.list.modify=Modify Indicator sebserver.exam.indicator.action.list.modify=Edit
sebserver.exam.indicator.action.list.delete=Delete
sebserver.exam.indicator.action.save=Save sebserver.exam.indicator.action.save=Save
sebserver.exam.indicator.form.title=Indicator sebserver.exam.indicator.form.title=Indicator
@ -274,6 +275,11 @@ sebserver.exam.indicator.form.title.new=New Indicator
sebserver.exam.indicator.form.exam=Exam sebserver.exam.indicator.form.exam=Exam
sebserver.exam.indicator.form.name=Name sebserver.exam.indicator.form.name=Name
sebserver.exam.indicator.form.type=Type sebserver.exam.indicator.form.type=Type
sebserver.exam.indicator.form.color=Color sebserver.exam.indicator.form.color=Default Color
sebserver.exam.indicator.form.thresholds=Thresholds
sebserver.exam.indicator.thresholds.list.title=Thresholds sebserver.exam.indicator.thresholds.list.title=Thresholds
sebserver.exam.indicator.thresholds.list.value=Value
sebserver.exam.indicator.thresholds.list.color=Color
sebserver.exam.indicator.thresholds.list.add=New Threshold
sebserver.exam.indicator.thresholds.list.remove=Delete Threshold

View file

@ -127,7 +127,7 @@ CREATE TABLE IF NOT EXISTS `indicator` (
`exam_id` BIGINT UNSIGNED NOT NULL, `exam_id` BIGINT UNSIGNED NOT NULL,
`type` VARCHAR(45) NOT NULL, `type` VARCHAR(45) NOT NULL,
`name` VARCHAR(45) NOT NULL, `name` VARCHAR(45) NOT NULL,
`color` VARCHAR(45) NOT NULL, `color` VARCHAR(45) NULL,
INDEX `indicator_exam_idx` (`exam_id` ASC), INDEX `indicator_exam_idx` (`exam_id` ASC),
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
CONSTRAINT `exam_ref` CONSTRAINT `exam_ref`
@ -357,7 +357,7 @@ CREATE TABLE IF NOT EXISTS `threshold` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`indicator_id` BIGINT UNSIGNED NOT NULL, `indicator_id` BIGINT UNSIGNED NOT NULL,
`value` DECIMAL(10,4) NOT NULL, `value` DECIMAL(10,4) NOT NULL,
`color` VARCHAR(45) NOT NULL, `color` VARCHAR(45) NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
INDEX `indicator_threshold_id_idx` (`indicator_id` ASC), INDEX `indicator_threshold_id_idx` (`indicator_id` ASC),
CONSTRAINT `indicator_threshold_id` CONSTRAINT `indicator_threshold_id`

View file

@ -140,7 +140,7 @@ CREATE TABLE IF NOT EXISTS `indicator` (
`exam_id` BIGINT UNSIGNED NOT NULL, `exam_id` BIGINT UNSIGNED NOT NULL,
`type` VARCHAR(45) NOT NULL, `type` VARCHAR(45) NOT NULL,
`name` VARCHAR(45) NOT NULL, `name` VARCHAR(45) NOT NULL,
`color` VARCHAR(45) NOT NULL, `color` VARCHAR(45) NULL,
INDEX `indicator_exam_idx` (`exam_id` ASC), INDEX `indicator_exam_idx` (`exam_id` ASC),
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
CONSTRAINT `exam_ref` CONSTRAINT `exam_ref`
@ -381,7 +381,7 @@ CREATE TABLE IF NOT EXISTS `threshold` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`indicator_id` BIGINT UNSIGNED NOT NULL, `indicator_id` BIGINT UNSIGNED NOT NULL,
`value` DECIMAL(10,4) NOT NULL, `value` DECIMAL(10,4) NOT NULL,
`color` VARCHAR(45) NOT NULL, `color` VARCHAR(45) NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
INDEX `indicator_threshold_id_idx` (`indicator_id` ASC), INDEX `indicator_threshold_id_idx` (`indicator_id` ASC),
CONSTRAINT `indicator_threshold_id` CONSTRAINT `indicator_threshold_id`

View file

@ -6,7 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
package ch.ethz.seb.sebserver.webservice.batis; package ch.ethz.seb.sebserver.webservice.datalayer.batis;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -23,9 +23,6 @@ import org.joda.time.DateTimeZone;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito; import org.mockito.Mockito;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.JodaTimeTypeResolver;
public class JodaTimeTypeResolverTest { public class JodaTimeTypeResolverTest {
@Test @Test
@ -53,7 +50,7 @@ public class JodaTimeTypeResolverTest {
final int columnIndex = 0; final int columnIndex = 0;
final DateTime pointInTime = new DateTime(0, DateTimeZone.UTC); final DateTime pointInTime = new DateTime(0, DateTimeZone.UTC);
final String pointInTimeString = pointInTime.toString(Constants.DATE_TIME_PATTERN_UTC_NO_MILLIS); final String pointInTimeString = pointInTime.toString(JodaTimeTypeResolver.DATE_TIME_PATTERN_UTC_NO_MILLIS);
assertEquals("1970-01-01 00:00:00", pointInTimeString); assertEquals("1970-01-01 00:00:00", pointInTimeString);
final JodaTimeTypeResolver jodaTimeTypeResolver = new JodaTimeTypeResolver(); final JodaTimeTypeResolver jodaTimeTypeResolver = new JodaTimeTypeResolver();
@ -64,12 +61,12 @@ public class JodaTimeTypeResolverTest {
DateTime nullableResult = jodaTimeTypeResolver.getNullableResult(resultSetMock, columnName); DateTime nullableResult = jodaTimeTypeResolver.getNullableResult(resultSetMock, columnName);
assertNotNull(nullableResult); assertNotNull(nullableResult);
assertEquals(pointInTimeString, nullableResult.toString(Constants.DATE_TIME_PATTERN_UTC_NO_MILLIS)); assertEquals(pointInTimeString, nullableResult.toString(JodaTimeTypeResolver.DATE_TIME_PATTERN_UTC_NO_MILLIS));
assertEquals(pointInTime, nullableResult); assertEquals(pointInTime, nullableResult);
nullableResult = jodaTimeTypeResolver.getNullableResult(resultSetMock, columnIndex); nullableResult = jodaTimeTypeResolver.getNullableResult(resultSetMock, columnIndex);
assertNotNull(nullableResult); assertNotNull(nullableResult);
assertEquals(pointInTimeString, nullableResult.toString(Constants.DATE_TIME_PATTERN_UTC_NO_MILLIS)); assertEquals(pointInTimeString, nullableResult.toString(JodaTimeTypeResolver.DATE_TIME_PATTERN_UTC_NO_MILLIS));
assertEquals(pointInTime, nullableResult); assertEquals(pointInTime, nullableResult);
} }
@ -78,7 +75,7 @@ public class JodaTimeTypeResolverTest {
final String columnName = "timestamp"; final String columnName = "timestamp";
final DateTime pointInTime = new DateTime(0, DateTimeZone.UTC); final DateTime pointInTime = new DateTime(0, DateTimeZone.UTC);
final String pointInTimeString = pointInTime.toString(Constants.DATE_TIME_PATTERN_UTC_MILLIS); final String pointInTimeString = pointInTime.toString(JodaTimeTypeResolver.DATE_TIME_PATTERN_UTC_MILLIS);
assertEquals("1970-01-01 00:00:00.0", pointInTimeString); assertEquals("1970-01-01 00:00:00.0", pointInTimeString);
final JodaTimeTypeResolver jodaTimeTypeResolver = new JodaTimeTypeResolver(); final JodaTimeTypeResolver jodaTimeTypeResolver = new JodaTimeTypeResolver();
@ -88,7 +85,8 @@ public class JodaTimeTypeResolverTest {
final DateTime nullableResult = jodaTimeTypeResolver.getNullableResult(resultSetMock, columnName); final DateTime nullableResult = jodaTimeTypeResolver.getNullableResult(resultSetMock, columnName);
assertNotNull(nullableResult); assertNotNull(nullableResult);
assertEquals("1970-01-01 00:00:00", nullableResult.toString(Constants.DATE_TIME_PATTERN_UTC_NO_MILLIS)); assertEquals("1970-01-01 00:00:00",
nullableResult.toString(JodaTimeTypeResolver.DATE_TIME_PATTERN_UTC_NO_MILLIS));
assertEquals(pointInTime, nullableResult); assertEquals(pointInTime, nullableResult);
} }

View file

@ -894,7 +894,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
@Test @Test
public void deactivateUserAccount() throws Exception { public void deactivateUserAccount() throws Exception {
final String timeNow = DateTime.now(DateTimeZone.UTC).toString(Constants.DATE_TIME_PATTERN_UTC_NO_MILLIS); final String timeNow = DateTime.now(DateTimeZone.UTC).toString(Constants.STANDARD_DATE_TIME_FORMATTER);
// only a SEB Administrator or an Institutional administrator should be able to deactivate a user-account // only a SEB Administrator or an Institutional administrator should be able to deactivate a user-account
final String examAdminToken = getExamAdmin1(); final String examAdminToken = getExamAdmin1();
this.mockMvc.perform(post(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/user4/inactive") this.mockMvc.perform(post(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/user4/inactive")
@ -957,7 +957,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
@Test @Test
public void activateUserAccount() throws Exception { public void activateUserAccount() throws Exception {
final String timeNow = DateTime.now(DateTimeZone.UTC).toString(Constants.DATE_TIME_PATTERN_UTC_NO_MILLIS); final String timeNow = DateTime.now(DateTimeZone.UTC).toString(Constants.STANDARD_DATE_TIME_FORMATTER);
// only a SEB Administrator or an Institutional administrator should be able to deactivate a user-account // only a SEB Administrator or an Institutional administrator should be able to deactivate a user-account
final String examAdminToken = getExamAdmin1(); final String examAdminToken = getExamAdmin1();
this.mockMvc.perform(post(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/user6/active") this.mockMvc.perform(post(this.endpoint + API.USER_ACCOUNT_ENDPOINT + "/user6/active")

View file

@ -72,12 +72,12 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTester {
@Test @Test
public void getAllAsSEBAdminInTimeRange() throws Exception { public void getAllAsSEBAdminInTimeRange() throws Exception {
final DateTime zeroDate = DateTime.parse("1970-01-01 00:00:00", Constants.DATE_TIME_PATTERN_UTC_NO_MILLIS); final DateTime zeroDate = DateTime.parse("1970-01-01T00:00:00Z", Constants.STANDARD_DATE_TIME_FORMATTER);
assertEquals("0", String.valueOf(zeroDate.getMillis())); assertEquals("0", String.valueOf(zeroDate.getMillis()));
final String sec2 = zeroDate.plus(1000).toString(Constants.DATE_TIME_PATTERN_UTC_NO_MILLIS); final String sec2 = zeroDate.plus(1000).toString(Constants.STANDARD_DATE_TIME_FORMATTER);
final String sec4 = zeroDate.plus(4000).toString(Constants.DATE_TIME_PATTERN_UTC_NO_MILLIS); final String sec4 = zeroDate.plus(4000).toString(Constants.STANDARD_DATE_TIME_FORMATTER);
final String sec5 = zeroDate.plus(5000).toString(Constants.DATE_TIME_PATTERN_UTC_NO_MILLIS); final String sec5 = zeroDate.plus(5000).toString(Constants.STANDARD_DATE_TIME_FORMATTER);
final String sec6 = zeroDate.plus(6000).toString(Constants.DATE_TIME_PATTERN_UTC_NO_MILLIS); final String sec6 = zeroDate.plus(6000).toString(Constants.STANDARD_DATE_TIME_FORMATTER);
final String token = getSebAdminAccess(); final String token = getSebAdminAccess();
Page<UserActivityLog> logs = this.jsonMapper.readValue( Page<UserActivityLog> logs = this.jsonMapper.readValue(