diff --git a/src/main/java/org/olat/modules/reminder/RepositoryEntryRuleSPI.java b/src/main/java/org/olat/modules/reminder/RepositoryEntryRuleSPI.java new file mode 100644 index 0000000000000000000000000000000000000000..11ebc4532248c4a0948835f01c0469938c5fa16f --- /dev/null +++ b/src/main/java/org/olat/modules/reminder/RepositoryEntryRuleSPI.java @@ -0,0 +1,35 @@ +/** + * <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.repository.RepositoryEntry; + +/** + * + * + * Initial date: 26.05.2015<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public interface RepositoryEntryRuleSPI extends RuleSPI { + + public boolean evaluate(RepositoryEntry entry, ReminderRule rule); + +} diff --git a/src/main/java/org/olat/modules/reminder/manager/ReminderRuleEngine.java b/src/main/java/org/olat/modules/reminder/manager/ReminderRuleEngine.java index b2d7592fb40a640b9d453233c23f0d2dfb8636a9..ac3f48448cffb738c1b193a685ffc2e66c103319 100644 --- a/src/main/java/org/olat/modules/reminder/manager/ReminderRuleEngine.java +++ b/src/main/java/org/olat/modules/reminder/manager/ReminderRuleEngine.java @@ -34,6 +34,7 @@ import org.olat.modules.reminder.Reminder; import org.olat.modules.reminder.ReminderModule; import org.olat.modules.reminder.ReminderRule; import org.olat.modules.reminder.ReminderService; +import org.olat.modules.reminder.RepositoryEntryRuleSPI; import org.olat.modules.reminder.RuleSPI; import org.olat.modules.reminder.model.ReminderRules; import org.olat.modules.reminder.rule.BusinessGroupRoleRuleSPI; @@ -89,6 +90,9 @@ public class ReminderRuleEngine { List<ReminderRule> ruleList = new ArrayList<>(rules.getRules()); //1. Date rules doesn't need database queries boolean allOk = evaluateDateRule(ruleList); + if(allOk) { + allOk = evaluateRepositoryEntryRule(reminder.getEntry(), ruleList); + } List<Identity> identities; if(allOk) { @@ -122,6 +126,25 @@ public class ReminderRuleEngine { return allOk; } + /** + * + * @param reminder + */ + protected boolean evaluateRepositoryEntryRule(RepositoryEntry entry, List<ReminderRule> ruleList) { + boolean allOk = true; + + for(Iterator<ReminderRule> ruleIt=ruleList.iterator(); ruleIt.hasNext(); ) { + ReminderRule rule = ruleIt.next(); + RuleSPI ruleSpi = reminderModule.getRuleSPIByType(rule.getType()); + if(ruleSpi instanceof RepositoryEntryRuleSPI) { + allOk &= ((RepositoryEntryRuleSPI)ruleSpi).evaluate(entry, rule); + ruleIt.remove(); + } + } + + return allOk; + } + protected List<Identity> getIdentities(RepositoryEntry entry, Reminder reminder, List<ReminderRule> ruleList, boolean resend) { List<ReminderRule> identitiesProviderRules = new ArrayList<>(); diff --git a/src/main/java/org/olat/modules/reminder/rule/RepositoryEntryLifecycleAfterValidFromRuleSPI.java b/src/main/java/org/olat/modules/reminder/rule/RepositoryEntryLifecycleAfterValidFromRuleSPI.java new file mode 100644 index 0000000000000000000000000000000000000000..41b19ed8629a479cf1170012ef52cc2449c9617b --- /dev/null +++ b/src/main/java/org/olat/modules/reminder/rule/RepositoryEntryLifecycleAfterValidFromRuleSPI.java @@ -0,0 +1,127 @@ +/** + * <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.rule; + +import java.util.Calendar; +import java.util.Date; + +import org.olat.course.export.CourseEnvironmentMapper; +import org.olat.modules.reminder.ReminderRule; +import org.olat.modules.reminder.RepositoryEntryRuleSPI; +import org.olat.modules.reminder.RuleEditorFragment; +import org.olat.modules.reminder.model.ReminderRuleImpl; +import org.olat.modules.reminder.ui.RepositoryEntryLifecycleAfterValidFromRuleEditor; +import org.olat.repository.RepositoryEntry; +import org.olat.repository.model.RepositoryEntryLifecycle; +import org.springframework.stereotype.Service; + + +/** + * + * Initial date: 26.05.2015<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +@Service +public class RepositoryEntryLifecycleAfterValidFromRuleSPI implements RepositoryEntryRuleSPI { + + + @Override + public String getLabelI18nKey() { + return "rule.lifecycle.validfrom"; + } + + @Override + public String getCategory() { + return "course"; + } + + @Override + public ReminderRule clone(ReminderRule rule, CourseEnvironmentMapper envMapper) { + return rule.clone(); + } + + @Override + public RuleEditorFragment getEditorFragment(ReminderRule rule, RepositoryEntry entry) { + return new RepositoryEntryLifecycleAfterValidFromRuleEditor(rule, this.getClass().getSimpleName()); + } + + @Override + public boolean evaluate(RepositoryEntry entry, ReminderRule rule) { + boolean allOk = true; + if(rule instanceof ReminderRuleImpl) { + RepositoryEntryLifecycle lifecycle = entry.getLifecycle(); + if(lifecycle != null && lifecycle.getValidFrom() != null) { + allOk &= evaluate(lifecycle, rule); + } else { + allOk &= false; + } + } + return allOk; + } + + public boolean evaluate(RepositoryEntryLifecycle lifecycle, ReminderRule rule) { + Date now = cleanNow(); + Date validTo = lifecycle.getValidTo(); + if(validTo != null && now.compareTo(validTo) >= 0) { + return false;//the course is at the end + } + + ReminderRuleImpl r = (ReminderRuleImpl)rule; + int distance = Integer.parseInt(r.getRightOperand()); + LaunchUnit unit = LaunchUnit.valueOf(r.getRightUnit()); + Date referenceDate = getDate(lifecycle.getValidFrom(), distance, unit); + return now.compareTo(referenceDate) >= 0; + } + + private Date cleanNow() { + Calendar cal = Calendar.getInstance(); + cal.setTime(new Date()); + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + return cal.getTime(); + } + + private Date getDate(Date date, int distance, LaunchUnit unit) { + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + switch(unit) { + case day: + cal.add(Calendar.DATE, distance); + break; + case week: + cal.add(Calendar.DATE, 7 * distance); + break; + case month: + cal.add(Calendar.MONTH, distance); + break; + case year: + cal.add(Calendar.YEAR, distance); + break; + } + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + return cal.getTime(); + } +} \ No newline at end of file diff --git a/src/main/java/org/olat/modules/reminder/ui/RepositoryEntryLifecycleAfterValidFromRuleEditor.java b/src/main/java/org/olat/modules/reminder/ui/RepositoryEntryLifecycleAfterValidFromRuleEditor.java new file mode 100644 index 0000000000000000000000000000000000000000..dd2cb38f50f1ca500b3ebb9c657cb18ce42f3d56 --- /dev/null +++ b/src/main/java/org/olat/modules/reminder/ui/RepositoryEntryLifecycleAfterValidFromRuleEditor.java @@ -0,0 +1,136 @@ +/** + * <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.ui; + +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.form.flexible.FormItem; +import org.olat.core.gui.components.form.flexible.FormItemContainer; +import org.olat.core.gui.components.form.flexible.elements.SingleSelection; +import org.olat.core.gui.components.form.flexible.elements.TextElement; +import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; +import org.olat.core.gui.control.Controller; +import org.olat.core.gui.translator.Translator; +import org.olat.core.util.CodeHelper; +import org.olat.core.util.StringHelper; +import org.olat.core.util.Util; +import org.olat.modules.reminder.ReminderRule; +import org.olat.modules.reminder.RuleEditorFragment; +import org.olat.modules.reminder.model.ReminderRuleImpl; +import org.olat.modules.reminder.rule.LaunchUnit; + +/** + * + * Initial date: 26.05.2015<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class RepositoryEntryLifecycleAfterValidFromRuleEditor extends RuleEditorFragment { + + private static final String[] unitKeys = new String[]{ + LaunchUnit.day.name(), LaunchUnit.week.name(), LaunchUnit.month.name(), LaunchUnit.year.name() + }; + + private TextElement valueEl; + private SingleSelection unitEl; + + private final String ruleType; + + public RepositoryEntryLifecycleAfterValidFromRuleEditor(ReminderRule rule, String ruleType) { + super(rule); + this.ruleType = ruleType; + } + + @Override + public FormItem initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + + String page = Util.getPackageVelocityRoot(this.getClass()) + "/repo_valid.html"; + String id = Long.toString(CodeHelper.getRAMUniqueID()); + + Translator trans = formLayout.getTranslator(); + FormLayoutContainer ruleCont = FormLayoutContainer + .createCustomFormLayout("repo.valid.".concat(id), formLayout.getTranslator(), page); + ruleCont.setRootForm(formLayout.getRootForm()); + formLayout.add(ruleCont); + ruleCont.contextPut("id", id); + + String currentValue = null; + String currentUnit = null; + if(rule instanceof ReminderRuleImpl) { + ReminderRuleImpl r = (ReminderRuleImpl)rule; + currentValue = r.getRightOperand(); + currentUnit = r.getRightUnit(); + } + + valueEl = uifactory.addTextElement("launchvalue".concat(id), null, 128, currentValue, ruleCont); + valueEl.setDomReplacementWrapperRequired(false); + valueEl.setDisplaySize(3); + + String[] unitValues = new String[] { + trans.translate(LaunchUnit.day.name()), trans.translate(LaunchUnit.week.name()), + trans.translate(LaunchUnit.month.name()), trans.translate(LaunchUnit.year.name()) + }; + + unitEl = uifactory.addDropdownSingleselect("launchunit".concat(id), null, ruleCont, unitKeys, unitValues, null); + unitEl.setDomReplacementWrapperRequired(false); + boolean unitSelected = false; + if(currentUnit != null) { + for(String unitKey:unitKeys) { + if(currentUnit.equals(unitKey)) { + unitEl.select(unitKey, true); + unitSelected = true; + } + } + } + if(!unitSelected) { + unitEl.select(unitKeys[1], true); + } + + return ruleCont; + } + + @Override + public boolean validateFormLogic(UserRequest ureq) { + boolean allOk = true; + + unitEl.clearError(); + if(!unitEl.isOneSelected()) { + unitEl.setErrorKey("form.mandatory.hover", null); + allOk &= false; + } + + valueEl.clearError(); + if(!StringHelper.containsNonWhitespace(valueEl.getValue())) { + valueEl.setErrorKey("form.mandatory.hover", null); + allOk &= false; + } + + return allOk; + } + + @Override + public ReminderRule getConfiguration() { + ReminderRuleImpl configuredRule = new ReminderRuleImpl(); + configuredRule.setType(ruleType); + configuredRule.setOperator(">"); + configuredRule.setRightOperand(valueEl.getValue()); + configuredRule.setRightUnit(unitEl.getSelectedKey()); + return configuredRule; + } +} \ No newline at end of file diff --git a/src/main/java/org/olat/modules/reminder/ui/_content/repo_valid.html b/src/main/java/org/olat/modules/reminder/ui/_content/repo_valid.html new file mode 100644 index 0000000000000000000000000000000000000000..aedb0408566cf07889d21e73c9637d1d14f5f0e8 --- /dev/null +++ b/src/main/java/org/olat/modules/reminder/ui/_content/repo_valid.html @@ -0,0 +1,6 @@ +<div class='form-inline'> + $r.render("launchvalue$id") $r.render("launchunit$id") <span class="form-control-static">$r.translate("after.validfrom")</span> + #if($f.hasError("launchvalue$id")) + <br/>$r.render("launchvalue${id}_ERROR") + #end +</div> \ No newline at end of file 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 d017a6aff4aff10297880d7aa620ad8ee85fe402..a4ff292848da811fb1cd00a801e4784cd9b9399c 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 @@ -1,8 +1,9 @@ #Thu Apr 09 21:54:06 CEST 2015 admin.menu.title=Kurserinnerungen admin.menu.title.alt=Kurserinnerungen +after.validfrom=nach Beginsdatum ago=her -default.send.time=Standard Sendezeit für Erinnerungen +default.send.time=Standard Sendezeit f\u00FCr Erinnerungen enable.reminders=Kurserinnerungen einschalten enable.sms.reminders=SMS Erinnerungen error.group.not.found=Gruppe existiert nicht in diesem Kurs @@ -19,6 +20,7 @@ rule.course.enrollment.date=Einschreibedatum rule.course.role=Kursrolle rule.group.member=Gruppenteilnehmer rule.initial.course.launch.date=Erster Kursbesuch +rule.lifecycle.validfrom=Begindatum von Kursdurchf\u00FChrungzeitraum rule.recent.course.launch.date=Letzter Kursbesuch rule.user.property=Benutzereigenschaft 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 c133b55888ccea670cdf32a53a45f60726fe98ac..b2817fd996b1a366c3fdb80df1208eac96fb5dba 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 @@ -1,6 +1,7 @@ #Thu Apr 09 21:54:18 CEST 2015 admin.menu.title=Course reminders admin.menu.title.alt=Course reminders +after.validfrom=after begin date ago=since default.send.time=Default time to send reminders enable.reminders=Activate course reminders @@ -19,6 +20,7 @@ rule.after.date=After date rule.course.enrollment.date=Enrollment date rule.course.role=Course role rule.group.member=Group member +rule.lifecycle.validfrom=Begin date of execution period 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/test/java/org/olat/modules/reminder/manager/ReminderRuleEngineTest.java b/src/test/java/org/olat/modules/reminder/manager/ReminderRuleEngineTest.java index 6d3e71d0bfeb9ea3ecca61dee1a000cd3d6a2a27..8cfd80217b3c78d8bfbdd6c16560e7337c4dbb25 100644 --- a/src/test/java/org/olat/modules/reminder/manager/ReminderRuleEngineTest.java +++ b/src/test/java/org/olat/modules/reminder/manager/ReminderRuleEngineTest.java @@ -57,10 +57,14 @@ import org.olat.modules.reminder.rule.DateRuleSPI; import org.olat.modules.reminder.rule.InitialCourseLaunchRuleSPI; import org.olat.modules.reminder.rule.LaunchUnit; import org.olat.modules.reminder.rule.RecentCourseLaunchRuleSPI; +import org.olat.modules.reminder.rule.RepositoryEntryLifecycleAfterValidFromRuleSPI; import org.olat.modules.reminder.rule.RepositoryEntryRoleRuleSPI; import org.olat.modules.reminder.rule.UserPropertyRuleSPI; import org.olat.repository.RepositoryEntry; +import org.olat.repository.RepositoryManager; +import org.olat.repository.manager.RepositoryEntryLifecycleDAO; import org.olat.repository.manager.RepositoryEntryRelationDAO; +import org.olat.repository.model.RepositoryEntryLifecycle; import org.olat.repository.model.RepositoryEntryToGroupRelation; import org.olat.restapi.repository.course.CoursesWebService; import org.olat.test.JunitTestHelper; @@ -85,6 +89,10 @@ public class ReminderRuleEngineTest extends OlatTestCase { @Autowired private BusinessGroupDAO businessGroupDao; @Autowired + private RepositoryManager repositoryManager; + @Autowired + private RepositoryEntryLifecycleDAO lifecycleDao; + @Autowired private BusinessGroupRelationDAO businessGroupRelationDao; @Autowired private RepositoryEntryRelationDAO repositoryEntryRelationDao; @@ -652,6 +660,91 @@ public class ReminderRuleEngineTest extends OlatTestCase { return rules; } + @Test + public void afterBeginDate() { + //create a course with 3 members + Identity id1 = JunitTestHelper.createAndPersistIdentityAsRndUser("before-begin-1"); + Identity id2 = JunitTestHelper.createAndPersistIdentityAsRndUser("before-begin-2"); + Identity id3 = JunitTestHelper.createAndPersistIdentityAsRndUser("before-begin-3"); + + ICourse course = CoursesWebService.createEmptyCourse(null, "initial-launch-dates", "course long name", null); + RepositoryEntry re = course.getCourseEnvironment().getCourseGroupManager().getCourseEntry(); + + Calendar cal = Calendar.getInstance(); + cal.setTime(new Date());//now + cal.add(Calendar.DATE, -21); + Date validFrom = cal.getTime(); + cal.add(Calendar.DATE, 90); + Date validTo = cal.getTime(); + + RepositoryEntryLifecycle cycle = lifecycleDao.create("Cycle 1", "Cycle soft 1", false, validFrom, validTo); + re = repositoryManager.setDescriptionAndName(re, null, null, null, null, null, null, cycle); + repositoryEntryRelationDao.addRole(id1, re, GroupRoles.owner.name()); + repositoryEntryRelationDao.addRole(id2, re, GroupRoles.coach.name()); + repositoryEntryRelationDao.addRole(id3, re, GroupRoles.participant.name()); + dbInstance.commit(); + + { // check after 2 days + List<ReminderRule> rules = getRepositoryEntryLifecycleRuleValidFromRule(2, LaunchUnit.day); + boolean match = ruleEngine.evaluateRepositoryEntryRule(re, rules); + Assert.assertTrue(match); + } + + { // check after 7 days (between begin and and date) + List<ReminderRule> rules = getRepositoryEntryLifecycleRuleValidFromRule(7, LaunchUnit.day); + boolean match = ruleEngine.evaluateRepositoryEntryRule(re, rules); + Assert.assertTrue(match); + } + + { // check after 2 week s + List<ReminderRule> rules = getRepositoryEntryLifecycleRuleValidFromRule(2, LaunchUnit.week); + boolean match = ruleEngine.evaluateRepositoryEntryRule(re, rules); + Assert.assertTrue(match); + } + + { // check after 21 days + List<ReminderRule> rules = getRepositoryEntryLifecycleRuleValidFromRule(21, LaunchUnit.day); + boolean match = ruleEngine.evaluateRepositoryEntryRule(re, rules); + Assert.assertTrue(match); + } + + { // check after 3 weeks + List<ReminderRule> rules = getRepositoryEntryLifecycleRuleValidFromRule(3, LaunchUnit.week); + boolean match = ruleEngine.evaluateRepositoryEntryRule(re, rules); + Assert.assertTrue(match); + } + + { // check after 22 days + List<ReminderRule> rules = getRepositoryEntryLifecycleRuleValidFromRule(22, LaunchUnit.day); + boolean match = ruleEngine.evaluateRepositoryEntryRule(re, rules); + Assert.assertFalse(match); + } + + { // check after 4 weeks + List<ReminderRule> rules = getRepositoryEntryLifecycleRuleValidFromRule(4, LaunchUnit.week); + boolean match = ruleEngine.evaluateRepositoryEntryRule(re, rules); + Assert.assertFalse(match); + } + + { // check after 1 month + List<ReminderRule> rules = getRepositoryEntryLifecycleRuleValidFromRule(1, LaunchUnit.month); + boolean match = ruleEngine.evaluateRepositoryEntryRule(re, rules); + Assert.assertFalse(match); + } + } + + private List<ReminderRule> getRepositoryEntryLifecycleRuleValidFromRule(int amount, LaunchUnit unit) { + ReminderRuleImpl rule = new ReminderRuleImpl(); + rule.setType(RepositoryEntryLifecycleAfterValidFromRuleSPI.class.getSimpleName()); + rule.setOperator(">"); + rule.setRightOperand(Integer.toString(amount)); + rule.setRightUnit(unit.name()); + + List<ReminderRule> rules = new ArrayList<>(1); + rules.add(rule); + return rules; + } + @Test public void score() { //create a course with 3 members and generate some datas