diff --git a/src/main/java/org/olat/core/commons/services/sms/MessagesSPI.java b/src/main/java/org/olat/core/commons/services/sms/MessagesSPI.java
index eb01136e15c50eb2926b00352b414e7e6a2a5534..12d4f3da98dd03eea521dad838fd6944bafa8689 100644
--- a/src/main/java/org/olat/core/commons/services/sms/MessagesSPI.java
+++ b/src/main/java/org/olat/core/commons/services/sms/MessagesSPI.java
@@ -19,6 +19,11 @@
  */
 package org.olat.core.commons.services.sms;
 
+import org.olat.core.commons.services.sms.ui.AbstractSMSConfigurationController;
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.form.flexible.impl.Form;
+import org.olat.core.gui.control.WindowControl;
+
 /**
  * 
  * Initial date: 3 févr. 2017<br>
@@ -32,6 +37,8 @@ public interface MessagesSPI {
 	public String getName();
 	
 	public boolean isValid();
+	
+	public AbstractSMSConfigurationController getConfigurationController(UserRequest ureq, WindowControl wControl, Form form);
 
 	public boolean send(String messageId, String text, String recipient) throws SimpleMessageException;
 
diff --git a/src/main/java/org/olat/core/commons/services/sms/SimpleMessageModule.java b/src/main/java/org/olat/core/commons/services/sms/SimpleMessageModule.java
index f351063650fa14fe8cd41833aff034e9a0c8e728..19403fb277b6668390ddb86575447570c5825bac 100644
--- a/src/main/java/org/olat/core/commons/services/sms/SimpleMessageModule.java
+++ b/src/main/java/org/olat/core/commons/services/sms/SimpleMessageModule.java
@@ -42,13 +42,16 @@ import org.springframework.stereotype.Service;
 @Service
 public class SimpleMessageModule extends AbstractSpringModule implements ConfigOnOff {
 	
-	public static final String SMS_ENABLED = "message.enabled";
-	public static final String RESET_PASSWORD_ENABLED = "message.reset.password.enabled";
+	private static final String SMS_ENABLED = "message.enabled";
+	private static final String PROVIDER_ID = "message.provider.id";
+	private static final String RESET_PASSWORD_ENABLED = "message.reset.password.enabled";
 	
 	@Value("${message.enabled:false}")
 	private boolean enabled;
 	@Value("${message.reset.password.enabled:true}")
 	private boolean resetPassword;
+	@Value("${message.provider:WebSMS}")
+	private String providerId;
 	
 
 	@Autowired
@@ -66,11 +69,13 @@ public class SimpleMessageModule extends AbstractSpringModule implements ConfigO
 			enabled = "true".equals(enabledObj);
 		}
 		
-		String resetPasswordEnabledObj = getStringPropertyValue(RESET_PASSWORD_ENABLED, true);
-		if(StringHelper.containsNonWhitespace(resetPasswordEnabledObj)) {
-			resetPassword = "true".equals(resetPasswordEnabledObj);
+		String resetEnabledObj = getStringPropertyValue(RESET_PASSWORD_ENABLED, true);
+		if(StringHelper.containsNonWhitespace(resetEnabledObj)) {
+			resetPassword = "true".equals(resetEnabledObj);
 		}
 		
+		providerId = getStringPropertyValue(PROVIDER_ID, providerId);
+		
 		if(enabled) {//check
 			enableSmsUserProperty();
 		}
@@ -127,6 +132,12 @@ public class SimpleMessageModule extends AbstractSpringModule implements ConfigO
 		setStringProperty(RESET_PASSWORD_ENABLED, Boolean.toString(resetPassword), true);
 	}
 	
+	public String getProviderId() {
+		return providerId == null ? null : providerId.toLowerCase();
+	}
 	
-	
+	public void setProviderId(String providerId) {
+		this.providerId = providerId;
+		setStringProperty(PROVIDER_ID, providerId, true);
+	}
 }
diff --git a/src/main/java/org/olat/core/commons/services/sms/manager/SimpleMessageServiceImpl.java b/src/main/java/org/olat/core/commons/services/sms/manager/SimpleMessageServiceImpl.java
index 32276f770052a812fae9b3cf33d98926b837e5ed..82592e2fc330695a4cea1ef04c788b2b3b62be2b 100644
--- a/src/main/java/org/olat/core/commons/services/sms/manager/SimpleMessageServiceImpl.java
+++ b/src/main/java/org/olat/core/commons/services/sms/manager/SimpleMessageServiceImpl.java
@@ -29,6 +29,10 @@ import org.olat.core.commons.services.sms.SimpleMessageException;
 import org.olat.core.commons.services.sms.SimpleMessageModule;
 import org.olat.core.commons.services.sms.SimpleMessageService;
 import org.olat.core.commons.services.sms.model.MessageStatistics;
+import org.olat.core.commons.services.sms.ui.AbstractSMSConfigurationController;
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.form.flexible.impl.Form;
+import org.olat.core.gui.control.WindowControl;
 import org.olat.core.helpers.Settings;
 import org.olat.core.id.Identity;
 import org.olat.core.id.UserConstants;
@@ -84,7 +88,7 @@ public class SimpleMessageServiceImpl implements SimpleMessageService {
 		number = number.replace("+", "").replace(" ", "");
 		if(StringHelper.isLong(number)) {
 			try {
-				Long phone = new Long(number);
+				Long phone = Long.valueOf(number);
 				return phone > 0;
 			} catch (NumberFormatException e) {
 				//
@@ -134,8 +138,15 @@ public class SimpleMessageServiceImpl implements SimpleMessageService {
 	public MessagesSPI getMessagesSpi() {
 		if(Settings.isDebuging()) return new DevNullProvider();
 
-		if(messageModule.isEnabled() && messagesSpiList.size() > 0) {
-			return messagesSpiList.get(0);
+		if(messageModule.isEnabled() && !messagesSpiList.isEmpty()) {
+			String providerId = messageModule.getProviderId();
+			if(StringHelper.containsNonWhitespace(providerId) && !"devnull".equals(providerId)) {
+				for(MessagesSPI spi:messagesSpiList) {
+					if(providerId.equalsIgnoreCase(spi.getId())) {
+						return spi;
+					}
+				}
+			}
 		}
 		return new DevNullProvider();
 	}
@@ -157,6 +168,11 @@ public class SimpleMessageServiceImpl implements SimpleMessageService {
 			return true;
 		}
 
+		@Override
+		public AbstractSMSConfigurationController getConfigurationController(UserRequest ureq, WindowControl wControl, Form form) {
+			return null;
+		}
+
 		@Override
 		public boolean send(String messageId, String text, String recipient) {
 			log.info("Send: " + text);
diff --git a/src/main/java/org/olat/core/commons/services/sms/spi/BulkSMSProvider.java b/src/main/java/org/olat/core/commons/services/sms/spi/BulkSMSProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..fab8590a91d43dfd4a1f273243f4aea166707b5b
--- /dev/null
+++ b/src/main/java/org/olat/core/commons/services/sms/spi/BulkSMSProvider.java
@@ -0,0 +1,166 @@
+/**
+ * <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.core.commons.services.sms.spi;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.util.EntityUtils;
+import org.json.JSONObject;
+import org.olat.core.commons.services.sms.MessagesSPI;
+import org.olat.core.commons.services.sms.SimpleMessageException;
+import org.olat.core.commons.services.sms.ui.BulksSMSConfigurationController;
+import org.olat.core.configuration.AbstractSpringModule;
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.form.flexible.impl.Form;
+import org.olat.core.gui.control.WindowControl;
+import org.olat.core.logging.OLog;
+import org.olat.core.logging.Tracing;
+import org.olat.core.util.StringHelper;
+import org.olat.core.util.coordinate.CoordinatorManager;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+/**
+ * 
+ * Initial date: 4 mars 2019<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+@Service("messagesSpiBulkSMS")
+public class BulkSMSProvider extends AbstractSpringModule implements MessagesSPI {
+	
+	private static final OLog log = Tracing.createLoggerFor(BulkSMSProvider.class);
+	
+	private static final String TOKEN_ID = "bulksms.token.id";
+	private static final String TOKEN_SECRET = "bulksms.token.secret";
+	
+	@Value("${websms.url:https://api.bulksms.com/v1/messages}")
+	private String url;
+	
+	@Value("${bulksms.token.id:}")
+	private String tokenId;
+	@Value("${bulksms.token.secret:}")
+	private String tokenSecret;
+	
+	@Autowired
+	public BulkSMSProvider(CoordinatorManager coordinatorManager) {
+		super(coordinatorManager);
+	}
+
+	@Override
+	public void init() {
+		updateProperties();
+	}
+
+	@Override
+	protected void initFromChangedProperties() {
+		updateProperties();
+	}
+
+	private void updateProperties() {
+		tokenId = getStringPropertyValue(TOKEN_ID, tokenId);
+		tokenSecret = getStringPropertyValue(TOKEN_SECRET, tokenSecret);
+	}
+
+	@Override
+	public String getId() {
+		return "bulksms";
+	}
+
+	@Override
+	public String getName() {
+		return "BulkSMS";
+	}
+
+	@Override
+	public boolean isValid() {
+		return StringHelper.containsNonWhitespace(tokenId) && StringHelper.containsNonWhitespace(tokenSecret);
+	}
+	
+	@Override
+	public BulksSMSConfigurationController getConfigurationController(UserRequest ureq, WindowControl wControl, Form form) {
+		return new BulksSMSConfigurationController(ureq, wControl, form);
+	}
+
+	public String getTokenId() {
+		return tokenId;
+	}
+
+	public void setTokenId(String tokenId) {
+		this.tokenId = tokenId;
+		setSecretStringProperty(TOKEN_ID, tokenId, true);
+	}
+
+	public String getTokenSecret() {
+		return tokenSecret;
+	}
+
+	public void setTokenSecret(String tokenSecret) {
+		this.tokenSecret = tokenSecret;
+		setSecretStringProperty(TOKEN_SECRET, tokenSecret, true);
+	}
+
+	@Override
+	public boolean send(String messageId, String text, String recipient)
+	throws SimpleMessageException {
+		HttpPost send = new HttpPost(url + "?deduplication-id=" + messageId);
+		String token = StringHelper.encodeBase64(tokenId + ":" + tokenSecret);
+		send.setHeader(new BasicHeader("Authorization", "Basic " + token));
+		send.setHeader(new BasicHeader("Content-Type", "application/json"));
+		try(CloseableHttpClient httpclient = HttpClientBuilder.create().build()) {
+			
+			String phone = recipient.replace("+", "").replace(" ", "");
+			String objectStr = jsonPayload(text, phone);
+			HttpEntity smsEntity = new StringEntity(objectStr, ContentType.APPLICATION_JSON);
+			send.setEntity(smsEntity);
+			CloseableHttpResponse response = httpclient.execute(send);
+			int returnCode = response.getStatusLine().getStatusCode();
+			String responseString = EntityUtils.toString(response.getEntity());
+			if(returnCode == 200 || returnCode == 201) {
+				return true;
+			}
+			log.error("WebSMS return an error code " + returnCode + ": " + responseString);
+			return false;
+		} catch(Exception e) {
+			log.error("", e);
+			return false;
+		}
+	}
+	
+	private String jsonPayload(String text, String recipient) {
+		try {
+			JSONObject message = new JSONObject();
+			message.put("body", text);
+			message.put("to", recipient);
+			message.put("encoding", "UNICODE");
+			return message.toString();
+		} catch (Exception e) {
+			log.error("", e);
+			return null;
+		}
+	}
+}
diff --git a/src/main/java/org/olat/core/commons/services/sms/spi/WebSMSProvider.java b/src/main/java/org/olat/core/commons/services/sms/spi/WebSMSProvider.java
index 4c922d293140a7db04fa2aff21de2e31aa23315a..ec95344f69e4cc1541f6ddffafce2a14be6b59df 100644
--- a/src/main/java/org/olat/core/commons/services/sms/spi/WebSMSProvider.java
+++ b/src/main/java/org/olat/core/commons/services/sms/spi/WebSMSProvider.java
@@ -33,10 +33,16 @@ import org.apache.http.util.EntityUtils;
 import org.json.JSONArray;
 import org.json.JSONObject;
 import org.olat.core.commons.services.sms.MessagesSPI;
+import org.olat.core.commons.services.sms.ui.WebSMSConfigurationController;
+import org.olat.core.configuration.AbstractSpringModule;
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.form.flexible.impl.Form;
+import org.olat.core.gui.control.WindowControl;
 import org.olat.core.logging.OLog;
 import org.olat.core.logging.Tracing;
 import org.olat.core.util.StringHelper;
-import org.springframework.beans.factory.InitializingBean;
+import org.olat.core.util.coordinate.CoordinatorManager;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
@@ -48,11 +54,14 @@ import org.springframework.stereotype.Service;
  *
  */
 @Service("messagesSpiWebSMS")
