SEBSERV-29 SEBSERV-30 Indicator, ColorSelector, edx integration
This commit is contained in:
parent
584a900529
commit
100bf77bd4
38 changed files with 896 additions and 268 deletions
5
pom.xml
5
pom.xml
|
@ -289,6 +289,11 @@
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
<artifactId>commons-lang3</artifactId>
|
<artifactId>commons-lang3</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.cryptonode.jncryptor</groupId>
|
||||||
|
<artifactId>jncryptor</artifactId>
|
||||||
|
<version>1.2.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Testing -->
|
<!-- Testing -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
@ -28,15 +28,23 @@ public final class Constants {
|
||||||
public static final String FORM_URL_ENCODED_SEPARATOR = "&";
|
public static final String FORM_URL_ENCODED_SEPARATOR = "&";
|
||||||
public static final String FORM_URL_ENCODED_NAME_VALUE_SEPARATOR = "=";
|
public static final String FORM_URL_ENCODED_NAME_VALUE_SEPARATOR = "=";
|
||||||
|
|
||||||
|
public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
|
||||||
public static final String DEFAULT_DISPLAY_DATE_FORMAT = "MM-dd-yyy HH:mm";
|
public static final String DEFAULT_DISPLAY_DATE_FORMAT = "MM-dd-yyy HH:mm";
|
||||||
public static final String TIME_ZONE_OFFSET_TAIL_FORMAT = "|ZZ";
|
public static final String TIME_ZONE_OFFSET_TAIL_FORMAT = "|ZZ";
|
||||||
|
|
||||||
|
public static final DateTimeFormatter STANDARD_DATE_TIME_FORMATTER = DateTimeFormat
|
||||||
|
.forPattern(DEFAULT_DATE_TIME_FORMAT)
|
||||||
|
.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
|
||||||
|
@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
|
||||||
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();
|
||||||
|
|
|
@ -20,10 +20,10 @@ public final class API {
|
||||||
public static final String PARAM_INSTITUTION_ID = "institutionId";
|
public static final String PARAM_INSTITUTION_ID = "institutionId";
|
||||||
public static final String PARAM_MODEL_ID = "modelId";
|
public static final String PARAM_MODEL_ID = "modelId";
|
||||||
public static final String PARAM_MODEL_ID_LIST = "modelIds";
|
public static final String PARAM_MODEL_ID_LIST = "modelIds";
|
||||||
|
public static final String PARAM_PARENT_MODEL_ID = "parentModelId";
|
||||||
public static final String PARAM_ENTITY_TYPE = "entityType";
|
public static final String PARAM_ENTITY_TYPE = "entityType";
|
||||||
public static final String PARAM_BULK_ACTION_TYPE = "bulkActionType";
|
public static final String PARAM_BULK_ACTION_TYPE = "bulkActionType";
|
||||||
public static final String PARAM_LMS_SETUP_ID = "lmsSetupId";
|
public static final String PARAM_LMS_SETUP_ID = "lmsSetupId";
|
||||||
public static final String PARAM_EXAM_ID = "examId";
|
|
||||||
|
|
||||||
public static final String INSTITUTION_VAR_PATH_SEGMENT = "/{" + PARAM_INSTITUTION_ID + "}";
|
public static final String INSTITUTION_VAR_PATH_SEGMENT = "/{" + PARAM_INSTITUTION_ID + "}";
|
||||||
public static final String MODEL_ID_VAR_PATH_SEGMENT = "/{" + PARAM_MODEL_ID + "}";
|
public static final String MODEL_ID_VAR_PATH_SEGMENT = "/{" + PARAM_MODEL_ID + "}";
|
||||||
|
@ -54,7 +54,7 @@ public final class API {
|
||||||
|
|
||||||
public static final String EXAM_ADMINISTRATION_ENDPOINT = "/exam";
|
public static final String EXAM_ADMINISTRATION_ENDPOINT = "/exam";
|
||||||
|
|
||||||
public static final String EXAM_INDICATOR_ENDPOINT = "/exam/indicator";
|
public static final String EXAM_INDICATOR_ENDPOINT = "/indicator";
|
||||||
|
|
||||||
public static final String USER_ACTIVITY_LOG_ENDPOINT = "/useractivity";
|
public static final String USER_ACTIVITY_LOG_ENDPOINT = "/useractivity";
|
||||||
|
|
||||||
|
@ -75,4 +75,20 @@ public final class API {
|
||||||
public static final String PATH_VAR_ACTIVE = MODEL_ID_VAR_PATH_SEGMENT + ACTIVE_PATH_SEGMENT;
|
public static final String PATH_VAR_ACTIVE = MODEL_ID_VAR_PATH_SEGMENT + ACTIVE_PATH_SEGMENT;
|
||||||
public static final String PATH_VAR_INACTIVE = MODEL_ID_VAR_PATH_SEGMENT + INACTIVE_PATH_SEGMENT;
|
public static final String PATH_VAR_INACTIVE = MODEL_ID_VAR_PATH_SEGMENT + INACTIVE_PATH_SEGMENT;
|
||||||
|
|
||||||
|
// *************************
|
||||||
|
// ** Exam API
|
||||||
|
// *************************
|
||||||
|
|
||||||
|
public static final String EXAM_API_PARAM_EXAM_ID = "examId";
|
||||||
|
|
||||||
|
public static final String EXAM_API_SEB_CONNECTION_TOKEN = "seb-connection-token";
|
||||||
|
|
||||||
|
public static final String EXAM_API_HANDSHAKE_ENDPOINT = "/handshake";
|
||||||
|
|
||||||
|
public static final String EXAM_API_CONFIGURATION_REQUEST_ENDPOINT = "/examconfig";
|
||||||
|
|
||||||
|
public static final String EXAM_API_PING_ENDPOINT = "/sebping";
|
||||||
|
|
||||||
|
public static final String EXAM_API_EVENT_ENDPOINT = "/sebevent";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,12 +48,12 @@ public final class Indicator implements GrantEntity {
|
||||||
public final Long examId;
|
public final Long examId;
|
||||||
|
|
||||||
@JsonProperty(INDICATOR.ATTR_NAME)
|
@JsonProperty(INDICATOR.ATTR_NAME)
|
||||||
@NotNull
|
@NotNull(message = "indicator:name:notNull")
|
||||||
@Size(min = 3, max = 255, message = "indicator:name:size:{min}:{max}:${validatedValue}")
|
@Size(min = 3, max = 255, message = "indicator:name:size:{min}:{max}:${validatedValue}")
|
||||||
public final String name;
|
public final String name;
|
||||||
|
|
||||||
@JsonProperty(INDICATOR.ATTR_TYPE)
|
@JsonProperty(INDICATOR.ATTR_TYPE)
|
||||||
@NotNull
|
@NotNull(message = "indicator:type:notNull")
|
||||||
public final IndicatorType type;
|
public final IndicatorType type;
|
||||||
|
|
||||||
@JsonProperty(INDICATOR.ATTR_COLOR)
|
@JsonProperty(INDICATOR.ATTR_COLOR)
|
||||||
|
@ -62,12 +62,14 @@ public final class Indicator implements GrantEntity {
|
||||||
@JsonProperty(THRESHOLD.REFERENCE_NAME)
|
@JsonProperty(THRESHOLD.REFERENCE_NAME)
|
||||||
public final List<Threshold> thresholds;
|
public final List<Threshold> thresholds;
|
||||||
|
|
||||||
|
public final String examOwner;
|
||||||
|
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
public Indicator(
|
public Indicator(
|
||||||
@JsonProperty(INDICATOR.ATTR_ID) final Long id,
|
@JsonProperty(INDICATOR.ATTR_ID) final Long id,
|
||||||
@JsonProperty(EXAM.ATTR_INSTITUTION_ID) final Long institutionId,
|
@JsonProperty(EXAM.ATTR_INSTITUTION_ID) final Long institutionId,
|
||||||
@JsonProperty(EXAM.ATTR_OWNER) final String owner,
|
|
||||||
@JsonProperty(INDICATOR.ATTR_EXAM_ID) final Long examId,
|
@JsonProperty(INDICATOR.ATTR_EXAM_ID) final Long examId,
|
||||||
|
@JsonProperty(EXAM.ATTR_OWNER) final String examOwner,
|
||||||
@JsonProperty(INDICATOR.ATTR_NAME) final String name,
|
@JsonProperty(INDICATOR.ATTR_NAME) final String name,
|
||||||
@JsonProperty(INDICATOR.ATTR_TYPE) final IndicatorType type,
|
@JsonProperty(INDICATOR.ATTR_TYPE) final IndicatorType type,
|
||||||
@JsonProperty(INDICATOR.ATTR_COLOR) final String defaultColor,
|
@JsonProperty(INDICATOR.ATTR_COLOR) final String defaultColor,
|
||||||
|
@ -76,6 +78,7 @@ public final class Indicator implements GrantEntity {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.institutionId = institutionId;
|
this.institutionId = institutionId;
|
||||||
this.examId = examId;
|
this.examId = examId;
|
||||||
|
this.examOwner = examOwner;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.defaultColor = defaultColor;
|
this.defaultColor = defaultColor;
|
||||||
|
@ -86,6 +89,7 @@ public final class Indicator implements GrantEntity {
|
||||||
this.id = null;
|
this.id = null;
|
||||||
this.institutionId = exam.institutionId;
|
this.institutionId = exam.institutionId;
|
||||||
this.examId = exam.id;
|
this.examId = exam.id;
|
||||||
|
this.examOwner = exam.owner;
|
||||||
this.name = postParams.getString(Domain.INDICATOR.ATTR_NAME);
|
this.name = postParams.getString(Domain.INDICATOR.ATTR_NAME);
|
||||||
this.type = postParams.getEnum(Domain.INDICATOR.ATTR_TYPE, IndicatorType.class);
|
this.type = postParams.getEnum(Domain.INDICATOR.ATTR_TYPE, IndicatorType.class);
|
||||||
this.defaultColor = postParams.getString(Domain.INDICATOR.ATTR_COLOR);
|
this.defaultColor = postParams.getString(Domain.INDICATOR.ATTR_COLOR);
|
||||||
|
@ -119,7 +123,7 @@ public final class Indicator implements GrantEntity {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getOwnerId() {
|
public String getOwnerId() {
|
||||||
return null;
|
return this.examOwner;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getExamId() {
|
public Long getExamId() {
|
||||||
|
@ -145,14 +149,18 @@ public final class Indicator implements GrantEntity {
|
||||||
+ this.defaultColor + ", thresholds=" + this.thresholds + "]";
|
+ this.defaultColor + ", thresholds=" + this.thresholds + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Indicator createNew(final Long institutionId, final Exam exam) {
|
||||||
|
return new Indicator(null, institutionId, exam.id, exam.owner, null, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
public static final class Threshold {
|
public static final class Threshold {
|
||||||
|
|
||||||
@JsonProperty(THRESHOLD.ATTR_ID)
|
// @JsonProperty(THRESHOLD.ATTR_ID)
|
||||||
public final Long id;
|
// public final Long id;
|
||||||
|
//
|
||||||
@JsonProperty(THRESHOLD.ATTR_INDICATOR_ID)
|
// @JsonProperty(THRESHOLD.ATTR_INDICATOR_ID)
|
||||||
@NotNull
|
// @NotNull
|
||||||
public final Long indicatorId;
|
// public final Long indicatorId;
|
||||||
|
|
||||||
@JsonProperty(THRESHOLD.ATTR_VALUE)
|
@JsonProperty(THRESHOLD.ATTR_VALUE)
|
||||||
@NotNull
|
@NotNull
|
||||||
|
@ -163,24 +171,24 @@ public final class Indicator implements GrantEntity {
|
||||||
|
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
public Threshold(
|
public Threshold(
|
||||||
@JsonProperty(THRESHOLD.ATTR_ID) final Long id,
|
// @JsonProperty(THRESHOLD.ATTR_ID) final Long id,
|
||||||
@JsonProperty(THRESHOLD.ATTR_INDICATOR_ID) final Long indicatorId,
|
// @JsonProperty(THRESHOLD.ATTR_INDICATOR_ID) final Long indicatorId,
|
||||||
@JsonProperty(THRESHOLD.ATTR_VALUE) final Double value,
|
@JsonProperty(THRESHOLD.ATTR_VALUE) final Double value,
|
||||||
@JsonProperty(THRESHOLD.ATTR_COLOR) final String color) {
|
@JsonProperty(THRESHOLD.ATTR_COLOR) final String color) {
|
||||||
|
|
||||||
this.id = id;
|
// this.id = id;
|
||||||
this.indicatorId = indicatorId;
|
// this.indicatorId = indicatorId;
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.color = color;
|
this.color = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getId() {
|
// public Long getId() {
|
||||||
return this.id;
|
// return this.id;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
public Long getIndicatorId() {
|
// public Long getIndicatorId() {
|
||||||
return this.indicatorId;
|
// return this.indicatorId;
|
||||||
}
|
// }
|
||||||
|
|
||||||
public Double getValue() {
|
public Double getValue() {
|
||||||
return this.value;
|
return this.value;
|
||||||
|
@ -190,12 +198,12 @@ public final class Indicator implements GrantEntity {
|
||||||
return this.color;
|
return this.color;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
// @Override
|
||||||
public String toString() {
|
// public String toString() {
|
||||||
return "Threshold [id=" + this.id + ", indicatorId=" + this.indicatorId + ", value=" + this.value
|
// return "Threshold [id=" + this.id + ", indicatorId=" + this.indicatorId + ", value=" + this.value
|
||||||
+ ", color=" + this.color
|
// + ", color=" + this.color
|
||||||
+ "]";
|
// + "]";
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -105,12 +105,16 @@ public final class QuizData implements GrantEntity {
|
||||||
this.lmsType = lmsType;
|
this.lmsType = lmsType;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.startTime = LocalDateTime
|
this.startTime = (startTime != null)
|
||||||
.parse(startTime, Constants.DATE_TIME_PATTERN_UTC_NO_MILLIS)
|
? LocalDateTime
|
||||||
.toDateTime(DateTimeZone.UTC);
|
.parse(startTime, Constants.STANDARD_DATE_TIME_FORMATTER)
|
||||||
this.endTime = LocalDateTime
|
.toDateTime(DateTimeZone.UTC)
|
||||||
.parse(endTime, Constants.DATE_TIME_PATTERN_UTC_NO_MILLIS)
|
: null;
|
||||||
.toDateTime(DateTimeZone.UTC);
|
this.endTime = (endTime != null)
|
||||||
|
? LocalDateTime
|
||||||
|
.parse(endTime, Constants.STANDARD_DATE_TIME_FORMATTER)
|
||||||
|
.toDateTime(DateTimeZone.UTC)
|
||||||
|
: null;
|
||||||
this.startURL = startURL;
|
this.startURL = startURL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
/*
|
||||||
|
* 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.gbl.model.seb;
|
||||||
|
|
||||||
|
public class PingResponse {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
/*
|
||||||
|
* 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.gbl.model.seb;
|
||||||
|
|
||||||
|
public class RunningExams {
|
||||||
|
// TODO
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.gui.content;
|
||||||
import java.util.function.BooleanSupplier;
|
import java.util.function.BooleanSupplier;
|
||||||
|
|
||||||
import org.apache.commons.lang3.BooleanUtils;
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
|
import org.apache.tomcat.util.buf.StringUtils;
|
||||||
import org.eclipse.swt.widgets.Composite;
|
import org.eclipse.swt.widgets.Composite;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -36,6 +37,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.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.PageContext.AttributeKeys;
|
import ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.page.PageUtils;
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
|
import ch.ethz.seb.sebserver.gui.service.page.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;
|
||||||
|
@ -43,7 +45,6 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.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;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitution;
|
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.quiz.GetQuizData;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.quiz.GetQuizData;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.quiz.ImportAsExam;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.quiz.ImportAsExam;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
|
||||||
|
@ -71,6 +72,8 @@ public class ExamForm implements TemplateComposer {
|
||||||
new LocTextKey("sebserver.exam.indicator.list.column.name");
|
new LocTextKey("sebserver.exam.indicator.list.column.name");
|
||||||
private final static LocTextKey thresholdColumnKey =
|
private final static LocTextKey thresholdColumnKey =
|
||||||
new LocTextKey("sebserver.exam.indicator.list.column.thresholds");
|
new LocTextKey("sebserver.exam.indicator.list.column.thresholds");
|
||||||
|
private final static LocTextKey emptySelectionTextKey =
|
||||||
|
new LocTextKey("sebserver.exam.indicator.list.pleaseSelect");
|
||||||
|
|
||||||
protected ExamForm(
|
protected ExamForm(
|
||||||
final PageFormService pageFormService,
|
final PageFormService pageFormService,
|
||||||
|
@ -124,11 +127,6 @@ public class ExamForm implements TemplateComposer {
|
||||||
final EntityGrantCheck userGrantCheck = currentUser.entityGrantCheck(exam);
|
final EntityGrantCheck userGrantCheck = currentUser.entityGrantCheck(exam);
|
||||||
final boolean writeGrant = userGrantCheck.w();
|
final boolean writeGrant = userGrantCheck.w();
|
||||||
final boolean modifyGrant = userGrantCheck.m();
|
final boolean modifyGrant = userGrantCheck.m();
|
||||||
final boolean institutionActive = restService.getBuilder(GetInstitution.class)
|
|
||||||
.withURIVariable(API.PARAM_MODEL_ID, String.valueOf(exam.getInstitutionId()))
|
|
||||||
.call()
|
|
||||||
.map(inst -> inst.active)
|
|
||||||
.getOr(false);
|
|
||||||
|
|
||||||
// The Exam form
|
// The Exam form
|
||||||
final FormHandle<Exam> formHandle = this.pageFormService.getBuilder(
|
final FormHandle<Exam> formHandle = this.pageFormService.getBuilder(
|
||||||
|
@ -140,7 +138,7 @@ public class ExamForm implements TemplateComposer {
|
||||||
.putStaticValue(
|
.putStaticValue(
|
||||||
Domain.EXAM.ATTR_INSTITUTION_ID,
|
Domain.EXAM.ATTR_INSTITUTION_ID,
|
||||||
String.valueOf(exam.getInstitutionId()))
|
String.valueOf(exam.getInstitutionId()))
|
||||||
.putStaticValueIf(isNew,
|
.putStaticValue(
|
||||||
Domain.EXAM.ATTR_OWNER,
|
Domain.EXAM.ATTR_OWNER,
|
||||||
user.uuid)
|
user.uuid)
|
||||||
.putStaticValueIf(isNotNew,
|
.putStaticValueIf(isNotNew,
|
||||||
|
@ -192,11 +190,48 @@ public class ExamForm implements TemplateComposer {
|
||||||
"sebserver.exam.form.type",
|
"sebserver.exam.form.type",
|
||||||
String.valueOf(exam.type),
|
String.valueOf(exam.type),
|
||||||
this.resourceService::examTypeResources))
|
this.resourceService::examTypeResources))
|
||||||
|
.addField(FormBuilder.multiComboSelection(
|
||||||
|
Domain.EXAM.ATTR_SUPPORTER,
|
||||||
|
"sebserver.exam.form.supporter",
|
||||||
|
StringUtils.join(exam.supporter, Constants.LIST_SEPARATOR_CHAR),
|
||||||
|
this.resourceService::examSupporterResources)
|
||||||
|
.withCondition(isNotNew))
|
||||||
|
|
||||||
.buildFor(importFromQuizData
|
.buildFor(importFromQuizData
|
||||||
? restService.getRestCall(ImportAsExam.class)
|
? restService.getRestCall(ImportAsExam.class)
|
||||||
: restService.getRestCall(SaveExam.class));
|
: restService.getRestCall(SaveExam.class));
|
||||||
|
|
||||||
|
// propagate content actions to action-pane
|
||||||
|
formContext.clearEntityKeys()
|
||||||
|
.removeAttribute(AttributeKeys.IMPORT_FROM_QUIZZ_DATA)
|
||||||
|
|
||||||
|
.createAction(ActionDefinition.EXAM_MODIFY)
|
||||||
|
.withEntityKey(entityKey)
|
||||||
|
.publishIf(() -> modifyGrant && readonly)
|
||||||
|
|
||||||
|
.createAction(ActionDefinition.EXAM_SAVE)
|
||||||
|
.withExec(formHandle::processFormSave)
|
||||||
|
.publishIf(() -> !readonly && modifyGrant)
|
||||||
|
|
||||||
|
.createAction(ActionDefinition.EXAM_CANCEL_MODIFY)
|
||||||
|
.withEntityKey(entityKey)
|
||||||
|
.withAttribute(AttributeKeys.IMPORT_FROM_QUIZZ_DATA, String.valueOf(importFromQuizData))
|
||||||
|
.withExec(ExamForm::cancelModify)
|
||||||
|
.withConfirm("sebserver.overall.action.modify.cancel.confirm")
|
||||||
|
.publishIf(() -> !readonly)
|
||||||
|
|
||||||
|
.createAction(ActionDefinition.EXAM_DEACTIVATE)
|
||||||
|
.withEntityKey(entityKey)
|
||||||
|
.withExec(restService::activation)
|
||||||
|
.withConfirm(PageUtils.confirmDeactivation(exam, restService))
|
||||||
|
.publishIf(() -> writeGrant && readonly && exam.isActive())
|
||||||
|
|
||||||
|
.createAction(ActionDefinition.EXAM_ACTIVATE)
|
||||||
|
.withEntityKey(entityKey)
|
||||||
|
.withExec(restService::activation)
|
||||||
|
.publishIf(() -> writeGrant && readonly && !exam.isActive());
|
||||||
|
|
||||||
|
// additional data in read-only view
|
||||||
if (readonly) {
|
if (readonly) {
|
||||||
|
|
||||||
// List of Indicators
|
// List of Indicators
|
||||||
|
@ -227,24 +262,25 @@ public class ExamForm implements TemplateComposer {
|
||||||
|
|
||||||
.compose(content);
|
.compose(content);
|
||||||
|
|
||||||
|
formContext.clearEntityKeys()
|
||||||
|
.removeAttribute(AttributeKeys.IMPORT_FROM_QUIZZ_DATA)
|
||||||
|
|
||||||
|
.createAction(ActionDefinition.EXAM_INDICATOR_NEW)
|
||||||
|
.withParentEntityKey(entityKey)
|
||||||
|
.publishIf(() -> modifyGrant)
|
||||||
|
|
||||||
|
.createAction(ActionDefinition.EXAM_INDICATOR_MODIFY_FROM_LIST)
|
||||||
|
.withSelect(indicatorTable::getSelection, Action::applySingleSelection, emptySelectionTextKey)
|
||||||
|
.publishIf(() -> modifyGrant && indicatorTable.hasAnyContent())
|
||||||
|
|
||||||
|
.createAction(ActionDefinition.EXAM_INDICATOR_DELETE_FROM_LIST)
|
||||||
|
.withSelect(indicatorTable::getSelection, Action::applySingleSelection, emptySelectionTextKey)
|
||||||
|
.publishIf(() -> modifyGrant && indicatorTable.hasAnyContent());
|
||||||
|
|
||||||
// TODO List of attached SEB Configurations
|
// TODO List of attached SEB Configurations
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// propagate content actions to action-pane
|
|
||||||
formContext.clearEntityKeys()
|
|
||||||
.removeAttribute(AttributeKeys.IMPORT_FROM_QUIZZ_DATA)
|
|
||||||
|
|
||||||
.createAction(ActionDefinition.EXAM_SAVE)
|
|
||||||
.withExec(formHandle::processFormSave)
|
|
||||||
.publishIf(() -> !readonly && modifyGrant)
|
|
||||||
|
|
||||||
.createAction(ActionDefinition.EXAM_CANCEL_MODIFY)
|
|
||||||
.withEntityKey(entityKey)
|
|
||||||
.withAttribute(AttributeKeys.IMPORT_FROM_QUIZZ_DATA, String.valueOf(importFromQuizData))
|
|
||||||
.withExec(ExamForm::cancelModify)
|
|
||||||
.withConfirm("sebserver.overall.action.modify.cancel.confirm")
|
|
||||||
.publishIf(() -> !readonly);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result<Exam> getExistingExam(final EntityKey entityKey, final RestService restService) {
|
private Result<Exam> getExistingExam(final EntityKey entityKey, final RestService restService) {
|
||||||
|
|
|
@ -10,11 +10,7 @@ package ch.ethz.seb.sebserver.gui.content;
|
||||||
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import org.eclipse.swt.SWT;
|
|
||||||
import org.eclipse.swt.layout.GridData;
|
|
||||||
import org.eclipse.swt.widgets.Composite;
|
import org.eclipse.swt.widgets.Composite;
|
||||||
import org.eclipse.swt.widgets.Label;
|
|
||||||
import org.eclipse.swt.widgets.Text;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
@ -32,7 +28,6 @@ import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
|
||||||
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.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.ModalInputDialog;
|
|
||||||
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;
|
||||||
import ch.ethz.seb.sebserver.gui.service.page.action.Action;
|
import ch.ethz.seb.sebserver.gui.service.page.action.Action;
|
||||||
|
@ -136,9 +131,9 @@ public class ExamList implements TemplateComposer {
|
||||||
final GrantCheck userGrant = currentUser.grantCheck(EntityType.EXAM);
|
final GrantCheck userGrant = currentUser.grantCheck(EntityType.EXAM);
|
||||||
pageContext.clearEntityKeys()
|
pageContext.clearEntityKeys()
|
||||||
|
|
||||||
.createAction(ActionDefinition.TEST_ACTION)
|
// .createAction(ActionDefinition.TEST_ACTION)
|
||||||
.withExec(this::testModalInput)
|
// .withExec(this::testModalInput)
|
||||||
.publish()
|
// .publish()
|
||||||
|
|
||||||
.createAction(ActionDefinition.EXAM_IMPORT)
|
.createAction(ActionDefinition.EXAM_IMPORT)
|
||||||
.publishIf(userGrant::im)
|
.publishIf(userGrant::im)
|
||||||
|
@ -172,29 +167,29 @@ public class ExamList implements TemplateComposer {
|
||||||
.getText("sebserver.exam.type." + exam.type.name());
|
.getText("sebserver.exam.type." + exam.type.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Action testModalInput(final Action action) {
|
// private Action testModalInput(final Action action) {
|
||||||
final ModalInputDialog<String> dialog = new ModalInputDialog<>(
|
// final ModalInputDialog<String> dialog = new ModalInputDialog<>(
|
||||||
action.pageContext().getParent().getShell(),
|
// action.pageContext().getParent().getShell(),
|
||||||
this.widgetFactory);
|
// this.widgetFactory);
|
||||||
|
//
|
||||||
dialog.open(
|
// dialog.open(
|
||||||
"Test Input Dialog",
|
// "Test Input Dialog",
|
||||||
action.pageContext(),
|
// action.pageContext(),
|
||||||
value -> {
|
// value -> {
|
||||||
System.out.println("********************** value: " + value);
|
// System.out.println("********************** value: " + value);
|
||||||
},
|
// },
|
||||||
pc -> {
|
// pc -> {
|
||||||
final Composite parent = pc.getParent();
|
// final Composite parent = pc.getParent();
|
||||||
final Label label = new Label(parent, SWT.NONE);
|
// final Label label = new Label(parent, SWT.NONE);
|
||||||
label.setText("Please Enter:");
|
// label.setText("Please Enter:");
|
||||||
label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
|
// label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
|
||||||
|
//
|
||||||
final Text text = new Text(parent, SWT.LEFT | SWT.BORDER);
|
// final Text text = new Text(parent, SWT.LEFT | SWT.BORDER);
|
||||||
text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
|
// text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
|
||||||
return () -> text.getText();
|
// return () -> text.getText();
|
||||||
});
|
// });
|
||||||
|
//
|
||||||
return action;
|
// return action;
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,20 +8,47 @@
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.gui.content;
|
package ch.ethz.seb.sebserver.gui.content;
|
||||||
|
|
||||||
|
import org.eclipse.swt.widgets.Composite;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||||
|
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.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.form.FormBuilder;
|
||||||
|
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.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;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.page.action.Action;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicator;
|
||||||
|
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.auth.CurrentUser;
|
||||||
|
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
@Component
|
@Component
|
||||||
@GuiProfile
|
@GuiProfile
|
||||||
public class IndicatorForm implements TemplateComposer {
|
public class IndicatorForm implements TemplateComposer {
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
@ -35,7 +62,97 @@ public class IndicatorForm implements TemplateComposer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compose(final PageContext pageContext) {
|
public void compose(final PageContext pageContext) {
|
||||||
// TODO Auto-generated method stub
|
final CurrentUser currentUser = this.resourceService.getCurrentUser();
|
||||||
|
final RestService restService = this.resourceService.getRestService();
|
||||||
|
final WidgetFactory widgetFactory = this.pageFormService.getWidgetFactory();
|
||||||
|
final I18nSupport i18nSupport = this.resourceService.getI18nSupport();
|
||||||
|
|
||||||
|
final UserInfo user = currentUser.get();
|
||||||
|
final EntityKey entityKey = pageContext.getEntityKey();
|
||||||
|
final EntityKey parentEntityKey = pageContext.getParentEntityKey();
|
||||||
|
final boolean isNew = entityKey == null;
|
||||||
|
final boolean isReadonly = pageContext.isReadonly();
|
||||||
|
|
||||||
|
final Exam exam = restService
|
||||||
|
.getBuilder(GetExam.class)
|
||||||
|
.withURIVariable(API.PARAM_MODEL_ID, parentEntityKey.modelId)
|
||||||
|
.call()
|
||||||
|
.get(pageContext::notifyError);
|
||||||
|
|
||||||
|
// get data or create new. Handle error if happen
|
||||||
|
final Indicator indicator = (isNew)
|
||||||
|
? Indicator.createNew(exam.getInstitutionId(), exam)
|
||||||
|
: restService
|
||||||
|
.getBuilder(GetIndicator.class)
|
||||||
|
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
|
||||||
|
.call()
|
||||||
|
.get(pageContext::notifyError);
|
||||||
|
|
||||||
|
if (indicator == null) {
|
||||||
|
log.error("Failed to get Indicator. "
|
||||||
|
+ "Error was notified to the User. "
|
||||||
|
+ "See previous logs for more infomation");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// new PageContext with actual EntityKey
|
||||||
|
final PageContext formContext = pageContext.withEntityKey(indicator.getEntityKey());
|
||||||
|
|
||||||
|
// the default page layout
|
||||||
|
final LocTextKey titleKey = new LocTextKey(
|
||||||
|
(isNew)
|
||||||
|
? "sebserver.exam.indicator.form.title.new"
|
||||||
|
: "sebserver.exam.indicator.form.title");
|
||||||
|
final Composite content = widgetFactory.defaultPageLayout(
|
||||||
|
formContext.getParent(),
|
||||||
|
titleKey);
|
||||||
|
|
||||||
|
final FormHandle<Indicator> formHandle = this.pageFormService.getBuilder(
|
||||||
|
formContext.copyOf(content), 4)
|
||||||
|
.readonly(isReadonly)
|
||||||
|
.putStaticValueIf(() -> !isNew,
|
||||||
|
Domain.INDICATOR.ATTR_ID,
|
||||||
|
indicator.getModelId())
|
||||||
|
.putStaticValue(
|
||||||
|
Domain.EXAM.ATTR_INSTITUTION_ID,
|
||||||
|
exam.getModelId())
|
||||||
|
.putStaticValue(
|
||||||
|
Domain.INDICATOR.ATTR_EXAM_ID,
|
||||||
|
parentEntityKey.getModelId())
|
||||||
|
.addField(FormBuilder.text(
|
||||||
|
"examName",
|
||||||
|
"sebserver.exam.indicator.form.exam",
|
||||||
|
exam.name)
|
||||||
|
.readonly(true))
|
||||||
|
.addField(FormBuilder.text(
|
||||||
|
Domain.INDICATOR.ATTR_NAME,
|
||||||
|
"sebserver.exam.indicator.form.name",
|
||||||
|
indicator.name))
|
||||||
|
.addField(FormBuilder.singleSelection(
|
||||||
|
Domain.INDICATOR.ATTR_TYPE,
|
||||||
|
"sebserver.exam.indicator.form.type",
|
||||||
|
(indicator.type != null) ? indicator.type.name() : null,
|
||||||
|
this.resourceService::indicatorTypeResources))
|
||||||
|
.addField(FormBuilder.colorSelection(
|
||||||
|
Domain.INDICATOR.ATTR_TYPE,
|
||||||
|
"sebserver.exam.indicator.form.color",
|
||||||
|
indicator.defaultColor))
|
||||||
|
.buildFor((isNew)
|
||||||
|
? restService.getRestCall(NewIndicator.class)
|
||||||
|
: restService.getRestCall(SaveIndicator.class));
|
||||||
|
|
||||||
|
// propagate content actions to action-pane
|
||||||
|
formContext.clearEntityKeys()
|
||||||
|
.createAction(ActionDefinition.EXAM_INDICATOR_SAVE)
|
||||||
|
.withParentEntityKey(parentEntityKey)
|
||||||
|
.withExec(formHandle::processFormSave)
|
||||||
|
.publishIf(() -> !isReadonly)
|
||||||
|
|
||||||
|
.createAction(ActionDefinition.EXAM_INDICATOR_CANCEL_MODIFY)
|
||||||
|
.withEntityKey(parentEntityKey)
|
||||||
|
.withExec(Action::onEmptyEntityKeyGoToActivityHome)
|
||||||
|
.withConfirm("sebserver.overall.action.modify.cancel.confirm")
|
||||||
|
.publishIf(() -> !isReadonly);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ public class InstitutionForm implements TemplateComposer {
|
||||||
final EntityKey entityKey = pageContext.getEntityKey();
|
final EntityKey entityKey = pageContext.getEntityKey();
|
||||||
final boolean isNew = entityKey == null;
|
final boolean isNew = entityKey == null;
|
||||||
// get data or create new. Handle error if happen
|
// get data or create new. Handle error if happen
|
||||||
final Institution institution = (entityKey == null)
|
final Institution institution = (isNew)
|
||||||
? Institution.createNew()
|
? Institution.createNew()
|
||||||
: this.restService
|
: this.restService
|
||||||
.getBuilder(GetInstitution.class)
|
.getBuilder(GetInstitution.class)
|
||||||
|
@ -116,7 +116,7 @@ public class InstitutionForm implements TemplateComposer {
|
||||||
// The Institution form
|
// The Institution form
|
||||||
final FormHandle<Institution> formHandle = this.pageFormService.getBuilder(
|
final FormHandle<Institution> formHandle = this.pageFormService.getBuilder(
|
||||||
formContext.copyOf(content), 4)
|
formContext.copyOf(content), 4)
|
||||||
.readonly(formContext.isReadonly())
|
.readonly(isReadonly)
|
||||||
.putStaticValueIf(() -> !isNew,
|
.putStaticValueIf(() -> !isNew,
|
||||||
Domain.INSTITUTION.ATTR_ID,
|
Domain.INSTITUTION.ATTR_ID,
|
||||||
institution.getModelId())
|
institution.getModelId())
|
||||||
|
|
|
@ -196,7 +196,7 @@ public class LmsSetupForm implements TemplateComposer {
|
||||||
|
|
||||||
.createAction(ActionDefinition.LMS_SETUP_ACTIVATE)
|
.createAction(ActionDefinition.LMS_SETUP_ACTIVATE)
|
||||||
.withEntityKey(entityKey)
|
.withEntityKey(entityKey)
|
||||||
.withExec(restService::activation)
|
.withExec(action -> activate(action, formHandle))
|
||||||
.publishIf(() -> writeGrant && readonly && institutionActive && !lmsSetup.isActive())
|
.publishIf(() -> writeGrant && readonly && institutionActive && !lmsSetup.isActive())
|
||||||
|
|
||||||
.createAction(ActionDefinition.LMS_SETUP_SAVE)
|
.createAction(ActionDefinition.LMS_SETUP_SAVE)
|
||||||
|
@ -210,6 +210,14 @@ public class LmsSetupForm implements TemplateComposer {
|
||||||
.publishIf(() -> !readonly);
|
.publishIf(() -> !readonly);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Save and test connection before activation */
|
||||||
|
private Action activate(final Action action, final FormHandle<LmsSetup> formHandle) {
|
||||||
|
final RestService restService = this.resourceService.getRestService();
|
||||||
|
final Action testLmsSetup = this.testLmsSetup(action, formHandle);
|
||||||
|
final Action activation = restService.activation(testLmsSetup);
|
||||||
|
return activation;
|
||||||
|
}
|
||||||
|
|
||||||
/** LmsSetup test action implementation */
|
/** LmsSetup test action implementation */
|
||||||
private Action testLmsSetup(final Action action, final FormHandle<LmsSetup> formHandle) {
|
private Action testLmsSetup(final Action action, final FormHandle<LmsSetup> formHandle) {
|
||||||
// If we are in edit-mode we have to save the form before testing
|
// If we are in edit-mode we have to save the form before testing
|
||||||
|
|
|
@ -321,7 +321,6 @@ public enum ActionDefinition {
|
||||||
ImageIcon.NEW,
|
ImageIcon.NEW,
|
||||||
IndicatorForm.class,
|
IndicatorForm.class,
|
||||||
EXAM_VIEW_FROM_LIST,
|
EXAM_VIEW_FROM_LIST,
|
||||||
ActionCategory.FORM,
|
|
||||||
false),
|
false),
|
||||||
EXAM_INDICATOR_MODIFY_FROM_LIST(
|
EXAM_INDICATOR_MODIFY_FROM_LIST(
|
||||||
new LocTextKey("sebserver.exam.indicator.action.list.modify"),
|
new LocTextKey("sebserver.exam.indicator.action.list.modify"),
|
||||||
|
@ -338,7 +337,7 @@ public enum ActionDefinition {
|
||||||
ActionCategory.INDICATOR_LIST,
|
ActionCategory.INDICATOR_LIST,
|
||||||
true),
|
true),
|
||||||
EXAM_INDICATOR_SAVE(
|
EXAM_INDICATOR_SAVE(
|
||||||
new LocTextKey("sebserver.exam.indicator.action.list.save"),
|
new LocTextKey("sebserver.exam.indicator.action.save"),
|
||||||
ImageIcon.SAVE,
|
ImageIcon.SAVE,
|
||||||
ExamForm.class,
|
ExamForm.class,
|
||||||
EXAM_VIEW_FROM_LIST,
|
EXAM_VIEW_FROM_LIST,
|
||||||
|
|
|
@ -195,7 +195,9 @@ public final class Form implements FormBinding {
|
||||||
}
|
}
|
||||||
private FormFieldAccessor createAccessor(final Label label, final Selection selection) {
|
private FormFieldAccessor createAccessor(final Label label, final Selection selection) {
|
||||||
switch (selection.type()) {
|
switch (selection.type()) {
|
||||||
case MULTI : return createAccessor(label, selection, Form::adaptCommaSeparatedStringToJsonArray);
|
case MULTI:
|
||||||
|
case MULTI_COMBO:
|
||||||
|
return createAccessor(label, selection, Form::adaptCommaSeparatedStringToJsonArray);
|
||||||
default : return createAccessor(label, selection, null);
|
default : return createAccessor(label, selection, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -216,6 +216,14 @@ public class FormBuilder {
|
||||||
return new SelectionFieldBuilder(Selection.Type.MULTI_COMBO, name, label, value, itemsSupplier);
|
return new SelectionFieldBuilder(Selection.Type.MULTI_COMBO, name, label, value, itemsSupplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static SelectionFieldBuilder colorSelection(
|
||||||
|
final String name,
|
||||||
|
final String label,
|
||||||
|
final String value) {
|
||||||
|
|
||||||
|
return new SelectionFieldBuilder(Selection.Type.COLOR, name, label, value, null);
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,6 +89,7 @@ public final class SelectionFieldBuilder extends FieldBuilder {
|
||||||
if (this.type == Type.MULTI || this.type == Type.MULTI_COMBO) {
|
if (this.type == Type.MULTI || this.type == Type.MULTI_COMBO) {
|
||||||
final Composite composite = new Composite(builder.formParent, SWT.NONE);
|
final Composite composite = new Composite(builder.formParent, SWT.NONE);
|
||||||
final GridLayout gridLayout = new GridLayout(1, true);
|
final GridLayout gridLayout = new GridLayout(1, true);
|
||||||
|
gridLayout.verticalSpacing = 0;
|
||||||
gridLayout.horizontalSpacing = 0;
|
gridLayout.horizontalSpacing = 0;
|
||||||
gridLayout.marginLeft = 0;
|
gridLayout.marginLeft = 0;
|
||||||
gridLayout.marginHeight = 0;
|
gridLayout.marginHeight = 0;
|
||||||
|
|
|
@ -206,7 +206,7 @@ public class OAuth2AuthorizationContextHolder implements AuthorizationContextHol
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final OAuth2AccessToken accessToken = this.restTemplate.getAccessToken();
|
final OAuth2AccessToken accessToken = this.restTemplate.getAccessToken();
|
||||||
log.debug("Got token for user: {} : {}", username, accessToken);
|
log.debug("Got token for user: {} : {}", username, "--");
|
||||||
this.loggedInUser = getLoggedInUser();
|
this.loggedInUser = getLoggedInUser();
|
||||||
return true;
|
return true;
|
||||||
} catch (final OAuth2AccessDeniedException | AccessDeniedException e) {
|
} catch (final OAuth2AccessDeniedException | AccessDeniedException e) {
|
||||||
|
|
|
@ -0,0 +1,146 @@
|
||||||
|
/*
|
||||||
|
* 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.widget;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.eclipse.swt.SWT;
|
||||||
|
import org.eclipse.swt.graphics.Color;
|
||||||
|
import org.eclipse.swt.graphics.RGB;
|
||||||
|
import org.eclipse.swt.layout.GridData;
|
||||||
|
import org.eclipse.swt.layout.GridLayout;
|
||||||
|
import org.eclipse.swt.widgets.ColorDialog;
|
||||||
|
import org.eclipse.swt.widgets.Composite;
|
||||||
|
import org.eclipse.swt.widgets.Event;
|
||||||
|
import org.eclipse.swt.widgets.Label;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.util.Tuple;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
|
||||||
|
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon;
|
||||||
|
|
||||||
|
public class ColorSelection extends Composite implements Selection {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 4775375044147842526L;
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(ColorSelection.class);
|
||||||
|
|
||||||
|
private static final int ACTION_COLUMN_WIDTH = 20;
|
||||||
|
private final ColorDialog colorDialog;
|
||||||
|
private final Composite colorField;
|
||||||
|
private RGB selection;
|
||||||
|
|
||||||
|
ColorSelection(final Composite parent, final WidgetFactory widgetFactory) {
|
||||||
|
super(parent, SWT.NONE);
|
||||||
|
final GridLayout gridLayout = new GridLayout(2, false);
|
||||||
|
gridLayout.verticalSpacing = 1;
|
||||||
|
gridLayout.marginLeft = 0;
|
||||||
|
gridLayout.marginHeight = 0;
|
||||||
|
gridLayout.marginWidth = 0;
|
||||||
|
setLayout(gridLayout);
|
||||||
|
|
||||||
|
this.colorDialog = new ColorDialog(this.getShell(), SWT.NONE);
|
||||||
|
|
||||||
|
this.colorField = new Composite(this, SWT.NONE);
|
||||||
|
final GridData colorCell = new GridData(SWT.FILL, SWT.TOP, true, false);
|
||||||
|
colorCell.heightHint = 20;
|
||||||
|
this.colorField.setLayoutData(colorCell);
|
||||||
|
|
||||||
|
final Label imageButton = widgetFactory.imageButton(
|
||||||
|
ImageIcon.COLOR,
|
||||||
|
this,
|
||||||
|
new LocTextKey("Set Color"),
|
||||||
|
this::addColorSelection);
|
||||||
|
|
||||||
|
final GridData actionCell = new GridData(SWT.LEFT, SWT.CENTER, true, false);
|
||||||
|
actionCell.widthHint = ACTION_COLUMN_WIDTH;
|
||||||
|
imageButton.setLayoutData(actionCell);
|
||||||
|
|
||||||
|
this.addListener(SWT.Resize, this::adaptColumnWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type type() {
|
||||||
|
return Type.COLOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyNewMapping(final List<Tuple<String>> mapping) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void select(final String keys) {
|
||||||
|
this.selection = parseRGB(keys);
|
||||||
|
applySelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSelectionValue() {
|
||||||
|
return parseColorString(this.selection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
this.selection = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addColorSelection(final Event event) {
|
||||||
|
this.colorDialog.open(code -> {
|
||||||
|
if (code == SWT.CANCEL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.selection = this.colorDialog.getRGB();
|
||||||
|
applySelection();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applySelection() {
|
||||||
|
if (this.selection != null) {
|
||||||
|
this.colorField.setBackground(new Color(this.getDisplay(), this.selection));
|
||||||
|
} else {
|
||||||
|
this.colorField.setBackground(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void adaptColumnWidth(final Event event) {
|
||||||
|
try {
|
||||||
|
final int currentTableWidth = this.getClientArea().width;
|
||||||
|
final GridData comboCell = (GridData) this.colorField.getLayoutData();
|
||||||
|
comboCell.widthHint = currentTableWidth - ACTION_COLUMN_WIDTH;
|
||||||
|
this.layout();
|
||||||
|
} catch (final Exception e) {
|
||||||
|
log.warn("Failed to adaptColumnWidth: ", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static String parseColorString(final RGB color) {
|
||||||
|
if (color == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Integer.toHexString(color.red)
|
||||||
|
+ Integer.toHexString(color.green)
|
||||||
|
+ Integer.toHexString(color.blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
static RGB parseRGB(final String colorString) {
|
||||||
|
if (StringUtils.isBlank(colorString)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int r = Integer.parseInt(colorString.substring(0, 2), 16);
|
||||||
|
final int g = Integer.parseInt(colorString.substring(2, 4), 16);
|
||||||
|
final int b = Integer.parseInt(colorString.substring(4, 6), 16);
|
||||||
|
|
||||||
|
return new RGB(r, g, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -26,6 +26,7 @@ import org.eclipse.swt.SWT;
|
||||||
import org.eclipse.swt.events.SelectionAdapter;
|
import org.eclipse.swt.events.SelectionAdapter;
|
||||||
import org.eclipse.swt.events.SelectionEvent;
|
import org.eclipse.swt.events.SelectionEvent;
|
||||||
import org.eclipse.swt.graphics.Image;
|
import org.eclipse.swt.graphics.Image;
|
||||||
|
import org.eclipse.swt.graphics.ImageData;
|
||||||
import org.eclipse.swt.layout.GridData;
|
import org.eclipse.swt.layout.GridData;
|
||||||
import org.eclipse.swt.layout.GridLayout;
|
import org.eclipse.swt.layout.GridLayout;
|
||||||
import org.eclipse.swt.widgets.Composite;
|
import org.eclipse.swt.widgets.Composite;
|
||||||
|
@ -51,14 +52,14 @@ public class ImageUpload extends Composite {
|
||||||
|
|
||||||
ImageUpload(final Composite parent, final ServerPushService serverPushService, final boolean readonly) {
|
ImageUpload(final Composite parent, final ServerPushService serverPushService, final boolean readonly) {
|
||||||
super(parent, SWT.NONE);
|
super(parent, SWT.NONE);
|
||||||
super.setLayout(new GridLayout(2, false));
|
super.setLayout(new GridLayout(1, false));
|
||||||
|
|
||||||
this.serverPushService = serverPushService;
|
this.serverPushService = serverPushService;
|
||||||
|
|
||||||
if (!readonly) {
|
if (!readonly) {
|
||||||
this.fileUpload = new FileUpload(this, SWT.NONE);
|
this.fileUpload = new FileUpload(this, SWT.NONE);
|
||||||
this.fileUpload.setText("Select File");
|
this.fileUpload.setImage(WidgetFactory.ImageIcon.IMPORT.getImage(parent.getDisplay()));
|
||||||
this.fileUpload.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false));
|
this.fileUpload.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
|
||||||
|
|
||||||
final FileUploadHandler uploadHandler = new FileUploadHandler(new FileUploadReceiver() {
|
final FileUploadHandler uploadHandler = new FileUploadHandler(new FileUploadReceiver() {
|
||||||
|
|
||||||
|
@ -108,7 +109,7 @@ public class ImageUpload extends Composite {
|
||||||
|
|
||||||
public void setSelectionText(final String text) {
|
public void setSelectionText(final String text) {
|
||||||
if (this.fileUpload != null) {
|
if (this.fileUpload != null) {
|
||||||
this.fileUpload.setText(text);
|
this.fileUpload.setToolTipText(text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,8 +125,8 @@ public class ImageUpload extends Composite {
|
||||||
this.imageBase64 = imageBase64;
|
this.imageBase64 = imageBase64;
|
||||||
final Base64InputStream input = new Base64InputStream(
|
final Base64InputStream input = new Base64InputStream(
|
||||||
new ByteArrayInputStream(imageBase64.getBytes(StandardCharsets.UTF_8)), false);
|
new ByteArrayInputStream(imageBase64.getBytes(StandardCharsets.UTF_8)), false);
|
||||||
this.imageCanvas.setData(RWT.CUSTOM_VARIANT, "bgLogoNoImage");
|
|
||||||
this.imageCanvas.setBackgroundImage(new Image(super.getDisplay(), input));
|
setImage(this, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final boolean uploadInProgress(final ServerPushContext context) {
|
private static final boolean uploadInProgress(final ServerPushContext context) {
|
||||||
|
@ -153,12 +154,20 @@ public class ImageUpload extends Composite {
|
||||||
imageUpload.imageBase64.getBytes(StandardCharsets.UTF_8)),
|
imageUpload.imageBase64.getBytes(StandardCharsets.UTF_8)),
|
||||||
false);
|
false);
|
||||||
|
|
||||||
imageUpload.imageCanvas.setData(RWT.CUSTOM_VARIANT, "bgLogoNoImage");
|
setImage(imageUpload, input);
|
||||||
imageUpload.imageCanvas.setBackgroundImage(new Image(context.getDisplay(), input));
|
|
||||||
context.layout();
|
context.layout();
|
||||||
imageUpload.layout();
|
imageUpload.layout();
|
||||||
imageUpload.loadNewImage = false;
|
imageUpload.loadNewImage = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void setImage(final ImageUpload imageUpload, final Base64InputStream input) {
|
||||||
|
imageUpload.imageCanvas.setData(RWT.CUSTOM_VARIANT, "bgLogoNoImage");
|
||||||
|
|
||||||
|
final Image image = new Image(imageUpload.imageCanvas.getDisplay(), input);
|
||||||
|
final ImageData imageData = image.getImageData().scaledTo(200, 100);
|
||||||
|
|
||||||
|
imageUpload.imageCanvas.setBackgroundImage(new Image(imageUpload.imageCanvas.getDisplay(), imageData));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,18 +9,23 @@
|
||||||
package ch.ethz.seb.sebserver.gui.widget;
|
package ch.ethz.seb.sebserver.gui.widget;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.eclipse.swt.SWT;
|
import org.eclipse.swt.SWT;
|
||||||
import org.eclipse.swt.custom.TableEditor;
|
import org.eclipse.swt.layout.GridData;
|
||||||
import org.eclipse.swt.layout.GridLayout;
|
import org.eclipse.swt.layout.GridLayout;
|
||||||
import org.eclipse.swt.widgets.Combo;
|
import org.eclipse.swt.widgets.Combo;
|
||||||
import org.eclipse.swt.widgets.Composite;
|
import org.eclipse.swt.widgets.Composite;
|
||||||
|
import org.eclipse.swt.widgets.Control;
|
||||||
import org.eclipse.swt.widgets.Event;
|
import org.eclipse.swt.widgets.Event;
|
||||||
import org.eclipse.swt.widgets.Table;
|
import org.eclipse.swt.widgets.Label;
|
||||||
import org.eclipse.swt.widgets.TableColumn;
|
|
||||||
import org.eclipse.swt.widgets.TableItem;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -34,43 +39,40 @@ public class MultiSelectionCombo extends Composite implements Selection {
|
||||||
private static final Logger log = LoggerFactory.getLogger(MultiSelectionCombo.class);
|
private static final Logger log = LoggerFactory.getLogger(MultiSelectionCombo.class);
|
||||||
private static final long serialVersionUID = -7787134114963647332L;
|
private static final long serialVersionUID = -7787134114963647332L;
|
||||||
private static final int ACTION_COLUMN_WIDTH = 20;
|
private static final int ACTION_COLUMN_WIDTH = 20;
|
||||||
|
private static final String SELECTION_KEY = "SELECTION_KEY";
|
||||||
|
|
||||||
private final WidgetFactory widgetFactory;
|
private final WidgetFactory widgetFactory;
|
||||||
private final Table table;
|
|
||||||
private final Combo combo;
|
private final Combo combo;
|
||||||
private final List<String> selected = new ArrayList<>();
|
|
||||||
private final List<Tuple<String>> mapping = new ArrayList<>();
|
private final List<Tuple<Control>> selectionControls = new ArrayList<>();
|
||||||
|
private final List<Tuple<String>> selectedValues = new ArrayList<>();
|
||||||
|
private final Map<String, String> mapping = new HashMap<>();
|
||||||
|
//private final List<Tuple<String>> mapping = new ArrayList<>();
|
||||||
|
|
||||||
MultiSelectionCombo(final Composite parent, final WidgetFactory widgetFactory) {
|
MultiSelectionCombo(final Composite parent, final WidgetFactory widgetFactory) {
|
||||||
super(parent, SWT.NONE);
|
super(parent, SWT.NONE);
|
||||||
this.widgetFactory = widgetFactory;
|
this.widgetFactory = widgetFactory;
|
||||||
final GridLayout gridLayout = new GridLayout(1, true);
|
final GridLayout gridLayout = new GridLayout(2, false);
|
||||||
gridLayout.verticalSpacing = 1;
|
gridLayout.verticalSpacing = 1;
|
||||||
gridLayout.marginLeft = 0;
|
gridLayout.marginLeft = 0;
|
||||||
gridLayout.marginHeight = 0;
|
gridLayout.marginHeight = 0;
|
||||||
gridLayout.marginWidth = 0;
|
gridLayout.marginWidth = 0;
|
||||||
setLayout(gridLayout);
|
setLayout(gridLayout);
|
||||||
|
|
||||||
this.table = new Table(this, SWT.NONE);
|
this.addListener(SWT.Resize, this::adaptColumnWidth);
|
||||||
|
|
||||||
TableColumn column = new TableColumn(this.table, SWT.NONE);
|
this.combo = new Combo(this, SWT.NONE);
|
||||||
column = new TableColumn(this.table, SWT.NONE);
|
final GridData comboCell = new GridData(SWT.FILL, SWT.CENTER, true, false);
|
||||||
column.setWidth(ACTION_COLUMN_WIDTH);
|
this.combo.setLayoutData(comboCell);
|
||||||
this.table.setHeaderVisible(false);
|
|
||||||
this.table.addListener(SWT.Resize, this::adaptColumnWidth);
|
|
||||||
|
|
||||||
final TableItem header = new TableItem(this.table, SWT.NONE);
|
final Label imageButton = widgetFactory.imageButton(
|
||||||
final TableEditor editor = new TableEditor(this.table);
|
ImageIcon.ADD_BOX,
|
||||||
this.combo = new Combo(this.table, SWT.NONE);
|
this,
|
||||||
editor.grabHorizontal = true;
|
|
||||||
editor.setEditor(this.combo, header, 0);
|
|
||||||
|
|
||||||
widgetFactory.imageButton(
|
|
||||||
ImageIcon.ADD,
|
|
||||||
this.table,
|
|
||||||
new LocTextKey("Add"),
|
new LocTextKey("Add"),
|
||||||
this::addComboSelection);
|
this::addComboSelection);
|
||||||
|
final GridData actionCell = new GridData(SWT.LEFT, SWT.CENTER, true, false);
|
||||||
|
actionCell.widthHint = ACTION_COLUMN_WIDTH;
|
||||||
|
imageButton.setLayoutData(actionCell);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -80,49 +82,132 @@ public class MultiSelectionCombo extends Composite implements Selection {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyNewMapping(final List<Tuple<String>> mapping) {
|
public void applyNewMapping(final List<Tuple<String>> mapping) {
|
||||||
this.selected.clear();
|
this.mapping.putAll(mapping.stream()
|
||||||
this.mapping.clear();
|
.collect(Collectors.toMap(t -> t._1, t -> t._2)));
|
||||||
this.mapping.addAll(mapping);
|
this.clear();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void select(final String keys) {
|
public void select(final String keys) {
|
||||||
clear();
|
clear();
|
||||||
|
if (StringUtils.isBlank(keys)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Arrays.asList(StringUtils.split(keys, Constants.LIST_SEPARATOR))
|
||||||
|
.stream()
|
||||||
|
.forEach(this::addSelection);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getSelectionValue() {
|
public String getSelectionValue() {
|
||||||
if (this.selected.isEmpty()) {
|
if (this.selectedValues.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return this.mapping
|
return this.selectedValues
|
||||||
.stream()
|
.stream()
|
||||||
.filter(t -> this.selected.contains(t._2))
|
|
||||||
.map(t -> t._1)
|
.map(t -> t._1)
|
||||||
.reduce("", (s1, s2) -> s1.concat(Constants.LIST_SEPARATOR).concat(s2));
|
.reduce("", (s1, s2) -> {
|
||||||
|
if (!StringUtils.isBlank(s1)) {
|
||||||
|
return s1.concat(Constants.LIST_SEPARATOR).concat(s2);
|
||||||
|
} else {
|
||||||
|
return s1.concat(s2);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clear() {
|
public void clear() {
|
||||||
this.selected.clear();
|
this.selectedValues.clear();
|
||||||
this.table.remove(1, this.table.getItemCount());
|
this.selectionControls
|
||||||
final List<String> names = this.mapping
|
|
||||||
.stream()
|
.stream()
|
||||||
.map(t -> t._2)
|
.forEach(t -> {
|
||||||
.collect(Collectors.toList());
|
t._1.dispose();
|
||||||
this.combo.setItems(names.toArray(new String[names.size()]));
|
t._2.dispose();
|
||||||
|
});
|
||||||
|
this.selectionControls.clear();
|
||||||
|
this.combo.setItems(this.mapping.values().toArray(new String[this.mapping.size()]));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addComboSelection(final Event event) {
|
private void addComboSelection(final Event event) {
|
||||||
|
final int selectionIndex = this.combo.getSelectionIndex();
|
||||||
|
if (selectionIndex < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String itemName = this.combo.getItem(selectionIndex);
|
||||||
|
if (itemName == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Optional<Entry<String, String>> findFirst = this.mapping.entrySet()
|
||||||
|
.stream()
|
||||||
|
.filter(entity -> entity.getValue().equals(itemName))
|
||||||
|
.findFirst();
|
||||||
|
|
||||||
|
if (!findFirst.isPresent()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addSelection(findFirst.get().getKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addSelection(final String itemKey) {
|
||||||
|
final String itemName = this.mapping.get(itemKey);
|
||||||
|
if (itemName == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.selectedValues.add(new Tuple<>(itemKey, itemName));
|
||||||
|
final Label label = this.widgetFactory.label(this, itemName);
|
||||||
|
final Label imageButton = this.widgetFactory.imageButton(
|
||||||
|
ImageIcon.REMOVE_BOX,
|
||||||
|
this,
|
||||||
|
new LocTextKey("Remove"),
|
||||||
|
this::removeComboSelection);
|
||||||
|
imageButton.setData(SELECTION_KEY, itemName);
|
||||||
|
final GridData actionCell = new GridData(SWT.LEFT, SWT.CENTER, true, false);
|
||||||
|
actionCell.widthHint = ACTION_COLUMN_WIDTH;
|
||||||
|
imageButton.setLayoutData(actionCell);
|
||||||
|
|
||||||
|
this.selectionControls.add(new Tuple<>(label, imageButton));
|
||||||
|
|
||||||
|
this.combo.remove(itemName);
|
||||||
|
this.getParent().layout();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeComboSelection(final Event event) {
|
||||||
|
if (event.widget == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String selectionKey = (String) event.widget.getData(SELECTION_KEY);
|
||||||
|
final Optional<Tuple<Control>> findFirst = this.selectionControls.stream()
|
||||||
|
.filter(t -> selectionKey.equals(t._2.getData(SELECTION_KEY)))
|
||||||
|
.findFirst();
|
||||||
|
if (!findFirst.isPresent()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Tuple<Control> tuple = findFirst.get();
|
||||||
|
final int indexOf = this.selectionControls.indexOf(tuple);
|
||||||
|
this.selectionControls.remove(tuple);
|
||||||
|
|
||||||
|
tuple._1.dispose();
|
||||||
|
tuple._2.dispose();
|
||||||
|
|
||||||
|
final Tuple<String> value = this.selectedValues.remove(indexOf);
|
||||||
|
this.combo.add(value._2, this.combo.getItemCount());
|
||||||
|
|
||||||
|
this.getParent().layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void adaptColumnWidth(final Event event) {
|
private void adaptColumnWidth(final Event event) {
|
||||||
try {
|
try {
|
||||||
final int currentTableWidth = this.table.getParent().getClientArea().width;
|
final int currentTableWidth = this.getClientArea().width;
|
||||||
this.table.getColumn(0).setWidth(currentTableWidth - ACTION_COLUMN_WIDTH);
|
final GridData comboCell = (GridData) this.combo.getLayoutData();
|
||||||
|
comboCell.widthHint = currentTableWidth - ACTION_COLUMN_WIDTH;
|
||||||
|
this.layout();
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
log.warn("Failed to adaptColumnWidth: ", e);
|
log.warn("Failed to adaptColumnWidth: ", e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,8 @@ public interface Selection {
|
||||||
enum Type {
|
enum Type {
|
||||||
SINGLE,
|
SINGLE,
|
||||||
MULTI,
|
MULTI,
|
||||||
MULTI_COMBO
|
MULTI_COMBO,
|
||||||
|
COLOR,
|
||||||
}
|
}
|
||||||
|
|
||||||
Type type();
|
Type type();
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
/*
|
||||||
|
* 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.widget;
|
||||||
|
|
||||||
|
public class ThresholdList {
|
||||||
|
|
||||||
|
}
|
|
@ -62,6 +62,8 @@ public class WidgetFactory {
|
||||||
MINIMIZE("minimize.png"),
|
MINIMIZE("minimize.png"),
|
||||||
ADD("add.png"),
|
ADD("add.png"),
|
||||||
REMOVE("remove.png"),
|
REMOVE("remove.png"),
|
||||||
|
ADD_BOX("add_box.png"),
|
||||||
|
REMOVE_BOX("remove_box.png"),
|
||||||
EDIT("edit.png"),
|
EDIT("edit.png"),
|
||||||
TEST("test.png"),
|
TEST("test.png"),
|
||||||
IMPORT("import.png"),
|
IMPORT("import.png"),
|
||||||
|
@ -73,7 +75,8 @@ public class WidgetFactory {
|
||||||
SAVE("save.png"),
|
SAVE("save.png"),
|
||||||
NEW("new.png"),
|
NEW("new.png"),
|
||||||
DELETE("delete.png"),
|
DELETE("delete.png"),
|
||||||
SEARCH("lens.png");
|
SEARCH("lens.png"),
|
||||||
|
COLOR("color.png");
|
||||||
|
|
||||||
private String fileName;
|
private String fileName;
|
||||||
private ImageData image = null;
|
private ImageData image = null;
|
||||||
|
@ -325,49 +328,21 @@ public class WidgetFactory {
|
||||||
case MULTI_COMBO:
|
case MULTI_COMBO:
|
||||||
selection = new MultiSelectionCombo(parent, this);
|
selection = new MultiSelectionCombo(parent, this);
|
||||||
break;
|
break;
|
||||||
|
case COLOR:
|
||||||
|
selection = new ColorSelection(parent, this);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unsupported Selection.Type: " + type);
|
throw new IllegalArgumentException("Unsupported Selection.Type: " + type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (itemsSupplier != null) {
|
||||||
final Consumer<Selection> updateFunction = ss -> ss.applyNewMapping(itemsSupplier.get());
|
final Consumer<Selection> updateFunction = ss -> ss.applyNewMapping(itemsSupplier.get());
|
||||||
selection.adaptToControl().setData(POLYGLOT_WIDGET_FUNCTION_KEY, updateFunction);
|
selection.adaptToControl().setData(POLYGLOT_WIDGET_FUNCTION_KEY, updateFunction);
|
||||||
updateFunction.accept(selection);
|
updateFunction.accept(selection);
|
||||||
|
}
|
||||||
return selection;
|
return selection;
|
||||||
}
|
}
|
||||||
|
|
||||||
// public SingleSelection singleSelectionLocalized(
|
|
||||||
// final Composite parent,
|
|
||||||
// final Supplier<List<Tuple<String>>> itemsSupplier) {
|
|
||||||
//
|
|
||||||
// final SingleSelection singleSelection = new SingleSelection(parent);
|
|
||||||
// final Consumer<SingleSelection> updateFunction = ss -> ss.applyNewMapping(itemsSupplier.get());
|
|
||||||
// singleSelection.setData(POLYGLOT_WIDGET_FUNCTION_KEY, updateFunction);
|
|
||||||
// updateFunction.accept(singleSelection);
|
|
||||||
// return singleSelection;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public MultiSelection multiSelectionLocalized(
|
|
||||||
// final Composite parent,
|
|
||||||
// final Supplier<List<Tuple<String>>> itemsSupplier) {
|
|
||||||
//
|
|
||||||
// final MultiSelection multiSelection = new MultiSelection(parent);
|
|
||||||
// final Consumer<MultiSelection> updateFunction = ss -> ss.applyNewMapping(itemsSupplier.get());
|
|
||||||
// multiSelection.setData(POLYGLOT_WIDGET_FUNCTION_KEY, updateFunction);
|
|
||||||
// updateFunction.accept(multiSelection);
|
|
||||||
// return multiSelection;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public MultiSelectionCombo multiSelectionComboLocalized(
|
|
||||||
// final Composite parent,
|
|
||||||
// final Supplier<List<Tuple<String>>> itemsSupplier) {
|
|
||||||
//
|
|
||||||
// final MultiSelectionCombo multiSelection = new MultiSelectionCombo(parent, this);
|
|
||||||
// final Consumer<MultiSelectionCombo> updateFunction = ss -> ss.applyNewMapping(itemsSupplier.get());
|
|
||||||
// multiSelection.setData(POLYGLOT_WIDGET_FUNCTION_KEY, updateFunction);
|
|
||||||
// updateFunction.accept(multiSelection);
|
|
||||||
// return multiSelection;
|
|
||||||
// }
|
|
||||||
|
|
||||||
public ImageUpload imageUploadLocalized(
|
public ImageUpload imageUploadLocalized(
|
||||||
final Composite parent,
|
final Composite parent,
|
||||||
final LocTextKey locTextKey,
|
final LocTextKey locTextKey,
|
||||||
|
|
|
@ -107,7 +107,7 @@ public class AuthorizationServiceImpl implements AuthorizationService {
|
||||||
.withInstitutionalPrivilege(PrivilegeType.MODIFY)
|
.withInstitutionalPrivilege(PrivilegeType.MODIFY)
|
||||||
.withOwnerPrivilege(PrivilegeType.WRITE)
|
.withOwnerPrivilege(PrivilegeType.WRITE)
|
||||||
.andForRole(UserRole.EXAM_SUPPORTER)
|
.andForRole(UserRole.EXAM_SUPPORTER)
|
||||||
.withOwnerPrivilege(PrivilegeType.MODIFY)
|
.withInstitutionalPrivilege(PrivilegeType.READ_ONLY)
|
||||||
.create();
|
.create();
|
||||||
|
|
||||||
// grants for indicators
|
// grants for indicators
|
||||||
|
@ -120,7 +120,7 @@ public class AuthorizationServiceImpl implements AuthorizationService {
|
||||||
.withInstitutionalPrivilege(PrivilegeType.MODIFY)
|
.withInstitutionalPrivilege(PrivilegeType.MODIFY)
|
||||||
.withOwnerPrivilege(PrivilegeType.WRITE)
|
.withOwnerPrivilege(PrivilegeType.WRITE)
|
||||||
.andForRole(UserRole.EXAM_SUPPORTER)
|
.andForRole(UserRole.EXAM_SUPPORTER)
|
||||||
.withOwnerPrivilege(PrivilegeType.MODIFY)
|
.withInstitutionalPrivilege(PrivilegeType.READ_ONLY)
|
||||||
.create();
|
.create();
|
||||||
|
|
||||||
// TODO other entities
|
// TODO other entities
|
||||||
|
|
|
@ -12,6 +12,8 @@ import java.io.UnsupportedEncodingException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
import org.apache.commons.lang3.RandomStringUtils;
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
|
import org.cryptonode.jncryptor.AES256JNCryptor;
|
||||||
|
import org.cryptonode.jncryptor.JNCryptor;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
@ -35,6 +37,9 @@ public class ClientCredentialServiceImpl implements ClientCredentialService {
|
||||||
|
|
||||||
private final Environment environment;
|
private final Environment environment;
|
||||||
|
|
||||||
|
// TODO try to integrate with JNCryptor since this is also used by SEB
|
||||||
|
private final JNCryptor cryptor = new AES256JNCryptor();
|
||||||
|
|
||||||
protected ClientCredentialServiceImpl(final Environment environment) {
|
protected ClientCredentialServiceImpl(final Environment environment) {
|
||||||
this.environment = environment;
|
this.environment = environment;
|
||||||
}
|
}
|
||||||
|
@ -142,6 +147,7 @@ public class ClientCredentialServiceImpl implements ClientCredentialService {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
return Encryptors
|
return Encryptors
|
||||||
.delux(secret, getSalt(salt))
|
.delux(secret, getSalt(salt))
|
||||||
.encrypt(text.toString());
|
.encrypt(text.toString());
|
||||||
|
|
|
@ -95,7 +95,9 @@ public interface EntityDAO<T extends Entity, M extends ModelIdAware> {
|
||||||
* reported exception on error case */
|
* reported exception on error case */
|
||||||
Result<T> save(T data);
|
Result<T> save(T data);
|
||||||
|
|
||||||
/** Use this to delete a set Entity by a Collection of EntityKey
|
/** Use this to delete all entities defined by a set of EntityKey
|
||||||
|
* NOTE: the Set of EntityKey may contain EntityKey of other entity types like the concrete type of the DAO
|
||||||
|
* use extractPKsFromKeys to get a list of concrete primary keys for entities to delete
|
||||||
*
|
*
|
||||||
* @param all The Collection of EntityKey to delete
|
* @param all The Collection of EntityKey to delete
|
||||||
* @return Result referring a collection of all entities that has been deleted or refer to an error if
|
* @return Result referring a collection of all entities that has been deleted or refer to an error if
|
||||||
|
|
|
@ -261,8 +261,6 @@ public class IndicatorDAOImpl implements IndicatorDAO {
|
||||||
.execute()
|
.execute()
|
||||||
.stream()
|
.stream()
|
||||||
.map(tRec -> new Threshold(
|
.map(tRec -> new Threshold(
|
||||||
tRec.getId(),
|
|
||||||
tRec.getIndicatorId(),
|
|
||||||
tRec.getValue().doubleValue(),
|
tRec.getValue().doubleValue(),
|
||||||
tRec.getColor()))
|
tRec.getColor()))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
@ -270,8 +268,8 @@ public class IndicatorDAOImpl implements IndicatorDAO {
|
||||||
return new Indicator(
|
return new Indicator(
|
||||||
record.getId(),
|
record.getId(),
|
||||||
examRecord.getInstitutionId(),
|
examRecord.getInstitutionId(),
|
||||||
examRecord.getOwner(),
|
|
||||||
record.getExamId(),
|
record.getExamId(),
|
||||||
|
examRecord.getOwner(),
|
||||||
record.getName(),
|
record.getName(),
|
||||||
IndicatorType.valueOf(record.getType()),
|
IndicatorType.valueOf(record.getType()),
|
||||||
record.getColor(),
|
record.getColor(),
|
||||||
|
|
|
@ -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-01 09:00:00", "2021-01-01 09:00:00", "http://lms.mockup.com/api/"));
|
"2020-01-01T09:00:00", "2021-01-01T09:00:00", "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-01 09:00:00", "2021-01-01 09:00:00", "http://lms.mockup.com/api/"));
|
"2020-01-01T09:00:00", "2021-01-01T09:00:00", "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-30 09:00:00", "2018-08-01 00:00:00", "http://lms.mockup.com/api/"));
|
"2018-07-30T09:00:00", "2018-08-01T00:00:00", "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-01 00:00:00", "2019-01-01 00:00:00", "http://lms.mockup.com/api/"));
|
"2018-01-01T00:00:00", "2019-01-01T00:00:00", "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-01 09:00:00", "2021-01-01 09:00:00", "http://lms.mockup.com/api/"));
|
"2018-01-01T09:00:00", "2021-01-01T09:00:00", "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-01 09:00:00", "2021-01-01 09:00:00", "http://lms.mockup.com/api/"));
|
"2018-01-01T09:00:00", "2021-01-01T09:00:00", "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-01 09:00:00", "2021-01-01 09:00:00", "http://lms.mockup.com/api/"));
|
"2018-01-01T09:00:00", "2021-01-01T09:00:00", "http://lms.mockup.com/api/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -25,9 +25,13 @@ import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.http.client.ClientHttpRequest;
|
||||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||||
import org.springframework.security.access.AccessDeniedException;
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
|
import org.springframework.security.oauth2.client.OAuth2ClientContext;
|
||||||
|
import org.springframework.security.oauth2.client.OAuth2RequestAuthenticator;
|
||||||
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
|
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
|
||||||
|
import org.springframework.security.oauth2.client.http.AccessTokenRequiredException;
|
||||||
import org.springframework.security.oauth2.client.resource.OAuth2AccessDeniedException;
|
import org.springframework.security.oauth2.client.resource.OAuth2AccessDeniedException;
|
||||||
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
|
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
|
||||||
import org.springframework.security.oauth2.client.resource.UserRedirectRequiredException;
|
import org.springframework.security.oauth2.client.resource.UserRedirectRequiredException;
|
||||||
|
@ -115,6 +119,19 @@ final class OpenEdxLmsAPITemplate implements LmsAPITemplate {
|
||||||
this.knownTokenAccessPaths);
|
this.knownTokenAccessPaths);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.getEdxPage(this.lmsSetup.lmsApiUrl + OPEN_EDX_DEFAULT_COURSE_ENDPOINT);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
if (this.restTemplate != null) {
|
||||||
|
this.restTemplate.setAuthenticator(new EdxOAuth2RequestAuthenticator());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
this.getEdxPage(this.lmsSetup.lmsApiUrl + OPEN_EDX_DEFAULT_COURSE_ENDPOINT);
|
||||||
|
} catch (final Exception ee) {
|
||||||
|
return LmsSetupTestResult.ofQuizRequestError(ee.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return LmsSetupTestResult.ofOkay();
|
return LmsSetupTestResult.ofOkay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,28 +209,6 @@ final class OpenEdxLmsAPITemplate implements LmsAPITemplate {
|
||||||
return template;
|
return template;
|
||||||
}
|
}
|
||||||
|
|
||||||
// private Result<List<QuizData>> getAllQuizes(final LmsSetup lmsSetup) {
|
|
||||||
// if (this.allQuizzesSupplier == null) {
|
|
||||||
// this.allQuizzesSupplier = new CircuitBreaker<>(
|
|
||||||
// () -> collectAllCourses(lmsSetup.lmsApiUrl + OPEN_EDX_DEFAULT_COURSE_ENDPOINT)
|
|
||||||
// .stream()
|
|
||||||
// .reduce(
|
|
||||||
// new ArrayList<QuizData>(),
|
|
||||||
// (list, courseData) -> {
|
|
||||||
// list.add(quizDataOf(lmsSetup, courseData));
|
|
||||||
// return list;
|
|
||||||
// },
|
|
||||||
// (list1, list2) -> {
|
|
||||||
// list1.addAll(list2);
|
|
||||||
// return list1;
|
|
||||||
// }),
|
|
||||||
// 5, 1000L); // TODO specify better CircuitBreaker params
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return this.allQuizzesSupplier.get();
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
|
|
||||||
private Supplier<List<QuizData>> allQuizzesSupplier(final LmsSetup lmsSetup) {
|
private Supplier<List<QuizData>> allQuizzesSupplier(final LmsSetup lmsSetup) {
|
||||||
return () -> {
|
return () -> {
|
||||||
return initRestTemplateAndRequestAccessToken()
|
return initRestTemplateAndRequestAccessToken()
|
||||||
|
@ -320,8 +315,27 @@ final class OpenEdxLmsAPITemplate implements LmsAPITemplate {
|
||||||
params.add("client_id", resource.getClientId());
|
params.add("client_id", resource.getClientId());
|
||||||
params.add("client_secret", resource.getClientSecret());
|
params.add("client_secret", resource.getClientSecret());
|
||||||
|
|
||||||
return retrieveToken(request, resource, params, headers);
|
final OAuth2AccessToken retrieveToken = retrieveToken(request, resource, params, headers);
|
||||||
|
return retrieveToken;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class EdxOAuth2RequestAuthenticator implements OAuth2RequestAuthenticator {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void authenticate(
|
||||||
|
final OAuth2ProtectedResourceDetails resource,
|
||||||
|
final OAuth2ClientContext clientContext,
|
||||||
|
final ClientHttpRequest request) {
|
||||||
|
|
||||||
|
final OAuth2AccessToken accessToken = clientContext.getAccessToken();
|
||||||
|
if (accessToken == null) {
|
||||||
|
throw new AccessTokenRequiredException(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
request.getHeaders().set("Authorization", String.format("%s %s", "JWT:", accessToken.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
* 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.webservice.weblayer.api;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.seb.PingResponse;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.seb.RunningExams;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||||
|
|
||||||
|
@WebServiceProfile
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/${sebserver.webservice.api.exam.endpoint}")
|
||||||
|
public class ExamAPIController {
|
||||||
|
|
||||||
|
@RequestMapping(
|
||||||
|
path = API.EXAM_API_HANDSHAKE_ENDPOINT,
|
||||||
|
method = RequestMethod.GET,
|
||||||
|
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||||
|
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||||
|
public RunningExams handshake(
|
||||||
|
@RequestParam(name = API.PARAM_INSTITUTION_ID, required = true) final Long institutionId,
|
||||||
|
final HttpServletRequest request,
|
||||||
|
final HttpServletResponse response) {
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(
|
||||||
|
path = API.EXAM_API_CONFIGURATION_REQUEST_ENDPOINT,
|
||||||
|
method = RequestMethod.GET,
|
||||||
|
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||||
|
produces = MediaType.TEXT_XML_VALUE)
|
||||||
|
public RunningExams getConfig(
|
||||||
|
@RequestParam(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken,
|
||||||
|
@RequestParam(name = API.EXAM_API_PARAM_EXAM_ID, required = true) final String examId,
|
||||||
|
final HttpServletRequest request,
|
||||||
|
final HttpServletResponse response) {
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(
|
||||||
|
path = API.EXAM_API_PING_ENDPOINT,
|
||||||
|
method = RequestMethod.PUT,
|
||||||
|
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||||
|
produces = MediaType.TEXT_XML_VALUE)
|
||||||
|
public PingResponse ping(
|
||||||
|
@RequestParam(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken,
|
||||||
|
final HttpServletRequest request,
|
||||||
|
final HttpServletResponse response) {
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(
|
||||||
|
path = API.EXAM_API_EVENT_ENDPOINT,
|
||||||
|
method = RequestMethod.POST,
|
||||||
|
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||||
|
public void event(
|
||||||
|
@RequestParam(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken,
|
||||||
|
final HttpServletRequest request,
|
||||||
|
final HttpServletResponse response) {
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||||
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
|
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
|
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
|
||||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.IndicatorRecordDynamicSqlSupport;
|
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.IndicatorRecordDynamicSqlSupport;
|
||||||
|
@ -28,11 +29,11 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe
|
||||||
@WebServiceProfile
|
@WebServiceProfile
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + API.EXAM_INDICATOR_ENDPOINT)
|
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + API.EXAM_INDICATOR_ENDPOINT)
|
||||||
public class ExamIndicatorController extends EntityController<Indicator, Indicator> {
|
public class IndicatorController extends EntityController<Indicator, Indicator> {
|
||||||
|
|
||||||
private final ExamDAO examDao;
|
private final ExamDAO examDao;
|
||||||
|
|
||||||
protected ExamIndicatorController(
|
protected IndicatorController(
|
||||||
final AuthorizationService authorization,
|
final AuthorizationService authorization,
|
||||||
final BulkActionService bulkActionService,
|
final BulkActionService bulkActionService,
|
||||||
final IndicatorDAO entityDAO,
|
final IndicatorDAO entityDAO,
|
||||||
|
@ -53,7 +54,7 @@ public class ExamIndicatorController extends EntityController<Indicator, Indicat
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Indicator createNew(final POSTMapper postParams) {
|
protected Indicator createNew(final POSTMapper postParams) {
|
||||||
final Long examId = postParams.getLong(API.PARAM_EXAM_ID);
|
final Long examId = postParams.getLong(Domain.INDICATOR.ATTR_EXAM_ID);
|
||||||
|
|
||||||
return this.examDao
|
return this.examDao
|
||||||
.byPK(examId)
|
.byPK(examId)
|
|
@ -149,6 +149,31 @@ public class UserAccountController extends ActivatableEntityController<UserInfo,
|
||||||
.flatMap(this::additionalConsistencyChecks);
|
.flatMap(this::additionalConsistencyChecks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequestMapping(
|
||||||
|
path = API.PASSWORD_PATH_SEGMENT,
|
||||||
|
method = RequestMethod.PUT,
|
||||||
|
consumes = MediaType.APPLICATION_JSON_VALUE,
|
||||||
|
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
public UserInfo changePassword(@Valid @RequestBody final PasswordChange passwordChange) {
|
||||||
|
|
||||||
|
final String modelId = passwordChange.getModelId();
|
||||||
|
return this.userDAO.byModelId(modelId)
|
||||||
|
.flatMap(this.authorization::checkModify)
|
||||||
|
.map(ui -> checkPasswordChange(ui, passwordChange))
|
||||||
|
.flatMap(e -> this.userDAO.changePassword(modelId, passwordChange.getNewPassword()))
|
||||||
|
.flatMap(this::revokeAccessToken)
|
||||||
|
.flatMap(e -> this.userActivityLogDAO.log(ActivityType.PASSWORD_CHANGE, e))
|
||||||
|
.getOrThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result<UserInfo> revokeAccessToken(final UserInfo userInfo) {
|
||||||
|
return Result.tryCatch(() -> {
|
||||||
|
this.applicationEventPublisher.publishEvent(
|
||||||
|
new RevokeTokenEndpoint.RevokeTokenEvent(userInfo, userInfo.username));
|
||||||
|
return userInfo;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/** Additional consistency checks that has to be checked before create and save actions */
|
/** Additional consistency checks that has to be checked before create and save actions */
|
||||||
private <T extends UserAccount> Result<T> additionalConsistencyChecks(final T userInfo) {
|
private <T extends UserAccount> Result<T> additionalConsistencyChecks(final T userInfo) {
|
||||||
return Result.tryCatch(() -> {
|
return Result.tryCatch(() -> {
|
||||||
|
@ -194,29 +219,4 @@ public class UserAccountController extends ActivatableEntityController<UserInfo,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(
|
|
||||||
path = API.PASSWORD_PATH_SEGMENT,
|
|
||||||
method = RequestMethod.PUT,
|
|
||||||
consumes = MediaType.APPLICATION_JSON_VALUE,
|
|
||||||
produces = MediaType.APPLICATION_JSON_VALUE)
|
|
||||||
public UserInfo changePassword(@Valid @RequestBody final PasswordChange passwordChange) {
|
|
||||||
|
|
||||||
final String modelId = passwordChange.getModelId();
|
|
||||||
return this.userDAO.byModelId(modelId)
|
|
||||||
.flatMap(this.authorization::checkModify)
|
|
||||||
.map(ui -> checkPasswordChange(ui, passwordChange))
|
|
||||||
.flatMap(e -> this.userDAO.changePassword(modelId, passwordChange.getNewPassword()))
|
|
||||||
.flatMap(this::revokeAccessToken)
|
|
||||||
.flatMap(e -> this.userActivityLogDAO.log(ActivityType.PASSWORD_CHANGE, e))
|
|
||||||
.getOrThrow();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Result<UserInfo> revokeAccessToken(final UserInfo userInfo) {
|
|
||||||
return Result.tryCatch(() -> {
|
|
||||||
this.applicationEventPublisher.publishEvent(
|
|
||||||
new RevokeTokenEndpoint.RevokeTokenEvent(userInfo, userInfo.username));
|
|
||||||
return userInfo;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,9 +77,9 @@ sebserver.institution.list.column.active=Active
|
||||||
sebserver.institution.action.list=Institution
|
sebserver.institution.action.list=Institution
|
||||||
sebserver.institution.action.form=Institution
|
sebserver.institution.action.form=Institution
|
||||||
sebserver.institution.action.new=New Institution
|
sebserver.institution.action.new=New Institution
|
||||||
sebserver.institution.action.list.view=View Selected
|
sebserver.institution.action.list.view=View Institution
|
||||||
sebserver.institution.action.modify=Edit Institution
|
sebserver.institution.action.list.modify=Edit Institution
|
||||||
sebserver.institution.action.list.modify=Edit Selected
|
sebserver.institution.action.modify=Edit
|
||||||
sebserver.institution.action.save=Save Institution
|
sebserver.institution.action.save=Save Institution
|
||||||
sebserver.institution.action.activate=Activate
|
sebserver.institution.action.activate=Activate
|
||||||
sebserver.institution.action.deactivate=Deactivate
|
sebserver.institution.action.deactivate=Deactivate
|
||||||
|
@ -117,8 +117,8 @@ sebserver.useraccount.list.column.active=Active
|
||||||
sebserver.useraccount.action.list=User Account
|
sebserver.useraccount.action.list=User Account
|
||||||
sebserver.useraccount.action.form=User Account of {0}
|
sebserver.useraccount.action.form=User Account of {0}
|
||||||
sebserver.useraccount.action.new=New User Account
|
sebserver.useraccount.action.new=New User Account
|
||||||
sebserver.useraccount.action.view=View Selected
|
sebserver.useraccount.action.view=View User Account
|
||||||
sebserver.useraccount.action.list.modify=Edit Selected
|
sebserver.useraccount.action.list.modify=Edit User Account
|
||||||
sebserver.useraccount.action.modify=Edit
|
sebserver.useraccount.action.modify=Edit
|
||||||
sebserver.useraccount.action.save=Save User Account
|
sebserver.useraccount.action.save=Save User Account
|
||||||
sebserver.useraccount.action.activate=Activate
|
sebserver.useraccount.action.activate=Activate
|
||||||
|
@ -165,8 +165,8 @@ sebserver.lmssetup.list.column.active=Active
|
||||||
sebserver.lmssetup.action.list=LMS Setup
|
sebserver.lmssetup.action.list=LMS Setup
|
||||||
sebserver.lmssetup.action.form=LMS Setup
|
sebserver.lmssetup.action.form=LMS Setup
|
||||||
sebserver.lmssetup.action.new=New LMS Setup
|
sebserver.lmssetup.action.new=New LMS Setup
|
||||||
sebserver.lmssetup.action.list.view=View Selected
|
sebserver.lmssetup.action.list.view=View LMS Setup
|
||||||
sebserver.lmssetup.action.list.modify=Edit Selected
|
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 connect to the LMSs course API
|
||||||
|
@ -225,6 +225,9 @@ sebserver.exam.list.column.type=Type
|
||||||
sebserver.exam.list.empty=No Exams has been found. Please adapt the filter or import one from Quiz
|
sebserver.exam.list.empty=No Exams has been found. Please adapt the filter or import one from Quiz
|
||||||
|
|
||||||
sebserver.exam.action.list=Exam
|
sebserver.exam.action.list=Exam
|
||||||
|
sebserver.exam.action.list.view=View Exam
|
||||||
|
sebserver.exam.action.list.modify=Edit Exam
|
||||||
|
sebserver.exam.action.modify=Edit
|
||||||
sebserver.exam.action.import=Import From Quizzes
|
sebserver.exam.action.import=Import From Quizzes
|
||||||
sebserver.exam.action.save=Save
|
sebserver.exam.action.save=Save
|
||||||
sebserver.exam.action.activate=Activate
|
sebserver.exam.action.activate=Activate
|
||||||
|
@ -242,6 +245,7 @@ sebserver.exam.form.starttime=Start Time
|
||||||
sebserver.exam.form.endtime=End Time
|
sebserver.exam.form.endtime=End Time
|
||||||
sebserver.exam.form.status=Status
|
sebserver.exam.form.status=Status
|
||||||
sebserver.exam.form.type=Exam Type
|
sebserver.exam.form.type=Exam Type
|
||||||
|
sebserver.exam.form.supporter=Exam Supporter
|
||||||
|
|
||||||
sebserver.exam.type.UNDEFINED=Not Defined
|
sebserver.exam.type.UNDEFINED=Not Defined
|
||||||
sebserver.exam.type.MANAGED=Managed
|
sebserver.exam.type.MANAGED=Managed
|
||||||
|
@ -252,8 +256,9 @@ sebserver.exam.indicator.list.actions=Selected Indicator
|
||||||
sebserver.exam.indicator.list.title=Indicators
|
sebserver.exam.indicator.list.title=Indicators
|
||||||
sebserver.exam.indicator.list.column.type=Type
|
sebserver.exam.indicator.list.column.type=Type
|
||||||
sebserver.exam.indicator.list.column.name=Name
|
sebserver.exam.indicator.list.column.name=Name
|
||||||
sebserver.exam.indicator.list.column.thresholds
|
sebserver.exam.indicator.list.column.thresholds=Thresholds
|
||||||
sebserver.exam.indicator.list.empty=There is currently no Indicator defined for this Exam. Please create a new one
|
sebserver.exam.indicator.list.empty=There is currently no Indicator defined for this Exam. Please create a new one
|
||||||
|
sebserver.exam.indicator.list.pleaseSelect=Please Select an Indicator first
|
||||||
|
|
||||||
sebserver.exam.indicator.type.LAST_PING=Last Ping
|
sebserver.exam.indicator.type.LAST_PING=Last Ping
|
||||||
sebserver.exam.indicator.type.ERROR_COUNT=Error Count
|
sebserver.exam.indicator.type.ERROR_COUNT=Error Count
|
||||||
|
@ -262,4 +267,13 @@ 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=Modify Indicator
|
||||||
|
sebserver.exam.indicator.action.save=Save
|
||||||
|
|
||||||
|
sebserver.exam.indicator.form.title=Indicator
|
||||||
|
sebserver.exam.indicator.form.title.new=New Indicator
|
||||||
|
sebserver.exam.indicator.form.exam=Exam
|
||||||
|
sebserver.exam.indicator.form.name=Name
|
||||||
|
sebserver.exam.indicator.form.type=Type
|
||||||
|
sebserver.exam.indicator.form.color=Color
|
||||||
|
|
||||||
|
sebserver.exam.indicator.thresholds.list.title=Thresholds
|
||||||
|
|
|
@ -345,9 +345,7 @@ Button {
|
||||||
|
|
||||||
/* Push Buttons */
|
/* Push Buttons */
|
||||||
Button[PUSH],
|
Button[PUSH],
|
||||||
Button[PUSH]:default,
|
Button[PUSH]:default {
|
||||||
FileUpload,
|
|
||||||
FileUpload:default {
|
|
||||||
font: bold 12px Arial, Helvetica, sans-serif;
|
font: bold 12px Arial, Helvetica, sans-serif;
|
||||||
background-color: #0069B4;
|
background-color: #0069B4;
|
||||||
background-gradient-color: #0069B4;
|
background-gradient-color: #0069B4;
|
||||||
|
@ -359,16 +357,14 @@ FileUpload:default {
|
||||||
text-shadow: none;
|
text-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
Button[PUSH]:pressed,
|
Button[PUSH]:pressed {
|
||||||
FileUpload:pressed {
|
|
||||||
background-color: #444;
|
background-color: #444;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-gradient-color: #444;
|
background-gradient-color: #444;
|
||||||
background-image: gradient( linear, left top, left bottom, from( #444 ), to( #444 ) );
|
background-image: gradient( linear, left top, left bottom, from( #444 ), to( #444 ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
Button[PUSH]:hover,
|
Button[PUSH]:hover {
|
||||||
FileUpload:hover {
|
|
||||||
background-color: #82BE1E;
|
background-color: #82BE1E;
|
||||||
background-gradient-color: #82BE1E;
|
background-gradient-color: #82BE1E;
|
||||||
background-image: gradient( linear, left top, left bottom, from( #82BE1E ), to( #82BE1E ) );
|
background-image: gradient( linear, left top, left bottom, from( #82BE1E ), to( #82BE1E ) );
|
||||||
|
@ -376,8 +372,7 @@ FileUpload:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
Button[PUSH]:disabled,
|
Button[PUSH]:disabled {
|
||||||
FileUpload:disabled {
|
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border: 1px solid #EAECEE;
|
border: 1px solid #EAECEE;
|
||||||
color: #c0c0c0;
|
color: #c0c0c0;
|
||||||
|
@ -418,6 +413,18 @@ Button[PUSH]:hover.header {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FileUpload,
|
||||||
|
FileUpload:default,
|
||||||
|
FileUpload:hover,
|
||||||
|
FileUpload:pressed {
|
||||||
|
background-color: transparent;
|
||||||
|
background-gradient-color: transparent;
|
||||||
|
background-image: gradient( linear, left top, left bottom, from( transparent ), to( transparent ) );
|
||||||
|
border: none;
|
||||||
|
border-radius: 0px;
|
||||||
|
text-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Sash default */
|
/* Sash default */
|
||||||
Sash {
|
Sash {
|
||||||
|
|
BIN
src/main/resources/static/images/add_box.png
Normal file
BIN
src/main/resources/static/images/add_box.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 142 B |
BIN
src/main/resources/static/images/color.png
Normal file
BIN
src/main/resources/static/images/color.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 223 B |
BIN
src/main/resources/static/images/remove_box.png
Normal file
BIN
src/main/resources/static/images/remove_box.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 124 B |
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ch.ethz.seb.sebserver.gui.widget;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import org.eclipse.swt.graphics.RGB;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class ColorSelectionTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseRGB() {
|
||||||
|
String colorString = "FFFFFF";
|
||||||
|
assertEquals(
|
||||||
|
"RGB {255, 255, 255}",
|
||||||
|
ColorSelection.parseRGB(colorString).toString());
|
||||||
|
|
||||||
|
colorString = "FFaa34";
|
||||||
|
assertEquals(
|
||||||
|
"RGB {255, 170, 52}",
|
||||||
|
ColorSelection.parseRGB(colorString).toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseColorString() {
|
||||||
|
final RGB color = new RGB(255, 255, 255);
|
||||||
|
assertEquals("ffffff", ColorSelection.parseColorString(color));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue