diff --git a/src/main/java/org/olat/commons/coordinate/cluster/jms/ClusterEventBus.java b/src/main/java/org/olat/commons/coordinate/cluster/jms/ClusterEventBus.java index 6a374464e8f23d0e76c3c0f5ba1d51419ea25505..42bacb40554327c512dd04de3a9134b79d857d3f 100644 --- a/src/main/java/org/olat/commons/coordinate/cluster/jms/ClusterEventBus.java +++ b/src/main/java/org/olat/commons/coordinate/cluster/jms/ClusterEventBus.java @@ -207,6 +207,7 @@ public class ClusterEventBus extends AbstractEventBus implements MessageListener /** * this implementation must sum up all counts from all cluster nodes to return the correct number. */ + @Override public int getListeningIdentityCntFor(OLATResourceable ores) { return busInfos.getListenerCountFor(ores); } @@ -216,18 +217,15 @@ public class ClusterEventBus extends AbstractEventBus implements MessageListener * @see org.olat.core.util.event.AbstractOLATSystemBus#fireEventToListenersOf(org.olat.core.util.event.MultiUserEvent, * org.olat.core.id.OLATResourceable) */ + @Override public void fireEventToListenersOf(final MultiUserEvent event, final OLATResourceable ores) { // send the event wrapped over jms to all nodes // (the receiver will detect whether messages are from itself and thus can be ignored, since they were already sent directly. final long msgId = ++latestSentMsgId; final Integer nodeId = clusterConfig.getNodeId(); - if(ores != null && ores.getResourceableId() != null - && ores.getResourceableId().equals(0l) && "BusinessGroup".equals(ores.getResourceableTypeName())) { - System.out.println(); - } - jmsExecutor.execute(new Runnable() { + @Override public void run() { try { ObjectMessage message = sessionProducer.createObjectMessage(); @@ -260,20 +258,8 @@ public class ClusterEventBus extends AbstractEventBus implements MessageListener * called by springs org.springframework.jms.listener.DefaultMessageListenerContainer, see coredefaultconfig.xml * we receive a message here on the topic reserved for olat system bus messages. */ + @Override public void onMessage(Message message) { - /*synchronized(incomingMessagesQueue_) { - while(incomingMessagesQueue_.size()>LIMIT_ON_INCOMING_MESSAGE_QUEUE) { - try { - incomingMessagesQueue_.wait(); - } catch (InterruptedException e) { - // this empty catch is okay - } - } - incomingMessagesQueue_.addFirst(message); - incomingMessagesQueue_.addFirst(System.currentTimeMillis()); - incomingMessagesQueue_.notifyAll(); - }*/ - try{ serveMessage(message, -1); } catch(RuntimeException re) { @@ -325,13 +311,6 @@ public class ClusterEventBus extends AbstractEventBus implements MessageListener OLATResourceable ores = jmsWrapper.getOres(); boolean fromSameNode = clusterConfig.getNodeId().equals(nodeId); - //TODO jms update nodeinfo statistics, this doesn't work because we remove - //all the synchronization in the event bus - /* NodeInfo nodeInfo = getNodeInfoFor(nodeId); - if (log.isDebug() && !nodeInfo.update(jmsWrapper)) { - log.debug("onMessage: update failed. clustereventbus: "+this); - }*/ - String recMsg = "received msg: "+(fromSameNode? "[same node]":"")+" from node:" + nodeId + ", olat-id:" + jmsWrapper.getMsgId() + ", ores:" + ores.getResourceableTypeName() + ":" + ores.getResourceableId() + ", event:"+event+"}"; diff --git a/src/main/java/org/olat/core/util/mail/MailTemplate.java b/src/main/java/org/olat/core/util/mail/MailTemplate.java index e65cbf45d6434b3ff69e6d8be482966a4068ad7a..a81b2757cfb3e8fc49d8d21cec8bc28d0a717602 100644 --- a/src/main/java/org/olat/core/util/mail/MailTemplate.java +++ b/src/main/java/org/olat/core/util/mail/MailTemplate.java @@ -129,7 +129,7 @@ public abstract class MailTemplate { * @param context The context where to put the variables * @param recipient The current identity which will get the email */ - public abstract void putVariablesInMailContext(VelocityContext context, Identity recipient); + public abstract void putVariablesInMailContext(VelocityContext vContext, Identity recipient); public void addToContext(String name, String value) { context.put(name, value); diff --git a/src/main/java/org/olat/course/assessment/bulk/BulkAssessmentOverviewController.java b/src/main/java/org/olat/course/assessment/bulk/BulkAssessmentOverviewController.java index f63fcd4d776d41734b42f0774eb40706d06ba644..d158e3c2d3e9583c218b6868dbfafd67f3fe8716 100644 --- a/src/main/java/org/olat/course/assessment/bulk/BulkAssessmentOverviewController.java +++ b/src/main/java/org/olat/course/assessment/bulk/BulkAssessmentOverviewController.java @@ -266,7 +266,7 @@ public class BulkAssessmentOverviewController extends FormBasicController { StepRunnerCallback finish = new StepRunnerCallback() { @Override - public Step execute(UserRequest ureq, WindowControl wControl, StepsRunContext runContext) { + public Step execute(UserRequest uureq, WindowControl wControl, StepsRunContext runContext) { Date scheduledDate = (Date)runContext.get("scheduledDate"); AssessableCourseNode courseNode = (AssessableCourseNode)runContext.get("courseNode"); BulkAssessmentDatas datas = (BulkAssessmentDatas)runContext.get("datas"); @@ -300,12 +300,12 @@ public class BulkAssessmentOverviewController extends FormBasicController { Step start = new BulkAssessment_2_DatasStep(ureq, courseNode, datas, editableTask); StepRunnerCallback finish = new StepRunnerCallback() { @Override - public Step execute(UserRequest ureq, WindowControl wControl, StepsRunContext runContext) { + public Step execute(UserRequest uureq, WindowControl wControl, StepsRunContext runContext) { Task task = (Task)runContext.get("task"); Date scheduledDate = (Date)runContext.get("scheduledDate"); - AssessableCourseNode courseNode = (AssessableCourseNode)runContext.get("courseNode"); - BulkAssessmentDatas datas = (BulkAssessmentDatas)runContext.get("datas"); - Feedback feedback = doUpdateBulkAssessment(task, courseNode, scheduledDate, datas); + AssessableCourseNode assessableCourseNode = (AssessableCourseNode)runContext.get("courseNode"); + BulkAssessmentDatas bulkDatas = (BulkAssessmentDatas)runContext.get("datas"); + Feedback feedback = doUpdateBulkAssessment(task, assessableCourseNode, scheduledDate, bulkDatas); runContext.put("feedback", feedback); editedTask = null; return StepsMainRunController.DONE_MODIFIED; @@ -314,7 +314,7 @@ public class BulkAssessmentOverviewController extends FormBasicController { StepRunnerCallback cancel = new StepRunnerCallback() { @Override - public Step execute(UserRequest ureq, WindowControl wControl, StepsRunContext runContext) { + public Step execute(UserRequest uureq, WindowControl wControl, StepsRunContext runContext) { taskManager.returnTaskAfterEdition(editableTask, null); editedTask = null; return Step.NOSTEP; diff --git a/src/main/java/org/olat/course/assessment/bulk/BulkAssessmentToolController.java b/src/main/java/org/olat/course/assessment/bulk/BulkAssessmentToolController.java index a54021a1dc1c45387b6120af44dec3013fcc15cb..efd64c066766527a267b9b403a2b6869750b5bf2 100644 --- a/src/main/java/org/olat/course/assessment/bulk/BulkAssessmentToolController.java +++ b/src/main/java/org/olat/course/assessment/bulk/BulkAssessmentToolController.java @@ -103,7 +103,7 @@ public class BulkAssessmentToolController extends BasicController { private void doOpen(UserRequest ureq) { StepRunnerCallback finish = new StepRunnerCallback() { @Override - public Step execute(UserRequest ureq, WindowControl wControl, StepsRunContext runContext) { + public Step execute(UserRequest uureq, WindowControl bwControl, StepsRunContext runContext) { Date scheduledDate = (Date)runContext.get("scheduledDate"); BulkAssessmentDatas datas = (BulkAssessmentDatas)runContext.get("datas"); Feedback feedback = doBulkAssessment(scheduledDate, datas); diff --git a/src/main/java/org/olat/course/nodes/gta/ui/GTAParticipantController.java b/src/main/java/org/olat/course/nodes/gta/ui/GTAParticipantController.java index 42d13b40627b5ad4d3676c69e9d092bbefbcf676..16fd916a8f7a8b07d85930392f0ef2e4dbfb3bf5 100644 --- a/src/main/java/org/olat/course/nodes/gta/ui/GTAParticipantController.java +++ b/src/main/java/org/olat/course/nodes/gta/ui/GTAParticipantController.java @@ -301,8 +301,8 @@ public class GTAParticipantController extends GTAAbstractController { ICourse course = CourseFactory.loadCourse(courseEnv.getCourseResourceableId()); for(Identity identity:identities) { - UserCourseEnvironment userCourseEnv = AssessmentHelper.createAndInitUserCourseEnvironment(identity, course); - gtaNode.incrementUserAttempts(userCourseEnv); + UserCourseEnvironment uce = AssessmentHelper.createAndInitUserCourseEnvironment(identity, course); + gtaNode.incrementUserAttempts(uce); } } else { gtaNode.incrementUserAttempts(userCourseEnv); diff --git a/src/main/java/org/olat/course/nodes/gta/ui/GroupAssessmentController.java b/src/main/java/org/olat/course/nodes/gta/ui/GroupAssessmentController.java index 24c820d830f8faa4c633a11fc53b589a751bb892..4f5bfe1e322aa18ff06414d3ea024fd907dace7a 100644 --- a/src/main/java/org/olat/course/nodes/gta/ui/GroupAssessmentController.java +++ b/src/main/java/org/olat/course/nodes/gta/ui/GroupAssessmentController.java @@ -336,18 +336,20 @@ public class GroupAssessmentController extends FormBasicController { protected boolean validateFormLogic(UserRequest ureq) { boolean allOk = true; - List<AssessmentRow> rows = model.getObjects(); - for(AssessmentRow row:rows) { - TextElement scoreEl = row.getScoreEl(); - String value = scoreEl.getValue(); - if(withScore && StringHelper.containsNonWhitespace(value)) { - try { - float score = Float.parseFloat(value); - if(score < 0.0f) { - //not acceptable + if(withScore) { + List<AssessmentRow> rows = model.getObjects(); + for(AssessmentRow row:rows) { + TextElement scoreEl = row.getScoreEl(); + String value = scoreEl.getValue(); + if(StringHelper.containsNonWhitespace(value)) { + try { + float score = Float.parseFloat(value); + if(score < 0.0f) { + //not acceptable + } + } catch (NumberFormatException e) { + allOk = false; } - } catch (NumberFormatException e) { - allOk = false; } } } @@ -379,10 +381,13 @@ public class GroupAssessmentController extends FormBasicController { } else { for(AssessmentRow row:rows) { UserCourseEnvironment userCourseEnv = row.getUserCourseEnvironment(); - String value = row.getScoreEl().getValue(); + Float score = null; - if(withScore && StringHelper.containsNonWhitespace(value)) { - score = Float.parseFloat(value); + if(withScore) { + String value = row.getScoreEl().getValue(); + if(StringHelper.containsNonWhitespace(value)) { + score = Float.parseFloat(value); + } } Boolean passed = null; diff --git a/src/main/java/org/olat/course/reminder/ui/CourseReminderEditController.java b/src/main/java/org/olat/course/reminder/ui/CourseReminderEditController.java index 8d2195a118ecfdbff2cd3d53e4ce6f8a1fafa8e7..46dd6dfec49433aabce4a34d9a0994a3f129d3ee 100644 --- a/src/main/java/org/olat/course/reminder/ui/CourseReminderEditController.java +++ b/src/main/java/org/olat/course/reminder/ui/CourseReminderEditController.java @@ -101,7 +101,7 @@ public class CourseReminderEditController extends FormBasicController { @Override protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { - FormLayoutContainer generalCont = FormLayoutContainer.createDefaultFormLayout("general", getTranslator()); + FormLayoutContainer generalCont = FormLayoutContainer.createVerticalFormLayout("general", getTranslator()); generalCont.setRootForm(mainForm); formLayout.add(generalCont); @@ -111,6 +111,9 @@ public class CourseReminderEditController extends FormBasicController { String desc = reminder.getDescription(); descriptionEl = uifactory.addTextElement("reminder.description", "reminder.description", 128, desc, generalCont); + String sendTime = getSendTimeDescription(); + uifactory.addStaticTextElement("send.time.description.label", sendTime, generalCont); + //rules String rulePage = velocity_root + "/edit_rules.html"; rulesCont = FormLayoutContainer.createCustomFormLayout("rules", getTranslator(), rulePage); @@ -137,20 +140,32 @@ public class CourseReminderEditController extends FormBasicController { } //email content - FormLayoutContainer contentCont = FormLayoutContainer.createDefaultFormLayout("contents", getTranslator()); + FormLayoutContainer contentCont = FormLayoutContainer.createVerticalFormLayout("contents", getTranslator()); contentCont.setRootForm(mainForm); formLayout.add(contentCont); - String emailContent = reminder == null ? "" : reminder.getEmailBody(); + String emailContent = reminder == null ? null : reminder.getEmailBody(); + //TODO + if(StringHelper.containsNonWhitespace(emailContent)) { + emailContent = translate("reminder.def.body"); + } emailEl = uifactory.addRichTextElementForStringDataMinimalistic("email.content", "email.content", emailContent, 10, 60, contentCont, getWindowControl()); - FormLayoutContainer buttonLayout = FormLayoutContainer.createButtonLayout("buttons", getTranslator()); + String buttonPage = velocity_root + "/edit_rules_buttons.html"; + FormLayoutContainer buttonLayout = FormLayoutContainer.createCustomFormLayout("buttons", getTranslator(), buttonPage); buttonLayout.setRootForm(mainForm); - contentCont.add(buttonLayout); + formLayout.add(buttonLayout); uifactory.addFormSubmitButton("save", buttonLayout); uifactory.addFormCancelButton("cancel", buttonLayout, ureq, getWindowControl()); } + protected String getSendTimeDescription() { + String interval = reminderModule.getInterval(); + String desc = translate("interval." + interval); + String time = reminderModule.getDefaultSendTime(); + return translate("send.time.description", new String[] { desc, time} ); + } + protected RuleElement initRuleForm(UserRequest ureq, RuleSPI ruleSpi, ReminderRule rule) { String id = Integer.toString(counter++); String type = ruleSpi.getClass().getSimpleName(); diff --git a/src/main/java/org/olat/course/reminder/ui/_content/edit_rules_buttons.html b/src/main/java/org/olat/course/reminder/ui/_content/edit_rules_buttons.html new file mode 100644 index 0000000000000000000000000000000000000000..8c7c295ee00c713da68de7fc7a2cf4a38bce9dab --- /dev/null +++ b/src/main/java/org/olat/course/reminder/ui/_content/edit_rules_buttons.html @@ -0,0 +1,4 @@ +<div class="o_block o_button_group"> + $r.render("save") + $r.render("cancel") +</div> \ No newline at end of file diff --git a/src/main/java/org/olat/course/reminder/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/reminder/ui/_i18n/LocalStrings_de.properties index 62ec93e6b9ed03ee2e994166640375d60231aeca..634541b7ed507c811f15b653a518ffa8976b557e 100644 --- a/src/main/java/org/olat/course/reminder/ui/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/course/reminder/ui/_i18n/LocalStrings_de.properties @@ -20,6 +20,7 @@ month=Monate new.reminder=Neue Erinnerung passed=Bestanden points=Punkte +reminder.def.body=<p>Lieber $lastname $firstname</p><p>Sie haben sich in den Kurs "$coursename" eingetragen. Jetzt wäre ein guter Zeitpunkt es zu besuchen!</p><p>$courseurl</p><p>Viele Grüsse</p> reminder.description=Beschreibung reminder.id=ID reminder.resend=Erinnerung wurde erfolgreich geschickt. @@ -34,6 +35,8 @@ rule.submission.task=Grupenaufgabe Dokumente abgeben rules.description=Wenn folgende Bedingungen erf\u00FCllt sind send=Erinnerung jetzt schicken send.reminder=Geschickte Erinnerungen +send.time.description={0} um {1} +send.time.description.label=Intervall show.sent=Geschickte Erinnerungen zeigen table.header.actions=<i class\='o_icon o_icon_actions o_icon-lg'> </i> table.header.creationDate=Erstellt am diff --git a/src/main/java/org/olat/course/reminder/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/course/reminder/ui/_i18n/LocalStrings_en.properties index 1cc150f0a03ffbf1c02d2ce6834a2df767179d1c..0f11aa0755bfebc148291dd7041e6547fd22cf66 100644 --- a/src/main/java/org/olat/course/reminder/ui/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/course/reminder/ui/_i18n/LocalStrings_en.properties @@ -20,6 +20,7 @@ month=Months new.reminder=New reminder passed=Passed points=Points +reminder.def.body=<p>Dear $lastname $firstname</p><p>You are registered in the course "$coursename". Now would be a good time to view it!</p><p>$courseurl</p><p>Best regards</p> reminder.description=Description reminder.id=ID reminder.resend=Reminder was successfully sent. @@ -34,6 +35,8 @@ rule.submission.task=Group task documents submission rules.description=When matching all of the following conditions send=Send reminders now send.reminder=Send reminders +send.time.description={0} at {1} +send.time.description.label=Interval show.sent=Show sent reminders table.header.actions=<i class\='o_icon o_icon_actions o_icon-lg'> </i> table.header.creationDate=Creation date diff --git a/src/main/java/org/olat/modules/reminder/ReminderInterval.java b/src/main/java/org/olat/modules/reminder/ReminderInterval.java new file mode 100644 index 0000000000000000000000000000000000000000..41fdf81b275f32ec400b2848f1fa46909cd74183 --- /dev/null +++ b/src/main/java/org/olat/modules/reminder/ReminderInterval.java @@ -0,0 +1,74 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.modules.reminder; + +import org.olat.core.util.StringHelper; + +/** + * + * Initial date: 11.05.2015<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public enum ReminderInterval { + + every24(24, "interval.24"), + every12(12, "interval.12"), + every8(8, "interval.8"), + every6(6, "interval.6"), + every4(4, "interval.4"), + every2(2, "interval.2"), + every1(1, "interval.1"); + + private final int interval; + private final String key; + private final String i18nKey; + + private ReminderInterval(int interval, String i18nKey) { + this.interval = interval; + this.key = Integer.toString(interval); + this.i18nKey = i18nKey; + } + + public String key() { + return key; + } + + public int interval() { + return interval; + } + + public String i18nKey() { + return i18nKey; + } + + public static final ReminderInterval byKey(String key) { + ReminderInterval interval = null; + if(StringHelper.containsNonWhitespace(key)) { + for(ReminderInterval value:values()) { + if(key.equals(value.key())) { + interval = value; + break; + } + } + } + return interval; + } +} diff --git a/src/main/java/org/olat/modules/reminder/ReminderModule.java b/src/main/java/org/olat/modules/reminder/ReminderModule.java index 7d9212acd5f1cb44141ce9997ee06c2754d7a4e2..41d5017bbf883a1983a775ac93451501a37bd2bd 100644 --- a/src/main/java/org/olat/modules/reminder/ReminderModule.java +++ b/src/main/java/org/olat/modules/reminder/ReminderModule.java @@ -53,6 +53,7 @@ public class ReminderModule extends AbstractSpringModule { private static final String SMS_ENABLED = "sms.enabled"; private static final String SEND_TIME = "default.send.time"; private static final String SEND_TIMEZONE = "default.send.timezone"; + private static final String INTERVAL = "send.interval"; @Value("${reminders.enabled:true}") private boolean enabled; @@ -63,6 +64,9 @@ public class ReminderModule extends AbstractSpringModule { private String defaultSendTime; @Value("${reminders.default.send.timezone:server}") private String defaultSendTimeZone; + @Value("${reminders.interval:24}") + private String interval; + @Autowired private List<RuleSPI> ruleSpies; @@ -91,6 +95,11 @@ public class ReminderModule extends AbstractSpringModule { defaultSendTime = sendTimeObj; } + String intervalObj = getStringPropertyValue(INTERVAL, true); + if(StringHelper.containsNonWhitespace(intervalObj)) { + interval = intervalObj; + } + String sendTimezoneObj = getStringPropertyValue(SEND_TIMEZONE, true); if(StringHelper.containsNonWhitespace(sendTimezoneObj)) { defaultSendTimeZone = sendTimezoneObj; @@ -121,7 +130,7 @@ public class ReminderModule extends AbstractSpringModule { return selectedSpi; } /** - * Default 0 0 9 * * ? + * Default 0 0 9/1 * * ? * */ private void configureQuartzJob() { @@ -132,6 +141,7 @@ public class ReminderModule extends AbstractSpringModule { String currentCronExpression = cronTrigger.getCronExpression(); String cronExpression = getCronExpression(); if(!cronExpression.equals(currentCronExpression)) { + log.info("Start reminder with this cron expression: " + cronExpression); cronTrigger.setCronExpression(cronExpression); scheduler.rescheduleJob("reminderTrigger", Scheduler.DEFAULT_GROUP, (Trigger)cronTrigger.clone()); } @@ -141,7 +151,7 @@ public class ReminderModule extends AbstractSpringModule { } } - private String getCronExpression() { + protected String getCronExpression() { StringBuilder sb = new StringBuilder(); int hour = 9; int minute = 0; @@ -152,7 +162,21 @@ public class ReminderModule extends AbstractSpringModule { minute = parsedTime.getMinute(); } - sb.append("0 ").append(minute).append(" ").append(hour).append(" * * ?"); + ReminderInterval intervalVal = ReminderInterval.byKey(getInterval()); + String cronInterval; + if(intervalVal != null && !ReminderInterval.every24.equals(intervalVal)) { + int i = intervalVal.interval(); + if(i < hour) { + //correct the first time the cron job starts + int rest = hour % i; + hour = rest; + } + cronInterval = "/" + intervalVal.interval(); + } else { + cronInterval = "";//or 24 hours + } + + sb.append("0 ").append(minute).append(" ").append(hour).append(cronInterval).append(" * * ?"); return sb.toString(); } @@ -174,6 +198,15 @@ public class ReminderModule extends AbstractSpringModule { setStringProperty(SMS_ENABLED, Boolean.toString(smsEnabled), true); } + public String getInterval() { + return interval; + } + + public void setInterval(String interval) { + this.interval = interval; + setStringProperty(INTERVAL, interval, true); + } + public String getDefaultSendTime() { return defaultSendTime; } @@ -199,4 +232,12 @@ public class ReminderModule extends AbstractSpringModule { this.defaultSendTimeZone = timeZone.getID(); setStringProperty(SEND_TIMEZONE, defaultSendTimeZone, true); } + + public void setScheduler(String interval, String defaultSendTime) { + this.interval = interval; + this.defaultSendTime = defaultSendTime; + setStringProperty(INTERVAL, interval, false); + setStringProperty(SEND_TIME, defaultSendTime, false); + savePropertiesAndFireChangedEvent(); + } } diff --git a/src/main/java/org/olat/modules/reminder/manager/ReminderServiceImpl.java b/src/main/java/org/olat/modules/reminder/manager/ReminderServiceImpl.java index 40d9bb31b89866a6c0c71483242dcbfc1785362c..f43194ba37b4bc08067a86e814f64a3f313b00dc 100644 --- a/src/main/java/org/olat/modules/reminder/manager/ReminderServiceImpl.java +++ b/src/main/java/org/olat/modules/reminder/manager/ReminderServiceImpl.java @@ -22,8 +22,13 @@ package org.olat.modules.reminder.manager; import java.text.ParseException; import java.util.Date; import java.util.List; +import java.util.UUID; +import org.apache.velocity.VelocityContext; +import org.olat.core.helpers.Settings; import org.olat.core.id.Identity; +import org.olat.core.id.User; +import org.olat.core.id.UserConstants; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.Formatter; @@ -33,6 +38,7 @@ import org.olat.core.util.mail.MailBundle; import org.olat.core.util.mail.MailContext; import org.olat.core.util.mail.MailContextImpl; import org.olat.core.util.mail.MailManager; +import org.olat.core.util.mail.MailTemplate; import org.olat.core.util.mail.MailerResult; import org.olat.core.util.xml.XStreamHelper; import org.olat.modules.reminder.Reminder; @@ -46,6 +52,7 @@ import org.olat.modules.reminder.model.ReminderRules; import org.olat.modules.reminder.rule.DateRuleSPI; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryEntryRef; +import org.olat.user.UserManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -70,9 +77,11 @@ public class ReminderServiceImpl implements ReminderService { @Autowired private ReminderDAO reminderDao; @Autowired - private ReminderRuleEngine ruleEngine; - @Autowired private MailManager mailManager; + @Autowired + private UserManager userManager; + @Autowired + private ReminderRuleEngine ruleEngine; @Override public Reminder createReminder(RepositoryEntry entry, Identity creator) { @@ -176,16 +185,19 @@ public class ReminderServiceImpl implements ReminderService { MailContext context = new MailContextImpl("[RepositoryEntry:" + entry.getKey() + "]"); String subject = "Reminder"; String body = reminder.getEmailBody(); - - MailBundle bundle = new MailBundle(); - bundle.setContext(context); - bundle.setContactList(contactList); - bundle.setContent(subject, body); - - MailerResult result = mailManager.sendMessage(bundle); - List<Identity> failedIdentities = result.getFailedIdentites(); + String metaId = UUID.randomUUID().toString(); + String url = Settings.getServerContextPathURI() + "/url/RepositoryEntry/" + entry.getKey(); + + MailerResult overviewResult = new MailerResult(); + ReminderTemplate template = new ReminderTemplate(subject, body, url, entry); + for(Identity identityToRemind:identitiesToRemind) { String status; + MailBundle bundle = mailManager.makeMailBundle(context, identityToRemind, template, null, metaId, overviewResult); + MailerResult result = mailManager.sendMessage(bundle); + overviewResult.append(result); + + List<Identity> failedIdentities = result.getFailedIdentites(); if(failedIdentities != null && failedIdentities.contains(identityToRemind)) { status = "error"; } else { @@ -193,6 +205,38 @@ public class ReminderServiceImpl implements ReminderService { } reminderDao.markAsSend(reminder, identityToRemind, status); } - return result; + + return overviewResult; + } + + private class ReminderTemplate extends MailTemplate { + + private final String url; + private final RepositoryEntry entry; + + public ReminderTemplate(String subjectTemplate, String bodyTemplate, String url, RepositoryEntry entry) { + super(subjectTemplate, bodyTemplate, null); + this.url = url; + this.entry = entry; + } + + @Override + public void putVariablesInMailContext(VelocityContext vContext, Identity recipient) { + User user = recipient.getUser(); + vContext.put("firstname", user.getProperty(UserConstants.FIRSTNAME, null)); + vContext.put(UserConstants.FIRSTNAME, user.getProperty(UserConstants.FIRSTNAME, null)); + vContext.put("lastname", user.getProperty(UserConstants.LASTNAME, null)); + vContext.put(UserConstants.LASTNAME, user.getProperty(UserConstants.LASTNAME, null)); + String fullName = userManager.getUserDisplayName(recipient); + vContext.put("fullname", fullName); + vContext.put("fullName", fullName); + vContext.put("login", user.getProperty(UserConstants.EMAIL, null)); + // Put variables from greater context + if(entry != null) { + vContext.put("courseurl", url); + vContext.put("coursename", entry.getDisplayname()); + vContext.put("coursedescription", entry.getDescription()); + } + } } } diff --git a/src/main/java/org/olat/modules/reminder/ui/ReminderAdminController.java b/src/main/java/org/olat/modules/reminder/ui/ReminderAdminController.java index 7ecd9cf6cad9147fc471411be0ac71e199e64154..038f39c427e03ce7853fff412f7293a8a1252048 100644 --- a/src/main/java/org/olat/modules/reminder/ui/ReminderAdminController.java +++ b/src/main/java/org/olat/modules/reminder/ui/ReminderAdminController.java @@ -33,6 +33,8 @@ import org.olat.core.gui.components.form.flexible.impl.FormEvent; import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.WindowControl; +import org.olat.core.util.StringHelper; +import org.olat.modules.reminder.ReminderInterval; import org.olat.modules.reminder.ReminderModule; import org.olat.modules.reminder.model.SendTime; import org.springframework.beans.factory.annotation.Autowired; @@ -47,17 +49,40 @@ public class ReminderAdminController extends FormBasicController { private static final String[] enableKeys = new String[]{ "on" }; + private static final String[] intervalKeys = new String[]{ + ReminderInterval.every24.key(), + ReminderInterval.every12.key(), + ReminderInterval.every8.key(), + ReminderInterval.every6.key(), + ReminderInterval.every4.key(), + ReminderInterval.every2.key(), + ReminderInterval.every1.key() + }; + private MultipleSelectionElement enableEl; private IntegerElement hoursEl, minutesEl; private SingleSelection timezoneEl; + private SingleSelection intervalEl; private FormLayoutContainer timeLayout; + private String[] intervalValues; + @Autowired private ReminderModule reminderModule; public ReminderAdminController(UserRequest ureq, WindowControl wControl) { super(ureq, wControl); + intervalValues = new String[]{ + translate(ReminderInterval.every24.i18nKey()), + translate(ReminderInterval.every12.i18nKey()), + translate(ReminderInterval.every8.i18nKey()), + translate(ReminderInterval.every6.i18nKey()), + translate(ReminderInterval.every4.i18nKey()), + translate(ReminderInterval.every2.i18nKey()), + translate(ReminderInterval.every1.i18nKey()) + }; + initForm(ureq); } @@ -76,6 +101,21 @@ public class ReminderAdminController extends FormBasicController { enableEl.addActionListener(FormEvent.ONCHANGE); enableEl.select(enableKeys[0], reminderModule.isEnabled()); + String interval = reminderModule.getInterval(); + intervalEl = uifactory.addDropdownSingleselect("interval", formLayout, intervalKeys, intervalValues, null); + boolean found = false; + if(StringHelper.containsNonWhitespace(interval)) { + for(String intervalKey:intervalKeys) { + if(intervalKey.equals(interval)) { + intervalEl.select(intervalKey, true); + found = true; + } + } + } + if(!found) { + intervalEl.select(intervalKeys[0], true); + } + int hour = 9; int minute = 0; @@ -130,6 +170,7 @@ public class ReminderAdminController extends FormBasicController { if(enableEl == source) { boolean enabled = enableEl.isAtLeastSelected(1); timeLayout.setVisible(enabled); + intervalEl.setVisible(enabled); //enableSmsEl.setVisible(enabled); } @@ -142,10 +183,12 @@ public class ReminderAdminController extends FormBasicController { reminderModule.setEnabled(enabled); if(enabled) { + String interval = intervalEl.getSelectedKey(); + int hours = hoursEl.getIntValue(); int minutes = minutesEl.getIntValue(); String sendTime = hours + ":" + minutes; - reminderModule.setDefaultSendTime(sendTime); + reminderModule.setScheduler(interval, sendTime); if(timezoneEl.isOneSelected()) { String timeZoneID = timezoneEl.getSelectedKey(); diff --git a/src/main/java/org/olat/modules/reminder/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/reminder/ui/_i18n/LocalStrings_de.properties index 7d98b715cc95ebe37f6fa2204dcdac3b628a3c21..5c51a614e73a506bf8a953736a542a9bd699230a 100644 --- a/src/main/java/org/olat/modules/reminder/ui/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/modules/reminder/ui/_i18n/LocalStrings_de.properties @@ -6,6 +6,13 @@ default.send.time=Default time to send reminders enable.reminders=Activate course reminders enable.sms.reminders=SMS remidners error.group.not.found=Gruppe existiert nicht in diesem Kurs +interval.24=1 Mal pro Tag +interval.12=2 Mal pro Tag +interval.8=3 Mal pro Tag +interval.6=4 Mal pro Tag +interval.4=Jede vier Stunde +interval.2=Jede zwei Stunde +interval.1=Jede Stunde reminder.admin.title=Course reminders rule.after.date=Nach dem Datum rule.course.enrollment.date=Enrollment date @@ -14,3 +21,4 @@ rule.group.member=Gruppen Teilnehmer rule.initial.course.launch.date=Initial course launch date rule.recent.course.launch.date=Recent course launch date rule.user.property=User property + diff --git a/src/main/java/org/olat/modules/reminder/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/modules/reminder/ui/_i18n/LocalStrings_en.properties index 73c6889bd777ae38d7e6310c45b44ae8b774bba6..86a11b81a50d5783b0b76e3a81688b943fbbc5ab 100644 --- a/src/main/java/org/olat/modules/reminder/ui/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/modules/reminder/ui/_i18n/LocalStrings_en.properties @@ -6,6 +6,14 @@ default.send.time=Default time to send reminders enable.reminders=Activate course reminders enable.sms.reminders=SMS remidners error.group.not.found=Group doesn't exist within this course +interval=Interval +interval.24=1 time per day +interval.12=2 time per day +interval.8=3 time per day +interval.6=4 time per day +interval.4=Every 4 hours +interval.2=Every 2 hours +interval.1=Every hour reminder.admin.title=Course reminders rule.after.date=After date rule.course.enrollment.date=Enrollment date diff --git a/src/test/java/org/olat/modules/reminder/ReminderModuleTest.java b/src/test/java/org/olat/modules/reminder/ReminderModuleTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6a0b4372ff24604a9505ddd96cedb35ae01014b9 --- /dev/null +++ b/src/test/java/org/olat/modules/reminder/ReminderModuleTest.java @@ -0,0 +1,89 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.modules.reminder; + +import java.text.ParseException; +import java.util.Calendar; +import java.util.Date; + +import org.junit.Assert; +import org.junit.Test; +import org.olat.test.OlatTestCase; +import org.quartz.CronExpression; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * + * Initial date: 11.05.2015<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class ReminderModuleTest extends OlatTestCase { + + @Autowired + private ReminderModule reminderModule; + + + @Test + public void testCronJob_everyTwoHours() throws ParseException { + reminderModule.setScheduler("2", "9:30"); + String cron = reminderModule.getCronExpression(); + + Calendar cal = Calendar.getInstance(); + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 5); + + CronExpression cronExpression = new CronExpression(cron); + Date d1 = cronExpression.getNextValidTimeAfter(cal.getTime()); + + Calendar cal1 = Calendar.getInstance(); + cal1.setTime(d1); + Assert.assertEquals(1, cal1.get(Calendar.HOUR_OF_DAY)); + Assert.assertEquals(30, cal1.get(Calendar.MINUTE)); + } + + @Test + public void testCronJob_everyHeightHours() throws ParseException { + reminderModule.setScheduler("8", "6:30"); + String cron = reminderModule.getCronExpression(); + + Calendar cal = Calendar.getInstance(); + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 5); + + CronExpression cronExpression = new CronExpression(cron); + Date d1 = cronExpression.getNextValidTimeAfter(cal.getTime()); + + Calendar triggerCal = Calendar.getInstance(); + triggerCal.setTime(d1); + Assert.assertEquals(6, triggerCal.get(Calendar.HOUR_OF_DAY)); + Assert.assertEquals(30, triggerCal.get(Calendar.MINUTE)); + + Date d2 = cronExpression.getNextValidTimeAfter(d1); + triggerCal.setTime(d2); + Assert.assertEquals(14, triggerCal.get(Calendar.HOUR_OF_DAY)); + Assert.assertEquals(30, triggerCal.get(Calendar.MINUTE)); + + Date d3 = cronExpression.getNextValidTimeAfter(d2); + triggerCal.setTime(d3); + Assert.assertEquals(22, triggerCal.get(Calendar.HOUR_OF_DAY)); + Assert.assertEquals(30, triggerCal.get(Calendar.MINUTE)); + } +} \ No newline at end of file diff --git a/src/test/java/org/olat/test/AllTestsJunit4.java b/src/test/java/org/olat/test/AllTestsJunit4.java index 22ba3c1efe2b803bddf8251d919ffe2caae20f74..5cf77c00e057cd5e9b8fdeaf5f9923b48507ffa8 100644 --- a/src/test/java/org/olat/test/AllTestsJunit4.java +++ b/src/test/java/org/olat/test/AllTestsJunit4.java @@ -141,6 +141,7 @@ import org.junit.runners.Suite; org.olat.modules.wiki.gui.components.wikiToHtml.FilterUtilTest.class, org.olat.modules.coach.manager.CoachingDAOTest.class, org.olat.modules.coach.CoachingLargeTest.class, + org.olat.modules.reminder.ReminderModuleTest.class, org.olat.modules.reminder.manager.ReminderDAOTest.class, org.olat.modules.reminder.manager.ReminderRuleEngineTest.class, org.olat.properties.PropertyTest.class,