Merge remote-tracking branch 'origin/rel-1.5.2'
This commit is contained in:
commit
bf04341cf9
10 changed files with 100 additions and 18 deletions
2
pom.xml
2
pom.xml
|
@ -18,7 +18,7 @@
|
|||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<sebserver-version>1.5.1</sebserver-version>
|
||||
<sebserver-version>1.5.2</sebserver-version>
|
||||
<build-version>${sebserver-version}</build-version>
|
||||
<revision>${sebserver-version}</revision>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
|
|
|
@ -18,6 +18,7 @@ import org.eclipse.rap.rwt.RWT;
|
|||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.layout.GridData;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Control;
|
||||
import org.eclipse.swt.widgets.Label;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
@ -451,31 +452,28 @@ public class QuizLookupList implements TemplateComposer {
|
|||
}
|
||||
}
|
||||
|
||||
private Composite warningPanel = null;
|
||||
|
||||
private void handelPageReload(
|
||||
final Composite notePanel,
|
||||
final EntityTable<QuizData> table) {
|
||||
|
||||
if (table.isComplete()) {
|
||||
PageService.clearComposite(notePanel);
|
||||
if (this.warningPanel != null) {
|
||||
this.warningPanel.dispose();
|
||||
}
|
||||
this.warningPanel = null;
|
||||
} else {
|
||||
if (this.warningPanel != null && !this.warningPanel.isDisposed()) {
|
||||
this.warningPanel.dispose();
|
||||
|
||||
final Control[] children = notePanel.getChildren();
|
||||
if (children != null && children.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.warningPanel = this.widgetFactory.createWarningPanel(notePanel, 15, true);
|
||||
final Composite warningPanel = this.widgetFactory.createWarningPanel(notePanel, 15, true);
|
||||
warningPanel.setData("warningPanel", "TRUE");
|
||||
this.widgetFactory.imageButton(
|
||||
ImageIcon.SWITCH,
|
||||
this.warningPanel,
|
||||
warningPanel,
|
||||
TEXT_FETCH_NOTE_TOOLTIP,
|
||||
event -> table.applyFilter());
|
||||
|
||||
final Label text = new Label(this.warningPanel, SWT.NONE);
|
||||
final Label text = new Label(warningPanel, SWT.NONE);
|
||||
text.setData(RWT.MARKUP_ENABLED, Boolean.TRUE);
|
||||
text.setText(this.pageService.getI18nSupport().getText(TEXT_FETCH_NOTE));
|
||||
final GridData gridData = new GridData(SWT.LEFT, SWT.FILL, true, true);
|
||||
|
|
|
@ -224,6 +224,9 @@ public class EntityTable<ROW extends ModelIdAware> {
|
|||
}
|
||||
this.table.addListener(SWT.Selection, event -> {
|
||||
if (this.multiselection != null && event.item != null) {
|
||||
if (event.item == null || event.item.isDisposed()) {
|
||||
return;
|
||||
}
|
||||
handleMultiSelection((TableItem) event.item);
|
||||
event.doit = false;
|
||||
}
|
||||
|
@ -527,6 +530,9 @@ public class EntityTable<ROW extends ModelIdAware> {
|
|||
|
||||
// first remove all rows if there are some
|
||||
this.table.removeAll();
|
||||
if (this.multiselection != null) {
|
||||
this.multiselection.clear();
|
||||
}
|
||||
|
||||
// get page data and create rows
|
||||
final Page<ROW> page = this.pageSupplier.newBuilder()
|
||||
|
@ -642,10 +648,19 @@ public class EntityTable<ROW extends ModelIdAware> {
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
private ROW getRowData(final TableItem item) {
|
||||
if (item == null || item.isDisposed()) {
|
||||
log.warn("Selected item is null or disposed: {}", item);
|
||||
return null;
|
||||
}
|
||||
return (ROW) item.getData(TABLE_ROW_DATA);
|
||||
}
|
||||
|
||||
private EntityKey getEntityKey(final TableItem item) {
|
||||
if (item == null || item.isDisposed()) {
|
||||
log.warn("Selected item is null or disposed: {}", item);
|
||||
return null;
|
||||
}
|
||||
|
||||
final ROW rowData = getRowData(item);
|
||||
if (rowData instanceof Entity) {
|
||||
return ((Entity) rowData).getEntityKey();
|
||||
|
|
|
@ -98,7 +98,7 @@ public class PasswordInput extends Composite {
|
|||
final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
|
||||
passwordInput.setLayoutData(gridData);
|
||||
passwordInput.setText(value != null
|
||||
? Utils.escapeHTML_XML_EcmaScript(value)
|
||||
? value
|
||||
: StringUtils.EMPTY);
|
||||
if (!buildPassword) {
|
||||
passwordInput.setEditable(false);
|
||||
|
|
|
@ -536,6 +536,10 @@ public class MoodlePluginCourseAccess extends AbstractCachedCourseAccess impleme
|
|||
lmsSetup);
|
||||
}
|
||||
|
||||
final Set<String> qIdSet = internalIds.stream()
|
||||
.map(MoodleUtils::getQuizId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
return getCoursesForIds(restTemplate, moodleCourseIds)
|
||||
.stream()
|
||||
.filter(courseData -> !courseData.quizzes.isEmpty())
|
||||
|
@ -545,7 +549,7 @@ public class MoodlePluginCourseAccess extends AbstractCachedCourseAccess impleme
|
|||
urlPrefix,
|
||||
this.prependShortCourseName)
|
||||
.stream()
|
||||
.filter(q -> internalIds.contains(q.id)))
|
||||
.filter(q -> qIdSet.contains(MoodleUtils.getQuizId(q.id))))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
} catch (final Exception e) {
|
||||
|
|
|
@ -163,6 +163,23 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
|
|||
"lmsSetup:lmsClientsecret:notNull"));
|
||||
}
|
||||
|
||||
final Result<OlatLmsRestTemplate> restTemplateResult = getRestTemplate();
|
||||
if (restTemplateResult.hasError()) {
|
||||
missingAttrs.add(APIMessage.fieldValidationError(
|
||||
LMS_SETUP.ATTR_LMS_URL,
|
||||
"lmsSetup:lmsUrl:url.noservice"));
|
||||
} else {
|
||||
final OlatLmsRestTemplate olatLmsRestTemplate = restTemplateResult.get();
|
||||
try {
|
||||
olatLmsRestTemplate.testAuthentication();
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to test Authentication: {}", e.getMessage());
|
||||
missingAttrs.add(APIMessage.fieldValidationError(
|
||||
LMS_SETUP.ATTR_LMS_URL,
|
||||
"lmsSetup:lmsUrl:url.noaccess"));
|
||||
}
|
||||
}
|
||||
|
||||
if (!missingAttrs.isEmpty()) {
|
||||
return LmsSetupTestResult.ofMissingAttributes(LmsType.OPEN_OLAT, missingAttrs);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,12 @@ public class OlatLmsRestTemplate extends RestTemplate {
|
|||
private String token;
|
||||
private ClientCredentialsResourceDetails details;
|
||||
|
||||
public void testAuthentication() {
|
||||
if (this.token == null) {
|
||||
authenticate();
|
||||
}
|
||||
}
|
||||
|
||||
public OlatLmsRestTemplate(final ClientCredentialsResourceDetails details) {
|
||||
super();
|
||||
this.details = details;
|
||||
|
@ -59,6 +65,7 @@ public class OlatLmsRestTemplate extends RestTemplate {
|
|||
|
||||
return execution.execute(request, body);
|
||||
}
|
||||
|
||||
// otherwise, add the X-OLAT-TOKEN
|
||||
request.getHeaders().set("accept", "application/json");
|
||||
request.getHeaders().set("X-OLAT-TOKEN", OlatLmsRestTemplate.this.token);
|
||||
|
@ -85,6 +92,7 @@ public class OlatLmsRestTemplate extends RestTemplate {
|
|||
log.debug("OLAT [retry API call]: URL {}", request.getURI());
|
||||
}
|
||||
|
||||
response.close();
|
||||
response = execution.execute(request, body);
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
|
@ -97,7 +105,7 @@ public class OlatLmsRestTemplate extends RestTemplate {
|
|||
|
||||
} catch (final Exception e) {
|
||||
// TODO find a way to better deal with Olat temporary unavailability
|
||||
log.error("Unexpected error: ", e);
|
||||
log.error("Unexpected error: {}", e.getMessage());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.util.Collections;
|
|||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
|
@ -28,6 +29,7 @@ import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
|||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
|
||||
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.Features;
|
||||
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
|
@ -38,6 +40,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO;
|
|||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.SEBRestrictionService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleUtils;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamFinishedEvent;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamResetEvent;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamStartedEvent;
|
||||
|
@ -138,7 +141,12 @@ class ExamUpdateHandler {
|
|||
.forEach(quiz -> {
|
||||
|
||||
try {
|
||||
final Exam exam = exams.get(quiz.id);
|
||||
final Exam exam = getExamForQuizWithMoodleSpecialCase(exams, quiz);
|
||||
|
||||
if (exam == null) {
|
||||
log.warn("Failed to find map exam to fetched quiz-data: {}", quiz);
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasChanges(exam, quiz)) {
|
||||
|
||||
|
@ -356,7 +364,8 @@ class ExamUpdateHandler {
|
|||
!Objects.equals(exam.startTime, quizData.startTime) ||
|
||||
!Objects.equals(exam.endTime, quizData.endTime) ||
|
||||
!Utils.isEqualsWithEmptyCheckTruncated(exam.getDescription(), quizData.description) ||
|
||||
!Utils.isEqualsWithEmptyCheck(exam.getStartURL(), quizData.startURL)) {
|
||||
!Utils.isEqualsWithEmptyCheck(exam.getStartURL(), quizData.startURL) ||
|
||||
!Objects.equals(exam.externalId, quizData.id)) {
|
||||
|
||||
if (!Utils.isEqualsWithEmptyCheck(exam.name, quizData.name)) {
|
||||
log.info("Update name difference from LMS. Exam: {}, QuizData: {}", exam.name, quizData.name);
|
||||
|
@ -376,6 +385,11 @@ class ExamUpdateHandler {
|
|||
exam.getStartURL(),
|
||||
quizData.startURL);
|
||||
}
|
||||
if (!Objects.equals(exam.externalId, quizData.id)) {
|
||||
log.info("Update quizId difference from LMS. Exam:{}, QuizData: {}",
|
||||
exam.externalId,
|
||||
quizData.id);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -479,4 +493,29 @@ class ExamUpdateHandler {
|
|||
}
|
||||
}
|
||||
|
||||
private Exam getExamForQuizWithMoodleSpecialCase(final Map<String, Exam> exams, final QuizData quiz) {
|
||||
Exam exam = exams.get(quiz.id);
|
||||
|
||||
if (exam == null) {
|
||||
try {
|
||||
final LmsAPITemplate lms = this.lmsAPIService
|
||||
.getLmsAPITemplate(quiz.lmsSetupId)
|
||||
.getOrThrow();
|
||||
|
||||
if (lms.getType() == LmsType.MOODLE || lms.getType() == LmsType.MOODLE_PLUGIN) {
|
||||
final String quizId = MoodleUtils.getQuizId(quiz.id);
|
||||
final Optional<String> find =
|
||||
exams.keySet().stream().filter(key -> key.startsWith(quizId)).findFirst();
|
||||
if (find.isPresent()) {
|
||||
exam = exams.get(find.get());
|
||||
}
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to verify changed external Exam id from moodle course: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return exam;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ sebserver.webservice.clean-db-on-startup=false
|
|||
|
||||
# webservice configuration
|
||||
sebserver.init.adminaccount.gen-on-init=false
|
||||
sebserver.webservice.distributed=true
|
||||
sebserver.webservice.distributed=false
|
||||
#sebserver.webservice.master.delay.threshold=10000
|
||||
sebserver.webservice.http.external.scheme=http
|
||||
sebserver.webservice.http.external.servername=localhost
|
||||
|
|
|
@ -111,6 +111,7 @@ sebserver.form.validation.fieldError.serverNotAvailable=No service seems to be a
|
|||
sebserver.form.validation.fieldError.url.invalid=Invalid URL. The given URL cannot be reached.
|
||||
sebserver.form.validation.fieldError.typeInvalid=This type is not implemented yet and cannot be used.
|
||||
sebserver.form.validation.fieldError.url.noservice=The expected service is not available within the given URL and API access.
|
||||
sebserver.form.validation.fieldError.url.noaccess=There has no access been granted by the service. Please check the given access credentials.
|
||||
sebserver.form.validation.fieldError.thresholdDuplicate=There are duplicate threshold values.
|
||||
sebserver.form.validation.fieldError.thresholdEmpty=There are missing values or colors for the threshold declaration
|
||||
sebserver.form.validation.fieldError.invalidIP=Invalid IP v4. Please enter a valid IP-address (v4)
|
||||
|
|
Loading…
Reference in a new issue