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..044a9736112fde16757dab38e39eb907c21807ad 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,28 @@ 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(); 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..43be3091d38820d6da0e12a67983da1811fcc85d 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 @@ -34,6 +34,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..b7f032d2fe2054898a26d459ae289849b6eeaaf4 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 @@ -34,6 +34,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..8897b11119733e9c3aae1bd7544d94bb38f01355 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,11 @@ 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, true); + setStringProperty(SEND_TIME, defaultSendTime, true); + } } 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,