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,