-public class WebSMSProvider implements MessagesSPI, InitializingBean {
+public class WebSMSProvider extends AbstractSpringModule implements MessagesSPI {
 	
 	private static final OLog log = Tracing.createLoggerFor(WebSMSProvider.class);
 	private final BasicCredentialsProvider provider = new BasicCredentialsProvider();
 	
+	private static final String NAME = "websms.username";
+	private static final String CREDENTIALS = "websms.password";
+	
 	@Value("${websms.url:https://api.websms.com/rest/smsmessaging/text}")
 	private String url;
 	
@@ -63,6 +72,11 @@ public class WebSMSProvider implements MessagesSPI, InitializingBean {
 	
 	private boolean test = false;
 	
+	@Autowired
+	public WebSMSProvider(CoordinatorManager coordinatorManager) {
+		super(coordinatorManager);
+	}
+	
 	/**
 	 * Method means for unit tests. The changes are not persisted.
 	 * 
@@ -76,6 +90,24 @@ public class WebSMSProvider implements MessagesSPI, InitializingBean {
 				new UsernamePasswordCredentials(username, password));
 	}
 	
+	@Override
+	public void init() {
+		updateProperties();
+	}
+
+	@Override
+	protected void initFromChangedProperties() {
+		updateProperties();
+	}
+
+	private void updateProperties() {
+		username = getStringPropertyValue(NAME, username);
+		password = getStringPropertyValue(CREDENTIALS, password);
+
+		provider.setCredentials(new AuthScope("api.websms.com", 443),
+				new UsernamePasswordCredentials(username, password));
+	}
+	
 	protected void setTest(boolean test) {
 		this.test = test;
 	}
@@ -94,11 +126,28 @@ public class WebSMSProvider implements MessagesSPI, InitializingBean {
 	public boolean isValid() {
 		return StringHelper.containsNonWhitespace(username) && StringHelper.containsNonWhitespace(password);
 	}
+	
+	public String getUsername() {
+		return username;
+	}
+
+	public void setUsername(String username) {
+		this.username = username;
+		setSecretStringProperty(NAME, username, true);
+	}
+
+	public String getPassword() {
+		return password;
+	}
+
+	public void setPassword(String password) {
+		this.password = password;
+		setSecretStringProperty(CREDENTIALS, password, true);
+	}
 
 	@Override
-	public void afterPropertiesSet() throws Exception {
-		provider.setCredentials(new AuthScope("api.websms.com", 443),
-				new UsernamePasswordCredentials(username, password));
+	public WebSMSConfigurationController getConfigurationController(UserRequest ureq, WindowControl wControl, Form form) {
+		return new WebSMSConfigurationController(ureq, wControl, form);
 	}
 
 	@Override
@@ -109,7 +158,7 @@ public class WebSMSProvider implements MessagesSPI, InitializingBean {
 				.build()) {
 			
 			String phone = recipient.replace("+", "").replace(" ", "");
-			String objectStr = jsonPayload(messageId, text, new Long(phone));
+			String objectStr = jsonPayload(messageId, text, Long.valueOf(phone));
 			HttpEntity smsEntity = new StringEntity(objectStr, ContentType.APPLICATION_JSON);
 			send.setEntity(smsEntity);
 			CloseableHttpResponse response = httpclient.execute(send);
diff --git a/src/main/java/org/olat/core/commons/services/sms/ui/AbstractSMSConfigurationController.java b/src/main/java/org/olat/core/commons/services/sms/ui/AbstractSMSConfigurationController.java
new file mode 100644
index 0000000000000000000000000000000000000000..03be3c2886670e188d240ca1c5a99fe29f907d66
--- /dev/null
+++ b/src/main/java/org/olat/core/commons/services/sms/ui/AbstractSMSConfigurationController.java
@@ -0,0 +1,48 @@
+/**
+ * <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.core.commons.services.sms.ui;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.form.flexible.impl.Form;
+import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
+import org.olat.core.gui.control.WindowControl;
+
+/**
+ * 
+ * Initial date: 6 mars 2019<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public abstract class AbstractSMSConfigurationController extends FormBasicController {
+	
+	public AbstractSMSConfigurationController(UserRequest ureq, WindowControl wControl, Form form) {
+		super(ureq, wControl, LAYOUT_DEFAULT, null, form);
+	}
+
+	@Override
+	protected boolean validateFormLogic(UserRequest ureq) {
+		return super.validateFormLogic(ureq);
+	}
+
+	@Override
+	protected void formOK(UserRequest ureq) {
+		//
+	}
+}
diff --git a/src/main/java/org/olat/core/commons/services/sms/ui/BulksSMSConfigurationController.java b/src/main/java/org/olat/core/commons/services/sms/ui/BulksSMSConfigurationController.java
new file mode 100644
index 0000000000000000000000000000000000000000..339eab961dce5116a8f8e9e0f70e5e8bff4629a5
--- /dev/null
+++ b/src/main/java/org/olat/core/commons/services/sms/ui/BulksSMSConfigurationController.java
@@ -0,0 +1,92 @@
+/**
+ * <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.core.commons.services.sms.ui;
+
+import org.olat.core.commons.services.sms.spi.BulkSMSProvider;
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.form.flexible.FormItemContainer;
+import org.olat.core.gui.components.form.flexible.elements.TextElement;
+import org.olat.core.gui.components.form.flexible.impl.Form;
+import org.olat.core.gui.control.Controller;
+import org.olat.core.gui.control.WindowControl;
+import org.olat.core.util.StringHelper;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * 
+ * Initial date: 6 mars 2019<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class BulksSMSConfigurationController extends AbstractSMSConfigurationController {
+	
+	private TextElement tokenIdEl;
+	private TextElement tokenSecretEl;
+	
+	@Autowired
+	private BulkSMSProvider bulkSmsProvider;
+	
+	public BulksSMSConfigurationController(UserRequest ureq, WindowControl wControl, Form form) {
+		super(ureq, wControl, form);
+		
+		initForm(ureq);
+	}
+
+	@Override
+	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+		String tokenId = bulkSmsProvider.getTokenId();
+		tokenIdEl = uifactory.addTextElement("bulksms.token.id", 128, tokenId, formLayout);
+		tokenIdEl.setMandatory(true);
+		
+		String tokenSecret = bulkSmsProvider.getTokenSecret();
+		tokenSecretEl = uifactory.addTextElement("bulksms.token.secret", 128, tokenSecret, formLayout);
+		tokenSecretEl.setMandatory(true);
+	}
+
+	@Override
+	protected void doDispose() {
+		//
+	}
+
+	@Override
+	protected boolean validateFormLogic(UserRequest ureq) {
+		boolean allOk = super.validateFormLogic(ureq);
+		
+		tokenIdEl.clearError();
+		if(!StringHelper.containsNonWhitespace(tokenIdEl.getValue())) {
+			tokenIdEl.setErrorKey("form.legende.mandatory", null);
+			allOk &= false;
+		}
+		
+		tokenSecretEl.clearError();
+		if(!StringHelper.containsNonWhitespace(tokenSecretEl.getValue())) {
+			tokenSecretEl.setErrorKey("form.legende.mandatory", null);
+			allOk &= false;
+		}
+		
+		return allOk;
+	}
+
+	@Override
+	protected void formOK(UserRequest ureq) {
+		bulkSmsProvider.setTokenId(tokenIdEl.getValue());
+		bulkSmsProvider.setTokenSecret(tokenSecretEl.getValue());
+	}
+}
diff --git a/src/main/java/org/olat/core/commons/services/sms/ui/SimpleMessageServiceAdminConfigurationController.java b/src/main/java/org/olat/core/commons/services/sms/ui/SimpleMessageServiceAdminConfigurationController.java
index cd0545bbf92ea9f233aa9aa106c099b8cdcfa921..0bbd1269a05a4eddfc86a2034bd8714ba37f73f6 100644
--- a/src/main/java/org/olat/core/commons/services/sms/ui/SimpleMessageServiceAdminConfigurationController.java
+++ b/src/main/java/org/olat/core/commons/services/sms/ui/SimpleMessageServiceAdminConfigurationController.java
@@ -31,6 +31,7 @@ import org.olat.core.gui.components.form.flexible.elements.MultipleSelectionElem
 import org.olat.core.gui.components.form.flexible.elements.SingleSelection;
 import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
 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.springframework.beans.factory.annotation.Autowired;
@@ -49,39 +50,49 @@ public class SimpleMessageServiceAdminConfigurationController extends FormBasicC
 	private MultipleSelectionElement enableEl;
 	private MultipleSelectionElement resetPasswordEl;
 	
+	private AbstractSMSConfigurationController providerConfigCtrl;
+	
 	@Autowired
 	private SimpleMessageModule messageModule;
 	@Autowired
 	private SimpleMessageService messagesService;
 	
 	public SimpleMessageServiceAdminConfigurationController(UserRequest ureq, WindowControl wControl) {
-		super(ureq, wControl);
+		super(ureq, wControl, "admin_configuration");
 		
 		initForm(ureq);
+		updateEnableDisable();
+		updateProviderConfiguration(ureq);
 	}
 
 	@Override
 	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+		FormLayoutContainer serviceCont = FormLayoutContainer.createDefaultFormLayout("service", getTranslator());
+		formLayout.add(serviceCont);
+		
 		setFormTitle("admin.configuration.title");
 		setFormDescription("admin.configuration.description");
 		
 		String[] onValues = new String[]{ translate("on") };
-		enableEl = uifactory.addCheckboxesHorizontal("enable", "admin.enable", formLayout, onKeys, onValues);
+		enableEl = uifactory.addCheckboxesHorizontal("enable", "admin.enable", serviceCont, onKeys, onValues);
 		enableEl.addActionListener(FormEvent.ONCHANGE);
 		if(messageModule.isEnabled()) {
 			enableEl.select(onKeys[0], true);
 		}
 		
 		List<MessagesSPI> spies = messagesService.getMessagesSpiList();
-		String[] serviceKeys = new String[spies.size()];
-		String[] serviceValues = new String[spies.size()];
+		String[] serviceKeys = new String[spies.size() + 1];
+		String[] serviceValues = new String[spies.size() + 1];
+		serviceKeys[0] = "devnull";
+		serviceValues[0] = translate("no.service.provider");
 		for(int i=spies.size(); i-->0; ) {
-			serviceKeys[i] = spies.get(i).getId();
-			serviceValues[i] = spies.get(i).getName();
+			serviceKeys[i + 1] = spies.get(i).getId();
+			serviceValues[i + 1] = spies.get(i).getName();
 		}
-		serviceEl = uifactory.addDropdownSingleselect("service", "service", formLayout, serviceKeys, serviceValues, null);
+		serviceEl = uifactory.addDropdownSingleselect("service.providers", "service", serviceCont, serviceKeys, serviceValues, null);
+		serviceEl.addActionListener(FormEvent.ONCHANGE);
 		if(messagesService.getMessagesSpi() != null) {
-			String activeServiceId = messagesService.getMessagesSpi().getId();
+			String activeServiceId = messageModule.getProviderId();
 			for(int i=serviceKeys.length; i-->0; ) {
 				if(serviceKeys[i].equals(activeServiceId)) {
 					serviceEl.select(serviceKeys[i], true);
@@ -90,12 +101,34 @@ public class SimpleMessageServiceAdminConfigurationController extends FormBasicC
 		}
 
 		String[] resetPasswordValues = new String[]{ translate("on.sms") };
-		resetPasswordEl = uifactory.addCheckboxesHorizontal("reset.password", "reset.password", formLayout, onKeys, resetPasswordValues);
-		resetPasswordEl.addActionListener(FormEvent.ONCHANGE);
+		resetPasswordEl = uifactory.addCheckboxesHorizontal("reset.password", "reset.password", serviceCont, onKeys, resetPasswordValues);
 		if(messageModule.isResetPasswordEnabled()) {
 			resetPasswordEl.select(onKeys[0], true);
 		}
-		updateEnableDisable();
+		
+		FormLayoutContainer buttonCont = FormLayoutContainer.createButtonLayout("buttons", getTranslator());
+		formLayout.add(buttonCont);
+		uifactory.addFormSubmitButton("save", buttonCont);
+	}
+	
+	private void updateProviderConfiguration(UserRequest ureq) {
+		removeControllerListener(providerConfigCtrl);
+		if(serviceEl.isOneSelected()) {
+			flc.remove("configuration");
+			
+			String selectedProviderId = serviceEl.getSelectedKey();
+			List<MessagesSPI> spies = messagesService.getMessagesSpiList();
+			for(MessagesSPI spi:spies) {
+				if(spi.getId().equals(selectedProviderId)) {
+					providerConfigCtrl = spi.getConfigurationController(ureq, getWindowControl(), mainForm);
+					if(providerConfigCtrl != null) {
+						flc.add("configuration", providerConfigCtrl.getInitialFormItem());
+					}
+				}
+			}	
+		} else {
+			flc.remove("configuration");
+		}
 	}
 	
 	private void updateEnableDisable() {
@@ -118,23 +151,32 @@ public class SimpleMessageServiceAdminConfigurationController extends FormBasicC
 		//
 	}
 
+	@Override
+	protected boolean validateFormLogic(UserRequest ureq) {
+		boolean allOk = super.validateFormLogic(ureq);
+		if(providerConfigCtrl != null) {
+			allOk &= providerConfigCtrl.validateFormLogic(ureq);
+		}
+		return allOk;
+	}
+
 	@Override
 	protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
 		if(enableEl == source) {
 			updateEnableDisable();
-			messageModule.setEnabled(enableEl.isAtLeastSelected(1));
-		} else if(resetPasswordEl == source) {
-			messageModule.setResetPasswordEnabled(resetPasswordEl.isAtLeastSelected(1));
+		} else if(serviceEl == source) {
+			updateProviderConfiguration(ureq);
 		}
 		super.formInnerEvent(ureq, source, event);
 	}
 
 	@Override
 	protected void formOK(UserRequest ureq) {
-		//
+		messageModule.setEnabled(enableEl.isAtLeastSelected(1));
+		messageModule.setResetPasswordEnabled(resetPasswordEl.isAtLeastSelected(1));
+		messageModule.setProviderId(serviceEl.getSelectedKey());
+		if(providerConfigCtrl != null) {
+			providerConfigCtrl.formOK(ureq);
+		}
 	}
-
-	
-	
-
 }
diff --git a/src/main/java/org/olat/core/commons/services/sms/ui/WebSMSConfigurationController.java b/src/main/java/org/olat/core/commons/services/sms/ui/WebSMSConfigurationController.java
new file mode 100644
index 0000000000000000000000000000000000000000..4461a26d3d236e8546eb9f86ad057c78ee06710e
--- /dev/null
+++ b/src/main/java/org/olat/core/commons/services/sms/ui/WebSMSConfigurationController.java
@@ -0,0 +1,110 @@
+/**
+ * <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.core.commons.services.sms.ui;
+
+import org.olat.core.commons.services.sms.spi.WebSMSProvider;
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.form.flexible.FormItemContainer;
+import org.olat.core.gui.components.form.flexible.elements.TextElement;
+import org.olat.core.gui.components.form.flexible.impl.Form;
+import org.olat.core.gui.control.Controller;
+import org.olat.core.gui.control.WindowControl;
+import org.olat.core.util.StringHelper;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * 
+ * Initial date: 6 mars 2019<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class WebSMSConfigurationController extends AbstractSMSConfigurationController {
+
+	private static final String PLACEHOLDER = "xxx-placeholder-xxx";
+	
+	private TextElement usernameEl;
+	private TextElement passwordEl;
+	
+	private String replacedValue;
+	
+	@Autowired
+	private WebSMSProvider webSmsProvider;
+	
+	public WebSMSConfigurationController(UserRequest ureq, WindowControl wControl, Form form) {
+		super(ureq, wControl, form);
+		
+		initForm(ureq);
+	}
+
+	@Override
+	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+		String uname = webSmsProvider.getUsername();
+		usernameEl = uifactory.addTextElement("websms.username", 128, uname, formLayout);
+		usernameEl.setMandatory(true);
+		
+		String creds = webSmsProvider.getPassword();
+		if(StringHelper.containsNonWhitespace(creds)) {
+			replacedValue = creds;
+			creds = PLACEHOLDER;
+		}
+		passwordEl = uifactory.addPasswordElement("websms.password", "websms.password", 128, creds, formLayout);
+		passwordEl.setMandatory(true);
+	}
+
+	@Override
+	protected void doDispose() {
+		//
+	}
+	
+	@Override
+	protected boolean validateFormLogic(UserRequest ureq) {
+		boolean allOk = super.validateFormLogic(ureq);
+		
+		usernameEl.clearError();
+		if(!StringHelper.containsNonWhitespace(usernameEl.getValue())) {
+			usernameEl.setErrorKey("form.legende.mandatory", null);
+			allOk &= false;
+		}
+		
+		passwordEl.clearError();
+		if(!StringHelper.containsNonWhitespace(passwordEl.getValue())) {
+			passwordEl.setErrorKey("form.legende.mandatory", null);
+			allOk &= false;
+		} else if(!PLACEHOLDER.equals(passwordEl.getValue())) {
+			replacedValue = passwordEl.getValue();
+			passwordEl.setValue(PLACEHOLDER);
+		}
+		
+		return allOk;
+	}
+
+	@Override
+	protected void formOK(UserRequest ureq) {
+		webSmsProvider.setUsername(usernameEl.getValue());
+		
+		String credential = passwordEl.getValue();
+		if(!PLACEHOLDER.equals(credential)) {
+			webSmsProvider.setPassword(credential);
+			passwordEl.setValue(PLACEHOLDER);
+		} else if(StringHelper.containsNonWhitespace(replacedValue)) {
+			webSmsProvider.setPassword(replacedValue);
+		}
+	}
+}
diff --git a/src/main/java/org/olat/core/commons/services/sms/ui/_content/admin_configuration.html b/src/main/java/org/olat/core/commons/services/sms/ui/_content/admin_configuration.html
new file mode 100644
index 0000000000000000000000000000000000000000..383eddb54980366bcc2d531d1f26031b790c434a
--- /dev/null
+++ b/src/main/java/org/olat/core/commons/services/sms/ui/_content/admin_configuration.html
@@ -0,0 +1,9 @@
+$r.render("service")
+#if($r.available("configuration"))
+	$r.render("configuration")
+#end
+<div class="o_form form-horizontal"><div class="form-group clearfix">
+	<div class="col-sm-offset-3 col-sm-9">
+	$r.render("buttons")
+	</div>
+</div></div>
\ No newline at end of file
diff --git a/src/main/java/org/olat/core/commons/services/sms/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/core/commons/services/sms/ui/_i18n/LocalStrings_de.properties
index d28faea964eb592a0c5a0d034d5365586802b028..1456b3a6f7e49dba88cd052bdab782d356a85d94 100644
--- a/src/main/java/org/olat/core/commons/services/sms/ui/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/core/commons/services/sms/ui/_i18n/LocalStrings_de.properties
@@ -1,4 +1,4 @@
-#Tue Feb 07 13:58:37 CET 2017
+#Wed Mar 06 14:56:25 CET 2019
 admin.configuration.description=<p>Die Benachrichtigung per SMS ist eine optionale Komponente.</p><p>Achtung\: durch das Versenden von SMS entstehen Kosten pro SMS\!</p>
 admin.configuration.title=SMS Konfiguration
 admin.enable=SMS Versand
@@ -6,23 +6,28 @@ admin.menu.title=SMS
 admin.menu.title.alt=SMS Konfiguration udn Statistik
 admin.settings=SMS Dienst Konfiguration
 admin.statistics=SMS Versand
+bulksms.token.id=Token ID
+bulksms.token.secret=Token secret
 confirm.sms.phone=<h3>SMS Authentifizierung</h3><p>Das System erm\u00F6glicht eine Authentifizierung \u00FCber SMS, um ein verlorenes Passwort zur\u00FCcksetzen. Bitte geben Sie Ihre Mobile Telefonnummer an, um diesen Dienst zu aktivieren</p>
 dont.activate=Nicht aktivieren
 error.invalid.token=Token stimmt nicht\!
 error.phone.invalid=Das Nummer ist nicht valid (z.B. +41 12 345 67 89)
+no.service.provider=Kein Provider angew\u00E4hlt
 on=ein
 on.sms=mit SMS Code
-start.sms.authentication=SMS Authentifizierung starten
 reset.password=Password zur\u00FCcksetzen
 service=Dienst
 sms.phone.number=Telephon Mobil
-sms.phone.number.hint=+41 12 345 67 89
 sms.phone.number.example=Sie m\u00FCssen Ihre Telefonnummer im internationalen Format angeben (+41 12 345 67 89).
+sms.phone.number.hint=+41 12 345 67 89
 sms.send=SMS Authentifizierung starten
 sms.token=Ihr Token ist {0}
 sms.token.number=Token
-sms.token.number.explain=Geben Sie den Best\u00E4tigungscode ein, den Sie per SMS erhalten haben. Falls Sie nach 1 Minute noch keine SMS erhalten haben, überprüfen Sie bitte, ob Sie das korrekte internationale Nummernformat verwendet haben.
+sms.token.number.explain=Geben Sie den Best\u00E4tigungscode ein, den Sie per SMS erhalten haben. Falls Sie nach 1 Minute noch keine SMS erhalten haben, \u00FCberpr\u00FCfen Sie bitte, ob Sie das korrekte internationale Nummernformat verwendet haben.
+start.sms.authentication=SMS Authentifizierung starten
 table.header.month=Monat
 table.header.numOfMessages=SMS
 table.header.year=Jahr
 warning.spi.not.configured=Dienst ist nicht konfiguriert
+websms.password=Passwort
+websms.username=Benutzername
diff --git a/src/main/java/org/olat/core/commons/services/sms/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/core/commons/services/sms/ui/_i18n/LocalStrings_en.properties
index 6a610c4974761f4dff3d1f1e3e2356b99e760c3c..cd23e692896bae9b888630d74e3f02ae2609004a 100644
--- a/src/main/java/org/olat/core/commons/services/sms/ui/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/core/commons/services/sms/ui/_i18n/LocalStrings_en.properties
@@ -1,4 +1,4 @@
-#Tue Feb 07 13:47:28 CET 2017
+#Wed Mar 06 14:56:12 CET 2019
 admin.configuration.description=<p>Notification with SMS is an optional component.</p><p>Warning\: SMS costs occurred per SMS\!</p>
 admin.configuration.title=SMS configuration
 admin.enable=SMS distribution
@@ -6,23 +6,28 @@ admin.menu.title=SMS
 admin.menu.title.alt=SMS configuration and statistics
 admin.settings=SMS service configuration
 admin.statistics=SMS distribution
+bulksms.token.id=Token ID
+bulksms.token.secret=Token secret
 confirm.sms.phone=<h3>SMS Authentication</h3><p>The system allows to authenticate via SMS to reset a lost password. Please enter your mobile phone number to activate this service.</p>
 dont.activate=Don't activate
 error.invalid.token=The code is not valid.
 error.phone.invalid=The number is not a valid phone number (e.g. +41 12 345 67 89)
+no.service.provider=No provider selected
 on=on
 on.sms=with SMS code
-start.sms.authentication=Start SMS Authentication
 reset.password=Reset password
 service=Service
 sms.phone.number=Mobile telefon
-sms.phone.number.hint=+41 12 345 67 89
 sms.phone.number.example=You need to enter your phone number in the international format (e.g. +41 12 345 67 89).
+sms.phone.number.hint=+41 12 345 67 89
 sms.send=Start authentication by SMS
 sms.token=Your token is {0}
 sms.token.number=Code
 sms.token.number.explain=Please enter the 6 digit number that you received by SMS. If you don't receive a SMS after 1 minute, please make sure you used the correct international number format.
+start.sms.authentication=Start SMS Authentication
 table.header.month=Month
 table.header.numOfMessages=SMS
 table.header.year=Year
 warning.spi.not.configured=The service is not configured.
+websms.password=Password
+websms.username=User name
diff --git a/src/main/java/org/olat/core/commons/services/sms/ui/_i18n/LocalStrings_fr.properties b/src/main/java/org/olat/core/commons/services/sms/ui/_i18n/LocalStrings_fr.properties
index 9b4d9ce1116b46fec9754f6fcb7c557b03a78130..9e77b9391415978f323feeb9ad646af7d07962c2 100644
--- a/src/main/java/org/olat/core/commons/services/sms/ui/_i18n/LocalStrings_fr.properties
+++ b/src/main/java/org/olat/core/commons/services/sms/ui/_i18n/LocalStrings_fr.properties
@@ -1,4 +1,4 @@
-#Wed Mar 08 12:06:24 CET 2017
+#Wed Mar 06 14:57:26 CET 2019
 admin.configuration.description=<p>La notifications par SMS est un composant optionnel.</p><p>Attention\: l'envoi de SMS occasione des co\u00FBts\!</p>
 admin.configuration.title=Configuration SMS
 admin.enable=Distribution SMS
@@ -6,10 +6,13 @@ admin.menu.title=SMS
 admin.menu.title.alt=Configuration SMS et statistiques
 admin.settings=Configuration du service SMS
 admin.statistics=Distribution de SMS
+bulksms.token.id=Token ID
+bulksms.token.secret=Token secret
 confirm.sms.phone=<h3>Authentication par SMS</h3><p>Le syst\u00E8me permet de s'authentifier par SMS en cas d''oubli du mot de passe. Pour activer cette fonction, veuillez entrer votre num\u00E9ro de t\u00E9l\u00E9phone portable.</p>
 dont.activate=Ne pas activer
 error.invalid.token=Le code n'est pas valide.
 error.phone.invalid=Le num\u00E9ro n'est pas un num\u00E9ro de t\u00E9l\u00E9phone valide (par exemple +41 12 345 67 89)
+no.service.provider=Aucun
 on=Activ\u00E9
 on.sms=par code SMS
 reset.password=R\u00E9initialiser le mot de passe
@@ -26,3 +29,5 @@ table.header.month=Mois
 table.header.numOfMessages=SMS
 table.header.year=Ann\u00E9e
 warning.spi.not.configured=Le service n'est pas configur\u00E9.
+websms.password=Mot de passe
+websms.username=Nom d'utilisateur
diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/impl/FormLayoutContainer.java b/src/main/java/org/olat/core/gui/components/form/flexible/impl/FormLayoutContainer.java
index 48fc802af27aaa9ba0d3fefb628cf86ed1fb2e71..8dc829d078b748d5e0c2dc3a56a20ef1403d6d8c 100644
--- a/src/main/java/org/olat/core/gui/components/form/flexible/impl/FormLayoutContainer.java
+++ b/src/main/java/org/olat/core/gui/components/form/flexible/impl/FormLayoutContainer.java
@@ -297,9 +297,12 @@ public class FormLayoutContainer extends FormItemImpl implements FormItemContain
 	 * remove the component with the give name from this container	  
 	 * @param binderName
 	 */
+	@Override
 	public void remove(String formCompName) {
 		FormItem toBeRemoved = getFormComponent(formCompName);
-		remove(formCompName, toBeRemoved);
+		if(toBeRemoved != null) {
+			remove(formCompName, toBeRemoved);
+		}
 	}
 	
 	private void remove(String formCompName, FormItem toBeRemoved) {
diff --git a/src/main/java/org/olat/modules/quality/analysis/ui/_i18n/LocalStrings_fr.properties b/src/main/java/org/olat/modules/quality/analysis/ui/_i18n/LocalStrings_fr.properties
index 1d42a913667e63834f1b949e39bc63f1bb4eeb5e..207025e140fc7ef075a8214ef74ff3fb1b0aa90c 100644
--- a/src/main/java/org/olat/modules/quality/analysis/ui/_i18n/LocalStrings_fr.properties
+++ b/src/main/java/org/olat/modules/quality/analysis/ui/_i18n/LocalStrings_fr.properties
@@ -1,5 +1,6 @@
-#Thu Feb 28 14:56:33 CET 2019
+#Wed Mar 06 14:57:30 CET 2019
 analysis.details=D\u00E9tails des tendances
+analysis.details.print.title={0}\: {1}
 analysis.export=Exporter Excel
 analysis.pdf=Exporter au format PDF
 analysis.print=Imprimer
diff --git a/src/main/java/org/olat/user/ui/role/_i18n/LocalStrings_fr.properties b/src/main/java/org/olat/user/ui/role/_i18n/LocalStrings_fr.properties
index 51ae2c3a184f0c21d477ca4ce40ef95143419249..2a01ef809602c3732ca6bd3a892062701499778c 100644
--- a/src/main/java/org/olat/user/ui/role/_i18n/LocalStrings_fr.properties
+++ b/src/main/java/org/olat/user/ui/role/_i18n/LocalStrings_fr.properties
@@ -1,4 +1,4 @@
-#Thu Feb 28 14:58:47 CET 2019
+#Wed Mar 06 14:57:45 CET 2019
 add.role=Ajouter un nouveau r\u00F4le
 admin.menu.title=R\u00F4le utilisateur \u00E0 utilisateur
 admin.menu.title.alt=R\u00F4le utilisateur \u00E0 utilisateur
@@ -16,6 +16,7 @@ role.identifier=Identifiant
 role.name=R\u00F4le
 role.rights=Permissions
 table.header.id=ID
+table.header.identifier=Identifiant
 table.header.managed=<i class\='o_icon o_icon_managed'> </i>
 table.header.role=R\u00F4le
 table.header.role.contra=R\u00F4le inverse