Index: main/plugin-sms/plugin.xml
===================================================================
--- main/plugin-sms/plugin.xml	(revision 26839)
+++ main/plugin-sms/plugin.xml	(working copy)
@@ -20,6 +20,87 @@
         xsi:schemaLocation="http://www.ametys.org/schema/plugin http://www.ametys.org/schema/plugin-2.0.xsd"
         runtimeVersion="2.0" version="2.0">
     
+    <config>
+    	<param id="org.ametys.plugins.sms.broker.autorized" type="string">
+            <label i18n="true">PLUGINS_SMS_SUBSCRIBTION_SERVICE_BROKER_AUTORIZED_LABEL</label>
+            <description i18n="true">PLUGINS_SMS_SUBSCRIBTION_SERVICE_BROKER_AUTORIZED_DESCRIPTION</description>
+        	<validation>
+                <mandatory/>
+            </validation>
+            <default-value>\+33[67][0-9]{8}</default-value>
+            <category i18n="true">PLUGINS_SMS_SUBSCRIBTION_SERVICE_BROKER_CATEGORY</category>
+            <group i18n="true">PLUGINS_SMS_SUBSCRIBTION_SERVICE_BROKER_GROUP</group>
+            <order>10</order>
+            <widget>textarea</widget>
+        </param>
+        <param id="org.ametys.plugins.sms.broker.convert" type="string">
+            <label i18n="true">PLUGINS_SMS_SUBSCRIBTION_SERVICE_BROKER_CONVERT_LABEL</label>
+            <description i18n="true">PLUGINS_SMS_SUBSCRIBTION_SERVICE_BROKER_CONVERT_DESCRIPTION</description>
+        	<validation>
+                <mandatory/>
+            </validation>
+            <default-value>0([67][0-9]{8})/+33{1}&#13;0([67])[ .]([0-9]{2})[ .]([0-9]{2})[ .]([0-9]{2})[ .]([0-9]{2})/+33{1}{2}{3}{4}{5}</default-value>
+            <category i18n="true">PLUGINS_SMS_SUBSCRIBTION_SERVICE_BROKER_CATEGORY</category>
+            <group i18n="true">PLUGINS_SMS_SUBSCRIBTION_SERVICE_BROKER_GROUP</group>
+            <order>20</order>
+            <widget>textarea</widget>
+        </param>
+        <param id="org.ametys.plugins.sms.broker.sample" type="string">
+            <label i18n="true">PLUGINS_SMS_SUBSCRIBTION_SERVICE_BROKER_SAMPLE_LABEL</label>
+            <description i18n="true">PLUGINS_SMS_SUBSCRIBTION_SERVICE_BROKER_SAMPLE_DESCRIPTION</description>
+        	<validation>
+                <mandatory/>
+            </validation>
+            <default-value>+336XXXXXXXX&#13;06XXXXXXXX</default-value>         
+            <category i18n="true">PLUGINS_SMS_SUBSCRIBTION_SERVICE_BROKER_CATEGORY</category>
+            <group i18n="true">PLUGINS_SMS_SUBSCRIBTION_SERVICE_BROKER_GROUP</group>
+            <order>30</order>
+            <widget>textarea</widget>
+        </param>
+    </config>
+    
+    <extension-points>
+    	<single-extension-point id="org.ametys.plugins.sms.broker.Broker"
+                                class="org.ametys.plugins.sms.broker.Broker"
+                                default-extension-id="org.ametys.plugins.sms.broker.LoggerBroker"/>    
+    </extension-points>
+    
+    <feature name="factories" depends="web/factories">
+        <extensions>
+            <extension point="org.ametys.plugins.repository.AmetysObjectFactoryExtensionPoint"
+                       id="org.ametys.plugins.sms.dao.SubscribersListFactory"
+                       class="org.ametys.plugins.sms.dao.SubscribersListFactory">
+                <scheme>category</scheme>
+                <nodetype>ametys:smslist</nodetype>
+                <nodetype-definition>nodetypes/sms_nodetypes.xml</nodetype-definition>
+            </extension>
+        </extensions>
+    </feature>
+    
+    <feature name="sms-clientside-targetfactory">
+        <extensions>
+            <extension id="org.ametys.sms.bus.SmsMessageTargetFactory"
+                       point="org.ametys.cms.workspace.uitool.MessageTargetFactoriesManager"
+                       class="org.ametys.runtime.ui.impl.StaticContextualClientSideElement">
+                <action class="org.ametys.sms.bus.SmsMessageTargetFactory">
+                    <param name="type">sms-list</param>
+                </action>
+                <scripts>
+                    <file>js/org/ametys/sms/bus/SmsMessageTargetFactory.i18n.js</file>
+                </scripts>
+            </extension>
+        </extensions>
+    </feature>
+    
+    <feature name="org.ametys.plugins.sms.brokers">
+        <extensions>
+            <extension point="org.ametys.plugins.sms.broker.Broker"
+                       id="org.ametys.plugins.sms.broker.LoggerBroker"
+                       class="org.ametys.plugins.sms.broker.LoggerBroker"
+                       logger="org.ametys.plugins.sms.broker.LoggerBroker"/>
+        </extensions>
+    </feature>
+    
     <feature name="org.ametys.plugins.sms.rights">
         <extensions>
             <extension point="org.ametys.runtime.plugins.core.right.RightsExtensionPoint"
@@ -29,6 +110,11 @@
                     <description>PLUGINS_SMS_RIGHTS_SEND_DESCRIPTION</description>
                     <category>PLUGINS_SMS_RIGHTS_CATEGORY</category>
                 </right>
+                <right id="Plugins_SMS_Right_Action">
+                    <label>PLUGINS_SMS_RIGHTS_ACTION_LABEL</label>
+                    <description>PLUGINS_SMS_RIGHTS_ACTION_DESCRIPTION</description>
+                    <category>PLUGINS_SMS_RIGHTS_CATEGORY</category>
+                </right>
             </extension>
         </extensions>
     </feature>
@@ -39,7 +125,7 @@
                        point="org.ametys.cms.workspace.ribbon.RibbonControlsManager" 
                        class="org.ametys.runtime.ui.impl.StaticContextualClientSideElement">
                 <action class="org.ametys.ribbon.control.button.ActionButton">
-                    <param name="action">org.ametys.sms.SendSMS</param>
+                    <param name="action">org.ametys.sms.SendSmsDialog</param>
                     <param name="label" i18n="true">PLUGINS_SMS_UITOOL_SEND_SMS_LABEL</param>
                     <param name="default-description" i18n="true">PLUGINS_SMS_UITOOL_SEND_SMS_DESCRIPTION</param>
                     <param name="footer" i18n="true">PLUGINS_SMS_UITOOL_SEND_SMS_FOOTER</param>
@@ -48,12 +134,282 @@
                     <param name="icon-large" file="true">img/sms-phone-48.png</param>
                 </action>
                 <scripts>
-                    <file>js/org/ametys/sms/SendSMSDialog.i18n.js</file>
-                    <file>js/org/ametys/sms/SMSActions.i18n.js</file>
+                    <file>js/org/ametys/sms/SendSMSAction.i18n.js</file>
                 </scripts>
                 <right>Plugins_SMS_Right_Send</right>
             </extension>
+            
+            <extension id="org.ametys.plugins.sms.SmsList"
+                       point="org.ametys.cms.workspace.ribbon.RibbonControlsManager" 
+                       class="org.ametys.runtime.ui.impl.StaticContextualClientSideElement">
+                <action class="org.ametys.ribbon.control.button.UniqueInstanceToolButton">
+                    <param name="role">uitool-smslist</param>
+                    <param name="label" i18n="true">PLUGINS_SMS_LIST_UITOOL_LABEL</param>
+                    <param name="default-description" i18n="true">PLUGINS_SMS_LIST_UITOOL_DESC</param>
+                    <param name="footer" i18n="true">PLUGINS_SMS_LIST_UITOOL_FOOTER</param>
+                    <param name="icon-small" file="true">img/sms-phone-16.png</param>
+                    <param name="icon-medium" file="true">img/sms-phone-32.png</param>
+                    <param name="icon-large" file="true">img/sms-phone-48.png</param>
+                </action>
+                <right>Plugins_SMS_Right_Action</right>
+            </extension>
+            
+            <extension id="org.ametys.plugins.sms.SmsListFactory"
+                       point="org.ametys.cms.workspace.uitool.UIToolsFactoriesManager"
+                       class="org.ametys.runtime.ui.impl.StaticClientSideElement">
+                <action class="org.ametys.sms.tool.SmsListToolFactory">
+                    <param name="role">uitool-smslist</param>
+                    <param name="title" i18n="true">PLUGINS_SMS_LIST_UITOOL_LABEL</param>
+                    <param name="description" i18n="true">PLUGINS_SMS_LIST_UITOOL_DESC</param>
+                    <param name="footer" i18n="true">PLUGINS_SMS_LIST_UITOOL_FOOTER</param>
+                    <param name="icon-small" file="true">img/sms-phone-16.png</param>
+                    <param name="icon-medium" file="true">img/sms-phone-32.png</param>
+                    <param name="icon-large" file="true">img/sms-phone-48.png</param>
+                    <param name="default-location">l</param>
+                </action>                      
+                <scripts>
+                    <file>js/org/ametys/sms/tool/SmsListTool.i18n.js</file>
+                    <file>js/org/ametys/sms/tool/SmsListToolFactory.js</file>
+                </scripts>
+                <css>
+                    <file>css/smslist-tool.css</file>
+                </css>
+            </extension>
+            
         </extensions>
     </feature>
     
+    <feature name="phoneNumbers">
+        <extensions>
+            <extension id="org.ametys.sms.PhoneNumbers"
+                       point="org.ametys.cms.workspace.ribbon.RibbonControlsManager"
+                       class="org.ametys.runtime.ui.impl.StaticContextualClientSideElement">
+                <action class="org.ametys.ribbon.control.button.UniqueInstanceToolAndRightSelectionButton">
+                    <param name="role">uitool-sms-phone-numbers</param>
+                    <param name="target-type">^sms-list$</param>
+                    <param name="label" i18n="true">PLUGINS_SMS_NUMBER_UITOOL_LABEL</param>
+                    <param name="default-description" i18n="true">PLUGINS_SMS_NUMBER_UITOOL_DESC</param>
+                    <param name="icon-small" file="true">img/sms-phone-16.png</param>
+                    <param name="icon-medium" file="true">img/sms-phone-32.png</param>
+                    <param name="icon-large" file="true">img/sms-phone-48.png</param>
+                    <param name="right-id">Plugins_SMS_Right_Action</param>                    
+                </action>                      
+                <right>Plugins_SMS_Right_Action</right>              
+            </extension>
+
+            <extension id="org.ametys.sms.PhoneNumbersFactory"
+                       point="org.ametys.cms.workspace.uitool.UIToolsFactoriesManager"
+                       class="org.ametys.runtime.ui.impl.StaticClientSideElement">
+                <action class="org.ametys.sms.tool.PhoneNumbersToolFactory">
+                    <param name="role">uitool-sms-phone-numbers</param>
+                    <param name="title" i18n="true">PLUGINS_SMS_NUMBER_UITOOL_LABEL</param>
+                    <param name="description" i18n="true">PLUGINS_SMS_NUMBER_UITOOL_DESC</param>
+                    <param name="icon-small" file="true">img/sms-phone-16.png</param>
+                    <param name="icon-medium" file="true">img/sms-phone-32.png</param>
+                    <param name="icon-large" file="true">img/sms-phone-48.png</param>
+                    <param name="default-location"></param>
+                </action>                      
+                <scripts>
+                    <file>js/org/ametys/sms/tool/PhoneNumbersTool.i18n.js</file>
+                    <file>js/org/ametys/sms/tool/PhoneNumbersToolFactory.js</file>
+                </scripts>
+            </extension>
+        </extensions>
+    </feature>
+    
+    <feature name="userinterface.sms.ribbon">
+        <extensions>
+            <extension id="org.ametys.plugins.sms.listTab"
+                       point="org.ametys.cms.workspace.ribbon.RibbonTabsManager"
+                       class="org.ametys.runtime.ui.impl.StaticContextualClientSideElement">
+                <action class="org.ametys.cms.ribbon.tab.SelectionTab">
+                    <param name="target-type">^(sms-list|sms-list-provider)$</param>
+                </action> 
+                <scripts>
+                    <file plugin="cms">js/org/ametys/cms/ribbon/tab/SelectionTab.js</file>
+                </scripts>      
+            </extension> 
+            
+            <extension id="org.ametys.plugins.sms.list.Add"
+                       point="org.ametys.cms.workspace.ribbon.RibbonControlsManager" 
+                       class="org.ametys.plugins.sms.action.RightAddListClientSideElement">
+                <action class="org.ametys.sms.button.AddListActionButton">
+                    <param name="target-type">^sms-list-provider$</param>                
+                    <param name="action">org.ametys.sms.list.Edit.add</param>
+                    <param name="type">sms-list|sms-list-provider</param>
+                    <param name="mode">write</param>
+                    <param name="label" i18n="true">PLUGINS_SMS_LIST_UITOOL_SMS_ADD_LABEL</param>
+                    <param name="default-description" i18n="true">PLUGINS_SMS_LIST_UITOOL_SMS_ADD_DESC</param>
+                    <param name="disabled-description" i18n="true">PLUGINS_SMS_LIST_NO_RIGHT_DESC</param>
+                    <param name="noeditable-description" i18n="true">PLUGINS_SMS_LIST_NO_RIGHT_DESC</param>
+                    <param name="no-right-description" i18n="true">PLUGINS_SMS_LIST_NO_RIGHT_DESC</param>
+                    <param name="refresh-description" i18n="true">PLUGINS_SMS_LIST_WAIT_DESC</param>             
+                    <param name="footer"></param>
+                    <param name="icon-small" file="true">img/list/add_16.png</param>
+                    <param name="icon-medium" file="true">img/list/add_32.png</param>
+                    <param name="icon-large" file="true">img/list/add_48.png</param>
+                </action>
+                <scripts>
+                    <file>js/org/ametys/sms/list/ListActions.i18n.js</file>
+                    <file>js/org/ametys/sms/button/AddListActionButton.i18n.js</file>
+                </scripts>
+                <right>Plugins_SMS_Right_Action</right>  
+            </extension>
+            
+            <extension id="org.ametys.plugins.sms.list.Edit"
+                       point="org.ametys.cms.workspace.ribbon.RibbonControlsManager" 
+                       class="org.ametys.runtime.ui.impl.StaticContextualClientSideElement">
+                <action class="org.ametys.ribbon.control.button.RightActionSelectionButton">
+                    <param name="target-type">^sms-list$</param>
+                    <param name="action">org.ametys.sms.list.Edit.edit</param>
+                    <param name="type">sms-list</param>
+                    <param name="mode">write</param>
+                    <param name="enable-multiselection">false</param>                    
+                    <param name="label" i18n="true">PLUGINS_SMS_LIST_UITOOL_SMS_EDIT_LABEL</param>
+                    <param name="default-description" i18n="true">PLUGINS_SMS_LIST_UITOOL_SMS_EDIT_DESC</param>
+                    <param name="disabled-description" i18n="true">PLUGINS_SMS_LIST_LISTACTION_DISABLED</param>
+                    <param name="noeditable-description" i18n="true">PLUGINS_SMS_LIST_LISTACTION_NOEDITABLE</param>
+                    <param name="no-right-description" i18n="true">PLUGINS_SMS_LIST_NO_RIGHT_DESC</param>
+                    <param name="refresh-description" i18n="true">PLUGINS_SMS_LIST_WAIT_DESC</param>             
+                    <param name="footer"></param>
+                    <param name="icon-small" file="true">img/list/edit_16.png</param>
+                    <param name="icon-medium" file="true">img/list/edit_32.png</param>
+                    <param name="icon-large" file="true">img/list/edit_48.png</param>
+                    <param name="right-id">Plugins_SMS_Right_Action</param>
+                </action>
+                <scripts>
+                    <file>js/org/ametys/sms/list/ListActions.i18n.js</file>
+                </scripts>
+                <right>Plugins_SMS_Right_Action</right>  
+            </extension>
+            
+            <extension id="org.ametys.plugins.sms.list.Delete"
+                       point="org.ametys.cms.workspace.ribbon.RibbonControlsManager" 
+                       class="org.ametys.runtime.ui.impl.StaticContextualClientSideElement">
+                <action class="org.ametys.ribbon.control.button.RightActionSelectionButton">
+                    <param name="target-type">^sms-list$</param>
+                    <param name="action">org.ametys.sms.list.Delete.act</param>
+                    <param name="type">sms-list</param>
+                    <param name="mode">write</param>
+                    <param name="label" i18n="true">PLUGINS_SMS_LIST_UITOOL_SMS_DELETE_LABEL</param>
+                    <param name="default-description" i18n="true">PLUGINS_SMS_LIST_UITOOL_SMS_DELETE_DESC</param>
+                    <param name="disabled-description" i18n="true">PLUGINS_SMS_LIST_LISTACTION_DISABLED</param>
+                    <param name="noeditable-description" i18n="true">PLUGINS_SMS_LIST_LISTACTION_NOEDITABLE</param>
+                    <param name="no-right-description" i18n="true">PLUGINS_SMS_LIST_NO_RIGHT_DESC</param>
+                    <param name="refresh-description" i18n="true">PLUGINS_SMS_LIST_WAIT_DESC</param>             
+                    <param name="footer"></param>
+                    <param name="icon-small" file="true">img/list/delete_16.png</param>
+                    <param name="icon-medium" file="true">img/list/delete_32.png</param>
+                    <param name="icon-large" file="true">img/list/delete_48.png</param>
+                    <param name="right-id">Plugins_SMS_Right_Action</param>
+                </action>
+                <scripts>
+                    <file>js/org/ametys/sms/list/ListActions.i18n.js</file>
+                </scripts>
+                <right>Plugins_SMS_Right_Action</right>  
+            </extension>
+        </extensions>
+    </feature>
+    
+    <feature name="org.ametys.plugins.sms.component.subscribersDAO">
+        <components>
+			<component role="org.ametys.plugins.sms.dao.SubscriberListsDAO"
+			           class="org.ametys.plugins.sms.dao.SubscriberListsDAO">
+			</component>
+        	<component role="org.ametys.plugins.sms.dao.SubscribeDAO"
+			           class="org.ametys.plugins.sms.dao.SubscribeDAO">
+			</component>
+        </components>
+    </feature>
+    
+    <feature name="org.ametys.plugins.sms.service">
+    	<config>
+			<param-ref id="org.ametys.plugins.sms.broker.autorized"/>
+        	<param-ref id="org.ametys.plugins.sms.broker.convert"/>
+        	<param-ref id="org.ametys.plugins.sms.broker.sample"/>
+        </config>
+    	<extensions>
+    		<extension point="org.ametys.runtime.plugins.core.right.RightsExtensionPoint"
+                       id="org.ametys.plugins.sms.services.rights">
+                <right id="SMS_Right_Service_SMS">
+                    <label i18n="true">PLUGINS_SMS_SUBSCRIBTION_SERVICE_RIGHT_LABEL</label>
+                    <description i18n="true">PLUGINS_SMS_SUBSCRIBTION_SERVICE_RIGHT_DESC</description>
+                    <category i18n="true">plugin.web:PLUGINS_WEB_RIGHTS_SERVICE_CREATE_CATEGORY</category>
+                </right>
+            </extension>
+            
+	        <extension point="org.ametys.web.service.ServiceExtensionPoint"
+				class="org.ametys.web.service.StaticService"
+				id="org.ametys.plugins.sms.service.subscribe">
+				<right>SMS_Right_Service_SMS</right>
+				<url>service/subscribe.html</url>
+				<label i18n="true">PLUGINS_SMS_SUBSCRIBTION_SERVICE_LABEL</label>
+				<description i18n="true">PLUGINS_SMS_SUBSCRIBTION_SERVICE_LABEL_DESC</description>
+				<thumbnail>
+					<small>img/sms-phone-16.png</small>
+					<medium>img/sms-phone-32.png</medium>
+					<large>img/sms-phone-48.png</large>
+				</thumbnail>
+				<parameters>
+					<parameter name="title" type="string">
+						<label i18n="true">PLUGINS_SMS_SUBSCRIBTION_SERVICE_TITLE_LABEL</label>
+						<description i18n="true">PLUGINS_SMS_SUBSCRIBTION_SERVICE_TITLE_LABEL_DESC</description>
+					</parameter>
+					<parameter name="subscribers-list" type="string">
+						<label i18n="true">PLUGINS_SMS_SUBSCRIBTION_SERVICE_SUBSCRIBE_LIST_LABEL</label>
+						<description i18n="true">PLUGINS_SMS_SUBSCRIBTION_SERVICE_SUBSCRIBE_LIST_LABEL_DESC</description>
+						<default-value>-</default-value>
+						<validation>
+							<mandatory/>
+						</validation>
+						<widget>sorted-enumeration</widget>
+						<enumeration>
+							<custom-enumerator class="org.ametys.plugins.sms.service.SubscribersListEnumerator" />			            	
+						</enumeration>
+					</parameter>
+					<parameter name="register-type" type="string">
+						<label i18n="true">PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_REGISTER_TYPE_LABEL</label>
+						<description i18n="true">PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_REGISTER_TYPE_DESC</description>
+						<default-value>user-choice</default-value>
+						<validation>
+							<mandatory/>
+						</validation>
+						<widget>sorted-enumeration</widget>
+						<enumeration>
+			            	<entry>
+			            		<label i18n="true">PLUGINS_SMS_SUBSCRIBTION_SERVICE_REGISTER_TYPE_USER_CHOICE</label>
+			            		<value>user-choice</value>
+			            	</entry>
+			            	<entry>
+			            		<label i18n="true">PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_SUBSCRIBE</label>
+			            		<value>subscribe</value>
+			            	</entry>
+			            	<entry>
+			            		<label i18n="true">PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_UNSUBSCRIBE</label>
+			            		<value>unsubscribe</value>
+			            	</entry>
+						</enumeration>
+					</parameter>
+					<parameter name="xslt" type="string">
+						<label i18n="true">PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_XSLT_LABEL</label>
+						<description i18n="true">PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_XSLT_DESC</description>
+						<default-value>pages/services/subscribe/subscribe_1.2.xsl</default-value>
+						<validation>
+							<mandatory/>
+						</validation>
+						<widget>sorted-enumeration</widget>
+						<enumeration>
+							<custom-enumerator class="org.ametys.web.service.ServiceXSLTEnumerator">
+							<path>pages/services/subscribe</path>
+							<values>
+								<value>pages/services/subscribe/subscribe_1.2.xsl</value>
+							</values>
+							</custom-enumerator>
+						</enumeration>
+					</parameter>
+				</parameters>
+			</extension>	
+		</extensions>
+	</feature>
+    
 </plugin>
Index: main/plugin-sms/sitemap.xmap
===================================================================
--- main/plugin-sms/sitemap.xmap	(revision 26839)
+++ main/plugin-sms/sitemap.xmap	(working copy)
@@ -17,25 +17,110 @@
 <map:sitemap xmlns:map="http://apache.org/cocoon/sitemap/1.0">
 
     <map:components>
+        <map:selectors>
+            <map:selector name="request-parameter" src="org.apache.cocoon.selection.RequestParameterSelector" logger="sitemap.selector.requestparameter" />
+        </map:selectors>
         <map:generators>
-            <map:generator name="phone-file" src="org.ametys.plugins.sms.PhoneNumberFileGenerator" logger="org.ametys.plugins.sms.PhoneNumberFileGenerator"/>
+            <map:generator name="subscribe" src="org.ametys.plugins.sms.service.SubscribeGenerator" />
+            <map:generator name="sms-list" src="org.ametys.plugins.sms.SubscribersListGenerator" />
+            <map:generator name="list" src="org.ametys.plugins.sms.dao.ListInfoGenerator"/>         
+            <map:generator name="subcriber-phone-number" src="org.ametys.plugins.sms.subscriber.PhoneNumbersListGenerator"/>         
         </map:generators>
         <map:actions>
             <map:action name="check-not-front" src="org.ametys.web.CheckNotFrontAction"/>
+            <map:action name="subscribe" src="org.ametys.plugins.sms.service.SubscribeAction"/>
+            <map:action name="send-sms" src="org.ametys.plugins.sms.SendSMSAction"/>
+            <map:action name="stop-sms" src="org.ametys.plugins.sms.stop.UnsubscribeAction"/>
+            <map:action name="add-edit-list" src="org.ametys.plugins.sms.action.AddOrEditListAction"/>
+            <map:action name="delete-list" src="org.ametys.plugins.sms.action.DeleteListAction"/>
         </map:actions>
     </map:components>
     
     <map:pipelines>
         <map:pipeline>
+            <map:match pattern="service/subscribe.html">
+				<map:generate type="subscribe" label="xml" />
+				<map:transform type="xslt" src="service://@xslt"/>
+				<map:serialize type="xml"/>
+			</map:match>
+			
+			<map:match pattern="service/do-subscribe">
+            	<map:act type="subscribe">
+            		<map:generate type="action-result" label="xml"/>
+	                <map:transform type="i18n">
+	                    <map:parameter name="locale" value="{locale:locale}"/>
+	                    <map:parameter name="default-catalogue-id" value="plugin.{request-attr:pluginName}"/>
+	                </map:transform>
+	                <map:select type="request-parameter">
+                        <map:parameter name="parameter-name" value="ajax"/>
+                        <map:when test="true">
+                            <map:serialize type="xml"/>
+                        </map:when>
+                        <map:otherwise>
+                            <map:transform src="service:sms://pages/services/subscribe/result/result.xsl"/>
+                            <map:serialize type="xml"/>
+                        </map:otherwise>
+                    </map:select>
+            	</map:act>
+			</map:match>
+
+			<map:match pattern="stop-sms">	
+				<map:act type="stop-sms" >
+					<map:generate type="action-result" />
+					<map:serialize type="xml"/>
+				</map:act>
+			</map:match>
+
             <map:match pattern="**">
                 <map:act type="check-not-front"/>
             </map:match>
             
-            <map:match pattern="read-phone-file">
-                <map:generate type="phone-file"/>
-                <map:transform src="stylesheets/import-result-json.xsl" type="xslt"/>
-                <map:serialize type="text" mime-type="text/html"/>
+            <map:match pattern="sms-list">
+                <map:generate type="sms-list" >
+                    <map:parameter name="site-name" value="{request-param:site}" />
+                    <map:parameter name="lang" value="{request-param:lang}" />
+                </map:generate>
+                <map:transform type="i18n" label="xml">
+                    <map:parameter name="locale" value="{locale:locale}"/>
+                </map:transform>
+                <map:serialize type="xml"/>
+            </map:match>
+			
+			<map:match pattern="send-sms">	
+				<map:act type="send-sms" >
+					<map:generate type="action-result" />
+					<map:serialize type="xml"/>
+				</map:act>
+			</map:match>
+            
+            <map:match pattern="add-edit-list">  
+                <map:act type="add-edit-list" >
+                    <map:generate type="action-result" >
+                        <map:parameter name="id" value="{id}"/>
+                    </map:generate>
+                    <map:serialize type="xml"/>
+                </map:act>
             </map:match>
+            
+            <map:match pattern="delete-list">  
+                <map:act type="delete-list" >
+                    <map:generate type="action-result" />
+                    <map:serialize type="xml"/>
+                </map:act>
+            </map:match>
+            
+            <map:match pattern="list/properties">
+                <map:generate type="list"/>
+                <map:serialize type="xml"/> 
+            </map:match>
+            
+            <map:match pattern="subcriber-phone-number">
+                <map:generate type="subcriber-phone-number" >
+                    <map:parameter name="id" value="{request-param:id}" />
+                </map:generate>
+                <map:serialize type="xml"/> 
+            </map:match>
+            
         </map:pipeline>
     </map:pipelines>
     
Index: main/plugin-sms/i18n/messages_fr.xml
===================================================================
--- main/plugin-sms/i18n/messages_fr.xml	(revision 26839)
+++ main/plugin-sms/i18n/messages_fr.xml	(working copy)
@@ -20,6 +20,8 @@
     <message key="PLUGINS_SMS_RIGHTS_CATEGORY">Connecteur SMS</message>
     <message key="PLUGINS_SMS_RIGHTS_SEND_LABEL">Envoyer des SMS</message>
     <message key="PLUGINS_SMS_RIGHTS_SEND_DESCRIPTION">Autorise à envoyer des SMS</message>
+    <message key="PLUGINS_SMS_RIGHTS_ACTION_LABEL">Gestion des listes SMS</message>
+    <message key="PLUGINS_SMS_RIGHTS_ACTION_DESCRIPTION">Autorise à gérer les listes de SMS</message>
     
     <!-- UI -->
     <message key="PLUGINS_SMS_UITOOL_SEND_SMS_LABEL">Envoyer un SMS</message>
@@ -30,12 +32,12 @@
     <message key="PLUGINS_SMS_SEND_DIALOG_TITLE">Envoyer un SMS</message>
     <message key="PLUGINS_SMS_SEND_DIALOG_BUTTON_OK">OK</message>
     <message key="PLUGINS_SMS_SEND_DIALOG_BUTTON_CANCEL">Annuler</message>
-    <message key="PLUGINS_SMS_SEND_DIALOG_BUTTON_IMPORT">Ajouter des numéros depuis un fichier...</message>
-    <message key="PLUGINS_SMS_SEND_DIALOG_HINT">Entrez le message à envoyer par SMS (160 caractères maximum) ainsi que la liste des numéros des destinataires.</message>
+    <message key="PLUGINS_SMS_SEND_DIALOG_HINT">Sélectionnez une liste de diffusion à laquelle envoyer un SMS et saisissez le texte à envoyer (160 caractères maximum).</message>
+    <message key="PLUGINS_SMS_SEND_DIALOG_HINT2">Pour être en conformité avec la législation, il peut être nécessaire d'indiquer à l'utilisateur comment se désabonner de la liste de diffusion.&lt;br/&gt;Par exemple si votre fournisseur de SMS le gère, pensez à l'indiquer en fin de message : 'Stop sms: répondez STOP'</message>
     <message key="PLUGINS_SMS_SEND_DIALOG_FIELD_MESSAGE_LABEL">Message</message>
     <message key="PLUGINS_SMS_SEND_DIALOG_FIELD_MESSAGE_DESCRIPTION">Message du SMS (160 caractères maximum)</message>
-    <message key="PLUGINS_SMS_SEND_DIALOG_FIELD_PHONE_NUMBERS_LABEL">Numéros de téléphone</message>
-    <message key="PLUGINS_SMS_SEND_DIALOG_FIELD_PHONE_NUMBERS_DESCRIPTION">Liste des numéros de téléphone des destinataires du SMS</message>
+    <message key="PLUGINS_SMS_SEND_DIALOG_FIELD_LIST_LABEL">Liste de diffusion</message>
+    <message key="PLUGINS_SMS_SEND_DIALOG_FIELD_LIST_DESCRIPTION">Les différentes listes de diffusion SMS du site.</message>
     <message key="PLUGINS_SMS_SEND_DIALOG_IMPORT_TITLE">Importer des numéros de téléphone</message>
     <message key="PLUGINS_SMS_SEND_DIALOG_IMPORT_BUTTON_OK">OK</message>
     <message key="PLUGINS_SMS_SEND_DIALOG_IMPORT_BUTTON_CANCEL">Annuler</message>
@@ -51,4 +53,120 @@
     <message key="PLUGINS_SMS_SEND_DIALOG_IMPORT_ERROR_NOT_TEXT">Le fichier doit être un fichier texte de type .txt ou .csv.</message>
     <message key="PLUGINS_SMS_SEND_DIALOG_IMPORT_ERROR_UNKNOWN">L'import des numéros a échoué.</message>
     
+    <!--  Send SMS --> 
+    <message key="PLUGINS_SMS_SEND_ERROR">Une erreur est survenu dans l'envoi du SMS</message>
+    <message key="PLUGINS_SMS_SEND_FORM_ERROR_TITLE">Formulaire invalide</message>
+    <message key="PLUGINS_SMS_SEND_FORM_ERROR_MESSAGE">Le formulaire d'envoi de SMS est invalide : sélectionnez une liste de diffusion et saisissez un message de moins de 160 caractères.</message>
+    <message key="PLUGINS_SMS_SEND_WAITING_MSG">En attente de l'envoi du SMS.</message>
+    
+    <!--  List SMS -->
+    <message key="PLUGINS_SMS_LIST_UITOOL_LABEL">Listes de diffusion SMS.</message>
+    <message key="PLUGINS_SMS_LIST_UITOOL_DESC">Gestion des différentes listes de diffusion SMS</message>
+    <message key="PLUGINS_SMS_LIST_UITOOL_FOOTER">Voir l'aide pour plus de détails.</message>
+    <message key="PLUGINS_SMS_LIST_UITOOL_LANGUAGE">Langue</message>
+    <message key="PLUGINS_SMS_LIST_GET_LANGUAGES_ERROR">Une erreur est survenue : impossible de récupérer la liste des langues disponibles pour ce site.</message>
+    <message key="PLUGINS_SMS_LIST_UITOOL_COL_TITLE">Nom</message>
+    <message key="PLUGINS_SMS_LIST_UITOOL_COL_SUBSCRIBERS">Inscrit(s)</message>
+    <message key="PLUGINS_SMS_LIST_UITOOL_SMS_ADD_LABEL">Nouvelle liste</message>
+    <message key="PLUGINS_SMS_LIST_UITOOL_SMS_ADD_DESC">Ajoute une nouvelle liste</message>
+    <message key="PLUGINS_SMS_LIST_UITOOL_SMS_EDIT_LABEL">Modifier</message>
+    <message key="PLUGINS_SMS_LIST_UITOOL_SMS_EDIT_DESC">Modifie la liste sélectionnée</message>
+    <message key="PLUGINS_SMS_LIST_UITOOL_SMS_DELETE_LABEL">Supprimer</message>
+    <message key="PLUGINS_SMS_LIST_UITOOL_SMS_DELETE_DESC">Supprime la liste sélectionnée</message>
+    <message key="PLUGINS_SMS_LIST_LISTACTION_DISABLED">Ce bouton est désactivé car aucune liste n'est sélectionnée.</message>
+    <message key="PLUGINS_SMS_LIST_LISTACTION_NOEDITABLE">Ce bouton est désactivé car la liste sélectionnée ne peut être pas modifiée.</message>
+    <message key="PLUGINS_SMS_LIST_NO_RIGHT_DESC">Ce bouton est désactivé car vous n'avez pas les droits nécessaires.</message>
+    <message key="PLUGINS_SMS_LIST_WAIT_DESC">Veuillez patienter...</message>
+    
+    <!--  +
+          | BUS
+          + -->
+    <message key="PLUGINS_SMS_SMSLISTMESSAGETARGET_BUS_ERROR">Une erreur est survenue : elle a empêché la sélection de la liste de se terminer correctement. Il est conseillé de fermer puis de rouvrir l'outil ayant fait la sélection.</message>
+    
+    <!-- PhoneNumbers SMS -->
+    <message key="PLUGINS_SMS_NUMBER_UITOOL_COL_TITLE">Numéro</message>
+    <message key="PLUGINS_SMS_NUMBER_UITOOL_COL_DATE">Date d'inscription</message>
+    <message key="PLUGINS_SMS_NUMBER_UITOOL_LIST_SELECT">Sélectionnez une liste pour voir la liste des numéros abonnés à cette liste.</message>
+    <message key="PLUGINS_SMS_NUMBER_UITOOL_LABEL">Liste des numéros</message>
+    <message key="PLUGINS_SMS_NUMBER_UITOOL_DESC">La liste des numéros abonnés à la liste de diffusion.</message>
+    <message key="PLUGINS_SMS_NUMBER_UITOOL_DATETIME_FORMAT">d/m/Y à H:i</message>
+    <message key="PLUGINS_SMS_NUMBER_UITOOL_DATE_FORMAT">d/m/Y</message>
+        
+    <!--  Add List SMS -->
+    <message key="PLUGINS_SMS_ADD_LIST_FORM_TITLE_LABEL">Nom</message>
+    <message key="PLUGINS_SMS_ADD_LIST_FORM_TITLE_DESC">Le nom de la liste de diffusion</message>
+    <message key="PLUGINS_SMS_ADD_LIST_FORM_DESC_LABEL">Description</message>
+    <message key="PLUGINS_SMS_ADD_LIST_FORM_DESC_DESC">La description de la liste de diffusion</message>
+    <message key="PLUGINS_SMS_ADD_LIST_FORM_HINT">Veuillez remplir les informations suivantes pour créer ou éditer une liste de diffusion SMS.</message>
+    <message key="PLUGINS_SMS_ADD_LIST_FORM_BOX_TITLE">Nouvelle liste</message>    
+    <message key="PLUGINS_SMS_ADD_LIST_DELETE_ERROR">La suppression a échouée</message>
+    <message key="PLUGINS_SMS_ADD_LIST_HANDLE_LIST_DELETE">Supprimer la liste</message>
+    <message key="PLUGINS_SMS_ADD_LIST_HANDLE_LIST_DELETE_CONFIRM">Etes-vous sûr de vouloir supprimer cette liste ?</message>
+    <message key="PLUGINS_SMS_ADD_LIST_ADD_EDIT_ERROR">L'ajout ou l'édition a échoué.</message>
+    <message key="PLUGINS_SMS_ADD_LIST_WAITING_MSG">En attente de la création ou la mofication de la liste.</message>
+    <message key="PLUGINS_SMS_ADD_LIST_FORM_ERROR_MESSAGE">Le formulaire de création ou d'édition d'une liste est invalide : le nom est obligatoire.</message>
+    
+    <!-- Ribbon -->
+    <message key="RIBBON_TABS_TAB_SMS_LIST_LABEL">Listes</message>
+    <message key="RIBBON_TABS_TAB_SMS_GROUP_LIST_LABEL">Listes</message>
+    <message key="RIBBON_TABS_TAB_SMS_GROUP_LIST_LABEL">Abonnés</message>
+    <message key="RIBBON_TABS_TAB_SMS_LABEL">Liste de diffusion SMS</message>
+    
+    <!--  Subscribtion Service -->
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_LABEL">Abonnement diffusion SMS</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_LABEL_DESC">Formulaire d'abonnement à une liste de diffusion.</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_TITLE_LABEL">Titre</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_TITLE_LABEL_DESC">Titre du service d'enregistrement des numéros</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_SUBSCRIBE_LIST_LABEL">Choix de la liste de diffusion</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_SUBSCRIBE_LIST_LABEL_DESC">Choix de la liste de diffusion dans laquelle l'utilisateur enregistrera son numéro.</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_REGISTER_TYPE_USER_CHOICE">Dans une liste déroulante pour le visiteur</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_PHONE">Votre numéro de téléphone</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_PHONE_INFO_1">au format </message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_PHONE_INFO_2"> ,</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_PHONE_INFO_3"> ou </message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_LIST">Liste de diffusion</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_CAPTCHA">Entrez les caractères figurant dans l'image ci-contre</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_SUBMIT">Valider</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_REGISTER_TYPE_LABEL">Type de demande</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_REGISTER_TYPE_DESC">Choix du type de formulaire (formulaire d'inscription, de désinscription ou on laisse le choix à l'utilisateur).</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_UNSUBSCRIBE">Désinscription</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_SUBSCRIBE">Inscription</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_INFO">Veuillez remplir le formulaire ci-dessous pour vous inscrire ou vous désinscrire à la liste de diffusion <strong>{0}</strong>.</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_SUBSCRIBE_INFO">Veuillez remplir le formulaire ci-dessous pour vous inscrire à la liste de diffusion <strong>{0}</strong>.</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_UNSUBSCRIBE_INFO">Veuillez remplir le formulaire ci-dessous pour vous désinscrire à la liste de diffusion <strong>{0}</strong>.</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_INFO_LIST">Veuillez remplir le formulaire ci-dessous pour vous inscrire ou vous désinscrire à une liste de diffusion.</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_SUBSCRIBE_INFO_LIST">Veuillez remplir le formulaire ci-dessous pour vous inscrire à une liste de diffusion.</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_UNSUBSCRIBE_INFO_LIST">Veuillez remplir le formulaire ci-dessous pour vous désinscrire à une liste de diffusion.</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_MANDATORY_INFO">Les champs suivis d'une astérisque * sont obligatoires.</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_ERROR_DIRECTORY">La liste de diffusion n'est pas définie</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_ERROR_CAPTCHA">Le texte de l'image est invalide</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_ERROR_PHONE">Le numéro de téléphone est invalide</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_ERROR_PHONE2">Le numéro de téléphone enregistré ne correspond pas au format internationnal</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_ERROR_REGISTER">Le type du formulaire n'est pas défini</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_MESSAGE_ALREADY_RECORD">Le numéro de téléphone existe déjà dans cette liste de diffusion.</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_MESSAGE_DELETE">Votre numéro de téléphone a bien été supprimé de la liste de diffusion.</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_MESSAGE_ADD">Votre numéro de téléphone a bien été ajouté à la liste de diffusion.</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_MESSAGE_NOT_RECORD">Le numéro de téléphone n'existe pas dans cette liste de diffusion.</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_ERROR_TITLE">Saisie incorrecte</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_ERROR_DESCRIPTION">Votre formulaire contient les erreurs suivantes :</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_BACK">Cliquez ici pour revenir à la page précédente</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_BACK_ALT">Utilisez le bouton 'Précédent' de votre navigateur web pour corriger votre formulaire d'envoi par mail et rafraichissez l'image.</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_MESSAGE_RECORD_TITLE">Inscription réussie</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_MESSAGE_DELETED_TITLE">Supression réussie</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_MESSAGE_ISSUE_TITLE">Une erreur est survenue</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_BROKER_AUTORIZED_LABEL">Expression régulière des numéros autorisés</message>
+	<message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_BROKER_AUTORIZED_DESCRIPTION">Ce sont les expressions régulières qui définissent les différents formats de numéro de téléphone autorisés pour l'envoi de sms.&lt;br/&gt;Chaque expression doit être au format internationnal (commençant par '+' et suivi du code du pays, par exemple +33 pour la France).&lt;br/&gt;Une expression régulière par ligne.</message>
+	<message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_BROKER_CONVERT_LABEL">Expression régulière de conversion des numéros</message>
+	<message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_BROKER_CONVERT_DESCRIPTION">Ce paramètre permet d'accepter d'autres formats de numéro que le format international.&lt;br/&gt;Chaque ligne représente une conversion à opérer d'un format lisible vers le format international.&lt;br/&gt; Elle prend la forme 'regexp/transformation' :&lt;br/&gt; regexp: elle définit le format du numéro que peut entrer l'utilisateur.&lt;br/&gt; transformation: chacun des groupes de l'expression régulière sera remplacé par le groupe dans la transformation (les numéros commencent à 1).&lt;br/&gt; Un paramètre par ligne.</message>
+	<message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_BROKER_SAMPLE_LABEL">Exemple des formats autorisés</message>
+	<message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_BROKER_SAMPLE_DESCRIPTION">Ce sont les exemples que vous fournissez à l'utilisateur pour qu'il rentre les numéros de téléphone au bon format.</message>
+	<message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_BROKER_CATEGORY">Envoi de SMS</message>
+	<message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_BROKER_GROUP">Gestion des numéros</message>
+	<message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_NO_LIST">Il n'y a pas de liste de diffusion définie. Voyez avec l'administrateur.</message>
+	<message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_XSLT_LABEL">Affichage</message>
+	<message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_XSLT_DESC">Rendu du service</message>
+	<message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_RIGHT_LABEL">Abonnement SMS</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_RIGHT_DESC">Droit de créer les services d'abonnement aux listes de diffusion SMS</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_LIST_CHOOSE">- Choisissez une liste -</message>
+    
 </catalogue>
Index: main/plugin-sms/i18n/messages_en.xml
===================================================================
--- main/plugin-sms/i18n/messages_en.xml	(revision 26839)
+++ main/plugin-sms/i18n/messages_en.xml	(working copy)
@@ -20,6 +20,8 @@
     <message key="PLUGINS_SMS_RIGHTS_CATEGORY">SMS connector</message>
     <message key="PLUGINS_SMS_RIGHTS_SEND_LABEL">Send SMS</message>
     <message key="PLUGINS_SMS_RIGHTS_SEND_DESCRIPTION">Allow to send SMS</message>
+    <message key="PLUGINS_SMS_RIGHTS_ACTION_LABEL">Sms list management</message>
+    <message key="PLUGINS_SMS_RIGHTS_ACTION_DESCRIPTION">Allow to handle sms list</message>
     
     <!-- UI -->
     <message key="PLUGINS_SMS_UITOOL_SEND_SMS_LABEL">Send a SMS</message>
@@ -30,7 +32,6 @@
     <message key="PLUGINS_SMS_SEND_DIALOG_TITLE">Send a SMS</message>
     <message key="PLUGINS_SMS_SEND_DIALOG_BUTTON_OK">OK</message>
     <message key="PLUGINS_SMS_SEND_DIALOG_BUTTON_CANCEL">Cancel</message>
-    <message key="PLUGINS_SMS_SEND_DIALOG_BUTTON_IMPORT">Add numbers from a file...</message>
     <message key="PLUGINS_SMS_SEND_DIALOG_HINT">Enter the SMS message to send (160 maximum characters) and fill in the recipients' telephone numbers.</message>
     <message key="PLUGINS_SMS_SEND_DIALOG_FIELD_MESSAGE_LABEL">Message</message>
     <message key="PLUGINS_SMS_SEND_DIALOG_FIELD_MESSAGE_DESCRIPTION">SMS message (160 maximum characters)</message>
@@ -51,4 +52,118 @@
     <message key="PLUGINS_SMS_SEND_DIALOG_IMPORT_ERROR_NOT_TEXT">The file must be a text file of type .txt or .csv.</message>
     <message key="PLUGINS_SMS_SEND_DIALOG_IMPORT_ERROR_UNKNOWN">Importing the telephone numbers has failed.</message>
     
+    <!--  Send SMS -->
+    <message key="PLUGINS_SMS_SEND_ERROR">An error occurred in the SMS sending.</message>
+    <message key="PLUGINS_SMS_SEND_FORM_ERROR_TITLE">Invalid form</message>
+    <message key="PLUGINS_SMS_SEND_FORM_ERROR_MESSAGE">The SMS form is invalid.</message>
+    <message key="PLUGINS_SMS_SEND_WAITING_MSG">Waiting the SMS sending.</message>
+    
+    <!--  List SMS -->
+    <message key="PLUGINS_SMS_LIST_UITOOL_LABEL">SMS lists.</message>
+    <message key="PLUGINS_SMS_LIST_UITOOL_DESC">SMS lists management</message>
+    <message key="PLUGINS_SMS_LIST_UITOOL_FOOTER">See help for details</message>
+    <message key="PLUGINS_SMS_LIST_UITOOL_LANGUAGE">Language</message>
+    <message key="PLUGINS_SMS_LIST_GET_LANGUAGES_ERROR">An error occurred while retrieving the available languages for this site.</message>
+    <message key="PLUGINS_SMS_LIST_UITOOL_COL_TITLE">Name</message>
+    <message key="PLUGINS_SMS_LIST_UITOOL_COL_SUBSCRIBERS">Subscriber(s)</message>
+    <message key="PLUGINS_SMS_LIST_UITOOL_SMS_ADD">Add</message>
+    <message key="PLUGINS_SMS_LIST_UITOOL_SMS_ADD_DESC">Add a new list</message>
+    <message key="PLUGINS_SMS_LIST_UITOOL_SMS_EDIT_LABEL">Edit</message>
+    <message key="PLUGINS_SMS_LIST_UITOOL_SMS_EDIT_DESC">Edit the selected list</message>
+    <message key="PLUGINS_SMS_LIST_UITOOL_SMS_DELETE_LABEL">Delete</message>
+    <message key="PLUGINS_SMS_LIST_UITOOL_SMS_DELETE_DESC">Delete the selected list</message>
+    <message key="PLUGINS_SMS_LIST_LISTACTION_DISABLED">This button is disabled because no list is selected.</message>
+    <message key="PLUGINS_SMS_LIST_LISTACTION_NOEDITABLE">This button is disabled because the the selected list can not be modified.</message>
+    <message key="PLUGINS_SMS_LIST_NO_RIGHT_DESC">This button is disabled because you haven't the convenient rights.</message>
+    <message key="PLUGINS_SMS_LIST_WAIT_DESC">Please wait...</message>
+    
+     <!--  +
+          | BUS
+          + -->
+    <message key="PLUGINS_SMS_SMSLISTMESSAGETARGET_BUS_ERROR">An error occurred: it prevented the selection of the list to complete successfully. It is advisable to close and reopen the tool responsible for the selection.</message>
+    
+     <!-- PhoneNumbers SMS -->
+    <message key="PLUGINS_SMS_NUMBER_UITOOL_COL_TITLE">Phone numbers</message>
+    <message key="PLUGINS_SMS_NUMBER_UITOOL_COL_DATE">Subscribe date</message>
+    <message key="PLUGINS_SMS_NUMBER_UITOOL_LIST_SELECT">Choose a sms list to see the subscribers' phone numbers.</message>
+    <message key="PLUGINS_SMS_NUMBER_UITOOL_LABEL">Phone numbers list</message>
+    <message key="PLUGINS_SMS_NUMBER_UITOOL_DESC">Displays the list of the subscribers' phone numbers.</message>
+    <message key="PLUGINS_SMS_NUMBER_UITOOL_DATETIME_FORMAT">m/d/Y at H:i</message>
+    
+    <!--  Add List SMS -->
+    <message key="PLUGINS_SMS_ADD_LIST_FORM_TITLE_LABEL">Name</message>
+    <message key="PLUGINS_SMS_ADD_LIST_FORM_TITLE_DESC">The name of the sms list</message>
+    <message key="PLUGINS_SMS_ADD_LIST_FORM_DESC_LABEL">Description</message>
+    <message key="PLUGINS_SMS_ADD_LIST_FORM_DESC_DESC">the description of the sms list</message>
+    <message key="PLUGINS_SMS_ADD_LIST_FORM_HINT">Please, fill the following input to create or edit a sms list.</message>
+    <message key="PLUGINS_SMS_ADD_LIST_FORM_BOX_TITLE">New list</message>    
+    <message key="PLUGINS_SMS_ADD_LIST_DELETE_ERROR">The deletion has failed.</message>
+    <message key="PLUGINS_SMS_ADD_LIST_HANDLE_LIST_DELETE">Delete the list</message>
+    <message key="PLUGINS_SMS_ADD_LIST_HANDLE_LIST_DELETE_CONFIRM">Are you sure you want to delete this list ?</message>
+    <message key="PLUGINS_SMS_ADD_LIST_ADD_EDIT_ERROR">The add or the edition has failed.</message>
+    <message key="PLUGINS_SMS_ADD_LIST_WAITING_MSG">Waiting the add or the edit of the sms list.</message>
+    <message key="PLUGINS_SMS_ADD_LIST_FORM_ERROR_MESSAGE">The creation or edition form is invalid : the name is mandatory.</message>
+    
+     <!-- Ribbon -->
+    <message key="RIBBON_TABS_TAB_SMS_LIST_LABEL">Lists</message>
+    <message key="RIBBON_TABS_TAB_SMS_GROUP_LIST_LABEL">Lists</message>
+    <message key="RIBBON_TABS_TAB_SMS_GROUP_LIST_LABEL">Subscribers</message>
+    <message key="RIBBON_TABS_TAB_SMS_LABEL">SMS lists</message>
+    
+    <!--  Subscribtion Service -->
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_LABEL">Subscribers SMS</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_LABEL_DESC">Form to subscribe to a mailing list.</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_TITLE_LABEL">Title</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_TITLE_LABEL_DESC">Subscriber service title</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_SUBSCRIBE_LIST_LABEL">Mailing list</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_SUBSCRIBE_LIST_LABEL_DESC">Choice of the mailing list.</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_REGISTER_TYPE_USER_CHOICE">In a drop-down menu for the user</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_PHONE">Your phone number</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_PHONE_INFO_1">in the form of </message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_PHONE_INFO_2"> ,</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_PHONE_INFO_3"> or </message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_LIST">Mailing List</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_CAPTCHA">Enter the characters in the picture below</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_SUBMIT">Validate</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_REGISTER_TYPE_LABEL">Type</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_REGISTER_TYPE_DESC">Choice of the type of the form (subscribe form, unsubscribe form or we let the user choose).</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_UNSUBSCRIBE">Unsubscribe</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_SUBSCRIBE">Subscribe</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_INFO">Please fill out the form below to subscribe of unsubscribe to the mailing list <strong>{0}</strong>.</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_SUBSCRIBE_INFO">Please fill out the form below to subscribe to the mailing list <strong>{0}</strong>.</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_UNSUBSCRIBE_INFO">Please fill out the form below to unsubscribe to the mailing list <strong>{0}</strong>.</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_INFO_LIST">Please fill out the form below to subscribe or unsubscribe to a mailing list.</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_SUBSCRIBE_INFO_LIST">Please fill out the form below to subscribe to a mailing list.</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_UNSUBSCRIBE_INFO_LIST">Please fill out the form below to unsubscribe a the mailing list.</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_MANDATORY_INFO">Fields with an asterisk * are mandatory</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_ERROR_DIRECTORY">The mailing list is undefined</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_ERROR_CAPTCHA">The characters of the picture are not valid</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_ERROR_PHONE">The phone number is not valid</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_ERROR_PHONE2">The phone number recorded doesn't match with the international format</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_ERROR_REGISTER">The form type is undefined</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_MESSAGE_ALREADY_RECORD">The phone number already exists in this mailing list.</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_MESSAGE_DELETE">Your phone number was deleted successfully of the mailing list.</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_MESSAGE_ADD">Your phone number was added successfully of the mailing list.</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_MESSAGE_NOT_RECORD">Your phone number doesn't exist in this mailing list.</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_ERROR_TITLE">Incorrect input</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_ERROR_DESCRIPTION">Your form contains the following mistake :</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_BACK_ALT">Use the 'back' button of your web browser to correct your data and refresh the image.</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_BACK">Click here to come back to the form</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_MESSAGE_RECORD_TITLE">Subscribtion is successfull</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_MESSAGE_DELETED_TITLE">Unsubscribtion is successfull</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_MESSAGE_ISSUE_TITLE">An error occurred</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_BROKER_AUTORIZED_LABEL">Regular expression of the authorized number</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_BROKER_AUTORIZED_DESCRIPTION">This is regular expression which defined the different authorized phone number formats.&lt;br/&gt;Each expression must match with the international format (begin with '+' and follow by the country code, for example +33 for France).&lt;br/&gt;One regular expression by line.</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_BROKER_CONVERT_LABEL">Regular expression to convert numbers</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_BROKER_CONVERT_DESCRIPTION">To allow other phone number format than the international one.&lt;br/&gt;Each line is a conversion to transform a well-formed phone number to the international format.&lt;br/&gt; To the form 'regexp/transformation' :&lt;br/&gt; regexp: it defined the phone number format that the user can enter.&lt;br/&gt; transformation: each group in the regular expression will be replaced by the corresponding group in the transformation (group number begin to 1).</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_BROKER_SAMPLE_LABEL">Authorized number example</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_BROKER_SAMPLE_DESCRIPTION">This is the sample you show to the user to help him to enter phone number in the good format.</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_BROKER_CATEGORY">SMS</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_BROKER_GROUP">Phone number management</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_NO_LIST">There are no defined mailing list. Contact the admin.</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_XSLT_LABEL">Display</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_XSLT_DESC">Service rendering</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_RIGHT_LABEL">Subscribers SMS</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_RIGHT_DESC">Right to create subscribers sms service</message>
+    <message key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_LIST_CHOOSE">- Choose a list -</message>
 </catalogue>
Index: main/plugin-sms/scripts/oracle/sms_subscribers.sql
===================================================================
--- main/plugin-sms/scripts/oracle/sms_subscribers.sql	(revision 0)
+++ main/plugin-sms/scripts/oracle/sms_subscribers.sql	(revision 0)
@@ -0,0 +1,22 @@
+--
+--  Copyright 2009 Anyware Services
+--
+--  Licensed under the Apache License, Version 2.0 (the "License");
+--  you may not use this file except in compliance with the License.
+--  You may obtain a copy of the License at
+--
+--      http://www.apache.org/licenses/LICENSE-2.0
+--
+--  Unless required by applicable law or agreed to in writing, software
+--  distributed under the License is distributed on an "AS IS" BASIS,
+--  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+--  See the License for the specific language governing permissions and
+--  limitations under the License.
+--
+
+CREATE TABLE SMS_Subscribers(
+	PhoneNumber VARCHAR(24) NOT NULL,
+	ListId VARCHAR(64) NOT NULL, 
+	Date DATE NOT NULL,
+	PRIMARY KEY(PhoneNumber, ListId)
+);
Index: main/plugin-sms/scripts/postgresql/sms_subscribers.sql
===================================================================
--- main/plugin-sms/scripts/postgresql/sms_subscribers.sql	(revision 0)
+++ main/plugin-sms/scripts/postgresql/sms_subscribers.sql	(revision 0)
@@ -0,0 +1,27 @@
+--
+--  Copyright 2009 Anyware Services
+--
+--  Licensed under the Apache License, Version 2.0 (the "License");
+--  you may not use this file except in compliance with the License.
+--  You may obtain a copy of the License at
+--
+--      http://www.apache.org/licenses/LICENSE-2.0
+--
+--  Unless required by applicable law or agreed to in writing, software
+--  distributed under the License is distributed on an "AS IS" BASIS,
+--  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+--  See the License for the specific language governing permissions and
+--  limitations under the License.
+--
+BEGIN;
+
+DROP TABLE IF EXISTS SMS_Subscribers;
+
+CREATE TABLE SMS_Subscribers(
+	PhoneNumber VARCHAR(24) NOT NULL,
+	ListId VARCHAR(64) NOT NULL, 
+	Date TIMESTAMP NOT NULL,
+	PRIMARY KEY(PhoneNumber, ListId)
+);
+
+COMMIT;
Index: main/plugin-sms/scripts/derby/sms_subscribers.sql
===================================================================
--- main/plugin-sms/scripts/derby/sms_subscribers.sql	(revision 0)
+++ main/plugin-sms/scripts/derby/sms_subscribers.sql	(revision 0)
@@ -0,0 +1,22 @@
+--
+--  Copyright 2009 Anyware Services
+--
+--  Licensed under the Apache License, Version 2.0 (the "License");
+--  you may not use this file except in compliance with the License.
+--  You may obtain a copy of the License at
+--
+--      http://www.apache.org/licenses/LICENSE-2.0
+--
+--  Unless required by applicable law or agreed to in writing, software
+--  distributed under the License is distributed on an "AS IS" BASIS,
+--  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+--  See the License for the specific language governing permissions and
+--  limitations under the License.
+--
+
+CREATE TABLE SMS_Subscribers(
+	PhoneNumber VARCHAR(24) NOT NULL,
+	ListId VARCHAR(64) NOT NULL, 
+	Date date NOT NULL,
+	PRIMARY KEY(PhoneNumber, ListId)
+);
Index: main/plugin-sms/scripts/hsqldb/sms_subscribers.sql
===================================================================
--- main/plugin-sms/scripts/hsqldb/sms_subscribers.sql	(revision 0)
+++ main/plugin-sms/scripts/hsqldb/sms_subscribers.sql	(revision 0)
@@ -0,0 +1,23 @@
+--
+--  Copyright 2013 Anyware Services
+--
+--  Licensed under the Apache License, Version 2.0 (the "License");
+--  you may not use this file except in compliance with the License.
+--  You may obtain a copy of the License at
+--
+--      http://www.apache.org/licenses/LICENSE-2.0
+--
+--  Unless required by applicable law or agreed to in writing, software
+--  distributed under the License is distributed on an "AS IS" BASIS,
+--  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+--  See the License for the specific language governing permissions and
+--  limitations under the License.
+--
+
+CREATE TABLE SMS_Subscribers(
+	PhoneNumber VARCHAR(24) NOT NULL,
+	ListId VARCHAR(64) NOT NULL, 
+	Date date NOT NULL,
+	PRIMARY KEY(PhoneNumber, ListId)
+);
+
Index: main/plugin-sms/scripts/mysql/sms_subscribers.sql
===================================================================
--- main/plugin-sms/scripts/mysql/sms_subscribers.sql	(revision 0)
+++ main/plugin-sms/scripts/mysql/sms_subscribers.sql	(revision 0)
@@ -0,0 +1,23 @@
+--
+--  Copyright 2009 Anyware Services
+--
+--  Licensed under the Apache License, Version 2.0 (the "License");
+--  you may not use this file except in compliance with the License.
+--  You may obtain a copy of the License at
+--
+--      http://www.apache.org/licenses/LICENSE-2.0
+--
+--  Unless required by applicable law or agreed to in writing, software
+--  distributed under the License is distributed on an "AS IS" BASIS,
+--  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+--  See the License for the specific language governing permissions and
+--  limitations under the License.
+--
+
+DROP TABLE if EXISTS SMS_Subscribers;
+CREATE TABLE SMS_Subscribers(
+	PhoneNumber VARCHAR(24) NOT NULL,
+	ListId VARCHAR(64) NOT NULL, 
+	Date datetime NOT NULL,
+	PRIMARY KEY(PhoneNumber, ListId)
+)ENGINE=innodb;
Index: main/plugin-sms/src/org/ametys/plugins/sms/dao/SubscriberListsDAO.java
===================================================================
--- main/plugin-sms/src/org/ametys/plugins/sms/dao/SubscriberListsDAO.java	(revision 0)
+++ main/plugin-sms/src/org/ametys/plugins/sms/dao/SubscriberListsDAO.java	(revision 0)
@@ -0,0 +1,103 @@
+package org.ametys.plugins.sms.dao;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.avalon.framework.component.Component;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
+
+import org.ametys.cms.FilterNameHelper;
+import org.ametys.plugins.repository.AmetysObject;
+import org.ametys.plugins.repository.AmetysObjectIterable;
+import org.ametys.plugins.repository.ModifiableAmetysObject;
+import org.ametys.plugins.repository.ModifiableTraversableAmetysObject;
+import org.ametys.plugins.repository.TraversableAmetysObject;
+import org.ametys.plugins.repository.jcr.JCRAmetysObject;
+import org.ametys.web.repository.site.SiteManager;
+
+/**
+ * Handle the JRC object sms-list
+ */
+public class SubscriberListsDAO implements Component, Serviceable
+{
+    /**
+     * The Role of the subscribetListsDAO
+     */
+    public static final String ROLE = SubscriberListsDAO.class.getName(); 
+    
+    /** The site manager */
+    protected SiteManager _siteManager;
+    
+    public void service(ServiceManager manager) throws ServiceException
+    {
+        _siteManager = (SiteManager) manager.lookup(SiteManager.ROLE);        
+    }
+    
+    /**
+     * Add a new sms list
+     * @param label
+     * @param desc
+     * @param sitename
+     * @param lang
+     * @return the id of the list
+     * @throws RepositoryException
+     */
+    public String add(String label, String desc, String sitename, String lang) throws RepositoryException
+    {
+        AmetysObject parentNode = _getRootNode(sitename, lang);
+        
+        String name = FilterNameHelper.filterName(label);
+        
+        String smsListName = name;
+        int index = 2;
+        while (((TraversableAmetysObject) parentNode).hasChild(smsListName))
+        {
+            smsListName = name + "-" + (index++);
+        }
+        
+        JCRSubscribersList subCategory = ((ModifiableTraversableAmetysObject) parentNode).createChild(smsListName, "ametys:smslist");
+        subCategory.setTitle(label);
+        subCategory.setDescription(desc);
+        
+        ((ModifiableAmetysObject) parentNode).saveChanges();
+        
+        return  subCategory.getId();
+    }
+    
+    /**
+     * Get all the sms list
+     * @param sitename
+     * @param lang
+     * @return A list of JCR object (sms-list)
+     * @throws RepositoryException
+     */
+    public AmetysObjectIterable<? extends JCRSubscribersList> getSubscribersLists(String sitename, String lang) throws RepositoryException
+    {
+        TraversableAmetysObject parentNode = _getRootNode(sitename, lang);
+        return (AmetysObjectIterable<? extends JCRSubscribersList>) parentNode.getChildren();
+    }
+    
+    private TraversableAmetysObject _getRootNode (String sitename, String lang) throws RepositoryException
+    {
+        ModifiableTraversableAmetysObject pluginsNode = _siteManager.getSite(sitename).getRootPlugins();
+        
+        ModifiableTraversableAmetysObject categoriesNode = null;
+        if (!pluginsNode.hasChild("sms"))
+        {
+            categoriesNode = ((ModifiableTraversableAmetysObject) pluginsNode.createChild("sms", "ametys:unstructured")).createChild("ametys:categories", "ametys:unstructured");
+        }
+        else
+        {
+            categoriesNode = pluginsNode.getChild("sms/ametys:categories");
+        }
+        
+        if (!categoriesNode.hasChild(lang))
+        {
+            categoriesNode.createChild(lang, "ametys:unstructured");
+            ((JCRAmetysObject) pluginsNode).getNode().getSession().save();
+        }
+        
+        return categoriesNode.getChild(lang);
+    }
+}
Index: main/plugin-sms/src/org/ametys/plugins/sms/dao/ListInfoGenerator.java
===================================================================
--- main/plugin-sms/src/org/ametys/plugins/sms/dao/ListInfoGenerator.java	(revision 0)
+++ main/plugin-sms/src/org/ametys/plugins/sms/dao/ListInfoGenerator.java	(revision 0)
@@ -0,0 +1,111 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.plugins.sms.dao;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.cocoon.ProcessingException;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.generation.ServiceableGenerator;
+import org.apache.cocoon.xml.AttributesImpl;
+import org.apache.cocoon.xml.XMLUtils;
+import org.xml.sax.SAXException;
+
+import org.ametys.plugins.repository.AmetysObjectResolver;
+import org.ametys.runtime.right.RightsManager;
+import org.ametys.runtime.user.CurrentUserProvider;
+
+
+/**
+ * Give informations from a id list
+ */
+public class ListInfoGenerator extends ServiceableGenerator
+{
+    /** The object resolver */
+    protected AmetysObjectResolver _resolver;
+    /** The rights manager */
+    protected RightsManager _rightsManager;
+    /** The user provider */
+    protected CurrentUserProvider _userProvider;
+    
+    @Override
+    public void service(ServiceManager serviceManager) throws ServiceException
+    {
+        super.service(serviceManager);
+        _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
+        _userProvider = (CurrentUserProvider) serviceManager.lookup(CurrentUserProvider.ROLE);
+        _rightsManager = (RightsManager) serviceManager.lookup(RightsManager.ROLE);
+    }
+    
+    public void generate() throws IOException, SAXException, ProcessingException
+    {     
+        Map<String, Object> jsParameters = (Map<String, Object>) objectModel.get(ObjectModelHelper.PARENT_CONTEXT);
+        List<String> listIDs = (List<String>) jsParameters.get("id");
+        
+        contentHandler.startDocument();
+        XMLUtils.startElement(contentHandler, "infos");
+        
+        XMLUtils.startElement(contentHandler, "lists");
+        for (String id : listIDs)
+        {
+            JCRSubscribersList list = _resolver.resolveById(id);
+            
+            AttributesImpl attr = new AttributesImpl();
+            attr.addCDATAAttribute("id", id);
+            attr.addCDATAAttribute("label", list.getTitle());
+            attr.addCDATAAttribute("desc", list.getDescription());
+            
+            XMLUtils.createElement(contentHandler, "list", attr);
+        }
+        
+        XMLUtils.endElement(contentHandler, "lists");  
+        saxUserRights();
+        
+        XMLUtils.endElement(contentHandler, "infos");  
+        
+        contentHandler.endDocument();
+    }
+    
+    /* SAX the user rights on content
+    * @param content The content to check rights
+    * @throws SAXException if an error occurred while SAXing
+    */
+    /**
+     * get the contributor right
+     * @throws SAXException
+     */
+    protected void saxUserRights() throws SAXException
+    {
+        String login = _userProvider.getUser();
+        Set<String> rights = _rightsManager.getUserRights(login, "/contributor");
+
+        XMLUtils.startElement(contentHandler, "user-rights");
+           
+        for (String rightID : rights)
+        {
+            AttributesImpl rightAttr = new AttributesImpl();
+            rightAttr.addCDATAAttribute("id", rightID);
+            XMLUtils.createElement(contentHandler, "right", rightAttr);
+        }
+        XMLUtils.endElement(contentHandler, "user-rights");
+    }
+    
+}
Index: main/plugin-sms/src/org/ametys/plugins/sms/dao/SubscribeDAO.java
===================================================================
--- main/plugin-sms/src/org/ametys/plugins/sms/dao/SubscribeDAO.java	(revision 0)
+++ main/plugin-sms/src/org/ametys/plugins/sms/dao/SubscribeDAO.java	(revision 0)
@@ -0,0 +1,270 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.plugins.sms.dao;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import java.util.HashMap;
+
+import org.apache.avalon.framework.component.Component;
+import org.apache.avalon.framework.logger.AbstractLogEnabled;
+import org.joda.time.DateTime;
+
+import org.ametys.runtime.datasource.ConnectionHelper;
+import org.ametys.runtime.util.parameter.ParameterHelper;
+
+/**
+ * Subcribe action SQL
+ */
+public class SubscribeDAO extends AbstractLogEnabled implements Component
+{
+    /**
+     * The subscribe DAO role
+     */
+    public static final String ROLE = SubscribeDAO.class.getName(); 
+
+    /**
+     * Check if the phoneNumber exist in the database
+     * @param phoneNumber
+     * @param list
+     * @return true if exist
+     */
+    public boolean numberAlreadyExists(String phoneNumber, String list)
+    {
+        Connection con = null;
+        PreparedStatement stmt = null;
+        ResultSet rs = null;
+        try
+        {
+            con = ConnectionHelper.getConnection(ConnectionHelper.CORE_POOL_NAME);    
+            
+            String sql = "SELECT * FROM SMS_Subscribers WHERE PhoneNumber = ? AND ListId = ?";
+            
+            stmt = con.prepareStatement(sql);
+            stmt.setString(1, phoneNumber);
+            stmt.setString(2, list);
+
+            rs = stmt.executeQuery();
+    
+            return rs.next(); 
+        }
+        catch (SQLException e)
+        {
+            getLogger().error("Error during the connection to the database", e);
+            return false;
+        }
+        finally
+        {
+            ConnectionHelper.cleanup(rs);
+            ConnectionHelper.cleanup(stmt);
+            ConnectionHelper.cleanup(con);
+        }
+    }
+
+    /**
+     * Insert the phoneNumber in the database
+     * @param phoneNumber
+     * @param list
+     */
+    public void insertNumber(String phoneNumber, String list)
+    {
+        Connection con = null;
+        PreparedStatement stmt = null;
+        ResultSet rs = null;
+        try
+        {
+            con = ConnectionHelper.getConnection(ConnectionHelper.CORE_POOL_NAME);    
+            
+            String sqlUpdate = "INSERT INTO SMS_Subscribers (PhoneNumber, ListId, date) VALUES (?, ?, ?)";
+           
+            stmt = con.prepareStatement(sqlUpdate);
+            stmt.setString(1, phoneNumber);
+            stmt.setString(2, list);
+            
+            DateTime dateToday = new DateTime(); 
+
+            Timestamp date = new Timestamp(dateToday.getMillis());
+            stmt.setTimestamp(3, date);
+            
+            stmt.execute();
+        }
+        catch (SQLException e)
+        {
+            getLogger().error("Error during the connection to the database", e);
+        }
+        finally
+        {
+            ConnectionHelper.cleanup(rs);
+            ConnectionHelper.cleanup(stmt);
+            ConnectionHelper.cleanup(con);
+        }
+    }
+    
+    /**
+     * Delete the phoneNumber in the database
+     * @param phoneNumber
+     * @param list
+     */
+    public void deleteNumber(String phoneNumber, String list)
+    {
+        Connection con = null;
+        PreparedStatement stmt = null;
+        ResultSet rs = null;
+        try
+        {
+            con = ConnectionHelper.getConnection(ConnectionHelper.CORE_POOL_NAME);    
+            
+            String sql = "DELETE FROM SMS_Subscribers WHERE PhoneNumber = ?"
+                        + (list != null ? " AND ListId = ?" : "");
+            
+            stmt = con.prepareStatement(sql);
+            stmt.setString(1, phoneNumber);
+            if (list != null)
+            {
+                stmt.setString(2, list);
+            }
+            
+            stmt.execute();
+        }
+        catch (SQLException e)
+        {
+            getLogger().error("Error during the connection to the database", e);
+        }
+        finally
+        {
+            ConnectionHelper.cleanup(rs);
+            ConnectionHelper.cleanup(stmt);
+            ConnectionHelper.cleanup(con);
+        }  
+    }
+    
+    /**
+     * Delete all phoneNumber from a list
+     * @param list
+     */
+    public void deleteAllNumber(String list)
+    {
+        Connection con = null;
+        PreparedStatement stmt = null;
+        ResultSet rs = null;
+        try
+        {
+            con = ConnectionHelper.getConnection(ConnectionHelper.CORE_POOL_NAME);    
+            
+            String sql = "DELETE FROM SMS_Subscribers  WHERE ListId = ? ";
+            
+            stmt = con.prepareStatement(sql);
+            stmt.setString(1, list);
+            
+            stmt.execute();
+        }
+        catch (SQLException e)
+        {
+            getLogger().error("Error during the connection to the database", e);
+        }
+        finally
+        {
+            ConnectionHelper.cleanup(rs);
+            ConnectionHelper.cleanup(stmt);
+            ConnectionHelper.cleanup(con);
+        }  
+    }
+    
+    /**
+     * Get all the phoneNumber in the database form the list
+     * @param list
+     * @return the phone number list
+     */
+    public HashMap<String, String> getPhoneNumbersFromList(String list)
+    {
+        Connection con = null;
+        PreparedStatement stmt = null;
+        ResultSet rs = null;
+        try
+        {
+            con = ConnectionHelper.getConnection(ConnectionHelper.CORE_POOL_NAME);    
+            
+            String sql = "SELECT PhoneNumber, Date FROM SMS_Subscribers WHERE ListId = ?";
+            
+            stmt = con.prepareStatement(sql);
+            stmt.setString(1, list);
+
+            rs = stmt.executeQuery();
+    
+            HashMap<String, String> listPhoneNumbers = new HashMap<String, String>();
+            while (rs.next())
+            {
+                listPhoneNumbers.put(rs.getString("PhoneNumber"), ParameterHelper.valueToString(rs.getDate("Date")));
+            }
+            
+            return listPhoneNumbers;
+        }
+        catch (SQLException e)
+        {
+            getLogger().error("Error during the connection to the database", e);
+            return null;
+        }
+        finally
+        {
+            ConnectionHelper.cleanup(rs);
+            ConnectionHelper.cleanup(stmt);
+            ConnectionHelper.cleanup(con);
+        }
+    }
+    
+    /**
+     * Get the number of user in a list
+     * @return the number of user for each sms list
+     */
+    public HashMap<String, Integer> getNbUserFromList()
+    {
+        Connection con = null;
+        PreparedStatement stmt = null;
+        ResultSet rs = null;
+        try
+        {
+            con = ConnectionHelper.getConnection(ConnectionHelper.CORE_POOL_NAME);    
+            
+            String sql = "SELECT s1.ListId, (SELECT count(*) FROM SMS_Subscribers s2 WHERE s1.ListId = s2.ListId) as nb FROM SMS_Subscribers s1";
+            
+            stmt = con.prepareStatement(sql);
+
+            rs = stmt.executeQuery();
+    
+            HashMap<String, Integer> smsListMap = new HashMap<String, Integer>();
+            while (rs.next())
+            {
+                smsListMap.put(rs.getString("ListId"), rs.getInt("nb"));
+            }
+            
+            return smsListMap;
+        }
+        catch (SQLException e)
+        {
+            getLogger().error("Error during the connection to the database", e);
+            return new HashMap<String, Integer>();
+        }
+        finally
+        {
+            ConnectionHelper.cleanup(rs);
+            ConnectionHelper.cleanup(stmt);
+            ConnectionHelper.cleanup(con);
+        }
+    }
+}
Index: main/plugin-sms/src/org/ametys/plugins/sms/dao/SubscribersListFactory.java
===================================================================
--- main/plugin-sms/src/org/ametys/plugins/sms/dao/SubscribersListFactory.java	(revision 0)
+++ main/plugin-sms/src/org/ametys/plugins/sms/dao/SubscribersListFactory.java	(revision 0)
@@ -0,0 +1,33 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.plugins.sms.dao;
+
+import javax.jcr.Node;
+
+import org.ametys.plugins.repository.AmetysRepositoryException;
+import org.ametys.plugins.repository.jcr.DefaultTraversableAmetysObjectFactory;
+
+/**
+ * The Factory of the SubscribersList
+ */
+public class SubscribersListFactory extends DefaultTraversableAmetysObjectFactory
+{
+    @Override
+    public JCRSubscribersList getAmetysObject(Node node, String parentPath) throws AmetysRepositoryException
+    {
+        return new JCRSubscribersList(node, parentPath, this);
+    }
+}
Index: main/plugin-sms/src/org/ametys/plugins/sms/dao/JCRSubscribersList.java
===================================================================
--- main/plugin-sms/src/org/ametys/plugins/sms/dao/JCRSubscribersList.java	(revision 0)
+++ main/plugin-sms/src/org/ametys/plugins/sms/dao/JCRSubscribersList.java	(revision 0)
@@ -0,0 +1,123 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.plugins.sms.dao;
+
+import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.RepositoryException;
+
+import org.ametys.plugins.repository.AmetysObject;
+import org.ametys.plugins.repository.AmetysRepositoryException;
+import org.ametys.plugins.repository.RepositoryConstants;
+import org.ametys.plugins.repository.jcr.DefaultTraversableAmetysObject;
+
+/**
+ * The JCR object : a sms list
+ */
+public class JCRSubscribersList extends DefaultTraversableAmetysObject<SubscribersListFactory>
+{
+    /** Constants for title metadata. */
+    private static final String __METADATA_TITLE = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":title";
+    /** Constants for description metadata. */
+    private static final String __METADATA_DESC = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":description";
+
+    /**
+    * constructor
+    * @param node the node backing this {@link AmetysObject}.
+    * @param parentPath the parent path in the Ametys hierarchy.
+    * @param factory the {@link SubscribersListFactory} which creates the AmetysObject.
+    */
+    public JCRSubscribersList (Node node, String parentPath, SubscribersListFactory factory)
+    {
+           super(node, parentPath, factory);
+    }
+    
+    /**
+     * Retrieves the title.
+     * @return the title.
+     * @throws AmetysRepositoryException if an error occurs.
+     */
+    public String getTitle() throws AmetysRepositoryException
+    {
+        try
+        {
+            return getNode().getProperty(__METADATA_TITLE).getString();
+        }
+        catch (PathNotFoundException e)
+        {
+            return null;
+        }
+        catch (RepositoryException e)
+        {
+            throw new AmetysRepositoryException("Unable to get title property", e);
+        }
+    }
+    
+    /**
+     * Set the title.
+     * @param title the title.
+     * @throws AmetysRepositoryException if an error occurs.
+     */
+    public void setTitle(String title) throws AmetysRepositoryException
+    {
+        try
+        {
+            getNode().setProperty(__METADATA_TITLE, title);
+        }
+        catch (RepositoryException e)
+        {
+            throw new AmetysRepositoryException("Unable to set title property", e);
+        }
+    }
+    
+    /**
+     * Retrieves the description.
+     * @return the description.
+     * @throws AmetysRepositoryException if an error occurs.
+     */
+    public String getDescription() throws AmetysRepositoryException
+    {
+        try
+        {
+            return getNode().getProperty(__METADATA_DESC).getString();
+        }
+        catch (PathNotFoundException e)
+        {
+            return null;
+        }
+        catch (RepositoryException e)
+        {
+            throw new AmetysRepositoryException("Unable to get description property", e);
+        }
+    }
+    
+    /**
+     * Set the description.
+     * @param description the description.
+     * @throws AmetysRepositoryException if an error occurs.
+     */
+    public void setDescription(String description) throws AmetysRepositoryException
+    {
+        try
+        {
+            getNode().setProperty(__METADATA_DESC, description);
+        }
+        catch (RepositoryException e)
+        {
+            throw new AmetysRepositoryException("Unable to set description property", e);
+        }
+    }
+}
Index: main/plugin-sms/src/org/ametys/plugins/sms/SubscribersListGenerator.java
===================================================================
--- main/plugin-sms/src/org/ametys/plugins/sms/SubscribersListGenerator.java	(revision 0)
+++ main/plugin-sms/src/org/ametys/plugins/sms/SubscribersListGenerator.java	(revision 0)
@@ -0,0 +1,100 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.plugins.sms;
+
+import java.io.IOException;
+import java.util.HashMap;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.cocoon.ProcessingException;
+import org.apache.cocoon.generation.ServiceableGenerator;
+import org.apache.cocoon.xml.AttributesImpl;
+import org.apache.cocoon.xml.XMLUtils;
+import org.xml.sax.SAXException;
+
+import org.ametys.plugins.repository.AmetysRepositoryException;
+import org.ametys.plugins.sms.dao.JCRSubscribersList;
+import org.ametys.plugins.sms.dao.SubscribeDAO;
+import org.ametys.plugins.sms.dao.SubscriberListsDAO;
+
+
+/**
+ * get all SMS list
+ */
+public class SubscribersListGenerator extends ServiceableGenerator
+{
+    /** Subscriber list DAO */
+    protected SubscriberListsDAO _subscriberListDAO;
+    
+    /** The subscribe DAO */
+    protected SubscribeDAO _subscribeDAO;
+    
+    @Override
+    public void service(ServiceManager serviceManager) throws ServiceException
+    {
+        super.service(serviceManager);
+        _subscriberListDAO = (SubscriberListsDAO) serviceManager.lookup(SubscriberListsDAO.ROLE);
+        _subscribeDAO = (SubscribeDAO) serviceManager.lookup(SubscribeDAO.ROLE);
+    }
+    
+    public void generate() throws IOException, SAXException, ProcessingException
+    {
+        String siteName = parameters.getParameter("site-name", "");
+        String lang = parameters.getParameter("lang", "");
+                
+        contentHandler.startDocument();
+        XMLUtils.startElement(contentHandler, "lists");
+        
+        HashMap<String, Integer> smsListMap = _subscribeDAO.getNbUserFromList();
+        try
+        {
+            for (JCRSubscribersList subscriberlist : _subscriberListDAO.getSubscribersLists(siteName, lang))
+            {
+                String subscriberListId = subscriberlist.getId();
+                
+                AttributesImpl attr = new AttributesImpl();
+                attr.addCDATAAttribute("id", subscriberListId);
+                
+                XMLUtils.startElement(contentHandler, "list", attr);
+                XMLUtils.createElement(contentHandler, "label", subscriberlist.getTitle());
+                if (smsListMap.containsKey(subscriberListId))
+                {
+                    XMLUtils.createElement(contentHandler, "nb-user", smsListMap.get(subscriberListId).toString());
+                }
+                else
+                {
+                    XMLUtils.createElement(contentHandler, "nb-user", "0");
+                }
+                XMLUtils.endElement(contentHandler, "list");
+            }
+        }
+        catch (AmetysRepositoryException e)
+        {
+            e.printStackTrace();
+        }
+        catch (RepositoryException e)
+        {
+            e.printStackTrace();
+        }
+        
+        XMLUtils.endElement(contentHandler, "lists");
+        contentHandler.endDocument();
+    }
+
+}
Index: main/plugin-sms/src/org/ametys/plugins/sms/action/AddOrEditListAction.java
===================================================================
--- main/plugin-sms/src/org/ametys/plugins/sms/action/AddOrEditListAction.java	(revision 0)
+++ main/plugin-sms/src/org/ametys/plugins/sms/action/AddOrEditListAction.java	(revision 0)
@@ -0,0 +1,116 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.plugins.sms.action;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.cocoon.acting.ServiceableAction;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Redirector;
+import org.apache.cocoon.environment.Request;
+import org.apache.cocoon.environment.SourceResolver;
+import org.apache.commons.lang.StringUtils;
+
+import org.ametys.plugins.repository.AmetysObjectResolver;
+import org.ametys.plugins.sms.dao.JCRSubscribersList;
+import org.ametys.plugins.sms.dao.SubscriberListsDAO;
+import org.ametys.runtime.right.RightsManager;
+import org.ametys.runtime.right.RightsManager.RightResult;
+import org.ametys.runtime.user.CurrentUserProvider;
+
+/**
+ * Edit a JCRSubscribersList
+ */
+public class AddOrEditListAction extends ServiceableAction
+{
+    /** Subscriber list DAO */
+    protected SubscriberListsDAO _subscriberListDAO;
+
+    /** The object resolver */
+    protected AmetysObjectResolver _resolver;
+    
+    /** The rights manager */
+    protected RightsManager _rightsManager;
+    
+    /** The user provider */
+    protected CurrentUserProvider _userProvider;
+    
+    @Override
+    public void service(ServiceManager serviceManager) throws ServiceException
+    {
+        super.service(serviceManager);
+        _subscriberListDAO = (SubscriberListsDAO) serviceManager.lookup(SubscriberListsDAO.ROLE);
+        _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
+        _rightsManager = (RightsManager) serviceManager.lookup(RightsManager.ROLE);
+        _userProvider = (CurrentUserProvider) serviceManager.lookup(CurrentUserProvider.ROLE);
+    }
+    
+    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
+    {
+        Map<String, String> result = new HashMap<String, String>();
+        
+        String login = _userProvider.getUser();
+        RightResult res = _rightsManager.hasRight(login, "Plugins_SMS_Right_Action", "/contributor");
+        if (res == RightResult.RIGHT_OK)
+        {
+        
+            Request request = ObjectModelHelper.getRequest(objectModel);        
+            String mode = request.getParameter("mode");
+            String title = request.getParameter("title");
+            String desc = request.getParameter("desc");
+            String lang = request.getParameter("lang");
+            
+            if (StringUtils.isBlank(title) || StringUtils.isBlank(lang))
+            {
+                throw new IllegalArgumentException("An error occurred : bad parameters");
+            }
+            
+            String sitename = (String) request.getAttribute("siteName");
+            
+            String idList = "";
+            if (mode.equals("add"))
+            {    
+                idList = _subscriberListDAO.add(title, desc, sitename, lang);
+            }
+            else if (mode.equals("edit"))
+            {
+                idList = request.getParameter("id");
+                JCRSubscribersList list = _resolver.resolveById(idList);
+                if (list == null)
+                {
+                    throw new IllegalAccessError("An error occurred : no sms list match with the id");
+                }
+                list.setTitle(title);
+                list.setDescription(desc);
+          
+                list.saveChanges();
+            }
+    
+            result.put("id", idList);
+            
+        }
+        else
+        {
+            throw new IllegalAccessError("An error occurred : you have no right on the sms list");
+        }
+        return result;
+    }
+
+}
Index: main/plugin-sms/src/org/ametys/plugins/sms/action/RightAddListClientSideElement.java
===================================================================
--- main/plugin-sms/src/org/ametys/plugins/sms/action/RightAddListClientSideElement.java	(revision 0)
+++ main/plugin-sms/src/org/ametys/plugins/sms/action/RightAddListClientSideElement.java	(revision 0)
@@ -0,0 +1,60 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.plugins.sms.action;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+
+import org.ametys.runtime.right.RightsManager;
+import org.ametys.runtime.right.RightsManager.RightResult;
+import org.ametys.runtime.ui.impl.StaticContextualClientSideElement;
+import org.ametys.runtime.user.CurrentUserProvider;
+import org.ametys.runtime.util.I18nizableText;
+
+/**
+ * Get the right to add sms list 
+ */
+public class RightAddListClientSideElement extends StaticContextualClientSideElement
+{
+    /** The user provider */
+    protected CurrentUserProvider _userProvider;
+    
+    @Override
+    public void service(ServiceManager serviceManager) throws ServiceException
+    {
+        super.service(serviceManager);
+        _userProvider = (CurrentUserProvider) serviceManager.lookup(CurrentUserProvider.ROLE);
+        _rightsManager = (RightsManager) serviceManager.lookup(RightsManager.ROLE);
+    }
+    
+    @Override
+    public Map<String, I18nizableText> getCurrentParameters(Map<String, Object> contextualParameters)
+    {
+        Map<String, I18nizableText> parameters = new HashMap<String, I18nizableText>(_initialParameters);
+        String login = _userProvider.getUser();
+        
+        RightResult result = _rightsManager.hasRight(login, "Plugins_SMS_Right_Action", "/contributor");
+        if (result == RightResult.RIGHT_OK)
+        {
+            parameters.put("sms-has-right-add", new I18nizableText("true"));
+        }
+        
+        return parameters;
+    }
+}
Index: main/plugin-sms/src/org/ametys/plugins/sms/action/DeleteListAction.java
===================================================================
--- main/plugin-sms/src/org/ametys/plugins/sms/action/DeleteListAction.java	(revision 0)
+++ main/plugin-sms/src/org/ametys/plugins/sms/action/DeleteListAction.java	(revision 0)
@@ -0,0 +1,100 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.plugins.sms.action;
+
+import java.util.Map;
+
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.cocoon.acting.ServiceableAction;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Redirector;
+import org.apache.cocoon.environment.Request;
+import org.apache.cocoon.environment.SourceResolver;
+
+import org.ametys.plugins.repository.AmetysObjectResolver;
+import org.ametys.plugins.repository.ModifiableAmetysObject;
+import org.ametys.plugins.sms.dao.JCRSubscribersList;
+import org.ametys.plugins.sms.dao.SubscribeDAO;
+import org.ametys.plugins.sms.dao.SubscriberListsDAO;
+import org.ametys.runtime.right.RightsManager;
+import org.ametys.runtime.right.RightsManager.RightResult;
+import org.ametys.runtime.user.CurrentUserProvider;
+
+/**
+ * Delete a JCRSubscribersList
+ */
+public class DeleteListAction extends ServiceableAction
+{
+    /** Subscriber list DAO */
+    protected SubscriberListsDAO _subscriberListDAO;
+    
+    /** The subscribe DAO */
+    protected SubscribeDAO _subscribeDAO;
+    
+    /** The object resolver */
+    protected AmetysObjectResolver _resolver;
+    
+    /** The rights manager */
+    protected RightsManager _rightsManager;
+    
+    /** The user provider */
+    protected CurrentUserProvider _userProvider;
+    
+    @Override
+    public void service(ServiceManager serviceManager) throws ServiceException
+    {
+        super.service(serviceManager);
+        _subscriberListDAO = (SubscriberListsDAO) serviceManager.lookup(SubscriberListsDAO.ROLE);
+        _subscribeDAO = (SubscribeDAO) serviceManager.lookup(SubscribeDAO.ROLE);
+        _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
+        _rightsManager = (RightsManager) serviceManager.lookup(RightsManager.ROLE);
+        _userProvider = (CurrentUserProvider) serviceManager.lookup(CurrentUserProvider.ROLE);
+    }
+    
+    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
+    {
+        String login = _userProvider.getUser();
+        RightResult res = _rightsManager.hasRight(login, "Plugins_SMS_Right_Action", "/contributor");
+        if (res == RightResult.RIGHT_OK)
+        {
+            Request request = ObjectModelHelper.getRequest(objectModel);        
+            String[] idLists = request.getParameterValues("ids");
+    
+            for (String idList : idLists)
+            {
+                JCRSubscribersList list = _resolver.resolveById(idList);
+                if (list == null)
+                {
+                    throw new IllegalAccessError("An error occurred : no sms list match with the id");
+                }
+                ModifiableAmetysObject parent = list.getParent();
+                list.remove();
+                parent.saveChanges();
+    
+                _subscribeDAO.deleteAllNumber(idList);
+            }
+        }
+        else
+        {
+            throw new IllegalAccessError("An error occurred : you have no right on the sms list");
+        }
+        
+        return EMPTY_MAP;
+    }
+
+}
Index: main/plugin-sms/src/org/ametys/plugins/sms/SendSMSAction.java
===================================================================
--- main/plugin-sms/src/org/ametys/plugins/sms/SendSMSAction.java	(revision 0)
+++ main/plugin-sms/src/org/ametys/plugins/sms/SendSMSAction.java	(revision 0)
@@ -0,0 +1,80 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.plugins.sms;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.cocoon.acting.ServiceableAction;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Redirector;
+import org.apache.cocoon.environment.Request;
+import org.apache.cocoon.environment.SourceResolver;
+
+import org.ametys.plugins.sms.broker.Broker;
+import org.ametys.plugins.sms.dao.SubscribeDAO;
+import org.ametys.runtime.right.RightsManager;
+import org.ametys.runtime.right.RightsManager.RightResult;
+import org.ametys.runtime.user.CurrentUserProvider;
+
+
+/**
+ * Send the SMS
+ */
+public class SendSMSAction extends ServiceableAction
+{
+    /** The rights manager */
+    protected RightsManager _rightsManager;
+    /** The user provider */
+    protected CurrentUserProvider _userProvider;
+    /** The sms broker */
+    protected Broker _broker;
+    /** The subscribe DAO */
+    protected SubscribeDAO _subscribeDAO;
+    
+    @Override
+    public void service(ServiceManager serviceManager) throws ServiceException
+    {
+        super.service(serviceManager);
+        _userProvider = (CurrentUserProvider) serviceManager.lookup(CurrentUserProvider.ROLE);
+        _rightsManager = (RightsManager) serviceManager.lookup(RightsManager.ROLE);
+        _broker = (Broker) serviceManager.lookup(Broker.ROLE);
+        _subscribeDAO = (SubscribeDAO) serviceManager.lookup(SubscribeDAO.ROLE);
+    }
+    
+    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
+    {
+        Request request = ObjectModelHelper.getRequest(objectModel);
+
+        String login = _userProvider.getUser();
+        String message = request.getParameter("message");
+        String phoneList = request.getParameter("phoneList");
+        
+        if (_rightsManager.hasRight(login, "Plugins_SMS_Right_Send", "/contributor") != RightResult.RIGHT_OK)
+        {
+            throw new IllegalAccessException("The user '" + login + "' tried to send a message to the list '" + phoneList + "' without the convenient right: " + message);
+        }
+        
+        Set<String> phoneNumberList = _subscribeDAO.getPhoneNumbersFromList(phoneList).keySet();
+        _broker.send(phoneNumberList, message);
+
+        return EMPTY_MAP;
+    }
+
+}
Index: main/plugin-sms/src/org/ametys/plugins/sms/subscriber/PhoneNumbersListGenerator.java
===================================================================
--- main/plugin-sms/src/org/ametys/plugins/sms/subscriber/PhoneNumbersListGenerator.java	(revision 0)
+++ main/plugin-sms/src/org/ametys/plugins/sms/subscriber/PhoneNumbersListGenerator.java	(revision 0)
@@ -0,0 +1,66 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.plugins.sms.subscriber;
+
+import java.io.IOException;
+import java.util.HashMap;
+
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.cocoon.ProcessingException;
+import org.apache.cocoon.generation.ServiceableGenerator;
+import org.apache.cocoon.xml.AttributesImpl;
+import org.apache.cocoon.xml.XMLUtils;
+import org.xml.sax.SAXException;
+
+import org.ametys.plugins.sms.dao.SubscribeDAO;
+
+/**
+ * Generate all the phone numbers from a sms list
+ */
+public class PhoneNumbersListGenerator extends ServiceableGenerator
+{
+    /** The subscribe DAO */
+    protected SubscribeDAO _subscribeDAO;
+    
+    @Override
+    public void service(ServiceManager serviceManager) throws ServiceException
+    {
+        super.service(serviceManager);
+        _subscribeDAO = (SubscribeDAO) serviceManager.lookup(SubscribeDAO.ROLE);
+    }
+    
+    public void generate() throws IOException, SAXException, ProcessingException
+    {
+        String idList = parameters.getParameter("id", "");
+        
+        contentHandler.startDocument();
+        XMLUtils.startElement(contentHandler, "phoneNumbers");
+        
+        HashMap<String, String> listNumber = _subscribeDAO.getPhoneNumbersFromList(idList);
+        for (String number : listNumber.keySet())
+        {
+            AttributesImpl attr = new AttributesImpl();
+            attr.addCDATAAttribute("value", number);
+            attr.addCDATAAttribute("date", listNumber.get(number));
+            XMLUtils.createElement(contentHandler, "phoneNumber", attr);
+        }
+        
+        XMLUtils.endElement(contentHandler, "phoneNumbers");
+        contentHandler.endDocument();
+    }
+
+}
Index: main/plugin-sms/src/org/ametys/plugins/sms/stop/UnsubscribeAction.java
===================================================================
--- main/plugin-sms/src/org/ametys/plugins/sms/stop/UnsubscribeAction.java	(revision 0)
+++ main/plugin-sms/src/org/ametys/plugins/sms/stop/UnsubscribeAction.java	(revision 0)
@@ -0,0 +1,68 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.plugins.sms.stop;
+
+import java.util.Map;
+
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.cocoon.acting.ServiceableAction;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Redirector;
+import org.apache.cocoon.environment.Request;
+import org.apache.cocoon.environment.SourceResolver;
+
+import org.ametys.plugins.sms.dao.SubscribeDAO;
+import org.ametys.plugins.sms.dao.SubscriberListsDAO;
+import org.ametys.plugins.sms.dao.JCRSubscribersList;
+
+/**
+ * Unsubscribe the sender of the a STOP request
+ */
+public class UnsubscribeAction extends ServiceableAction
+{
+    /** The subscribe DAO */
+    protected SubscribeDAO _subscribeDAO;
+
+    /** The subscriber List DAO */
+    protected SubscriberListsDAO _subscriberListDAO;
+
+    
+    @Override
+    public void service(ServiceManager serviceManager) throws ServiceException
+    {
+        super.service(serviceManager);
+        _subscribeDAO = (SubscribeDAO) serviceManager.lookup(SubscribeDAO.ROLE);
+        _subscriberListDAO = (SubscriberListsDAO) serviceManager.lookup(SubscriberListsDAO.ROLE);
+    }
+    
+    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
+    {
+        Request request = ObjectModelHelper.getRequest(objectModel);
+        String siteName = (String) request.getAttribute("site");
+        String lang = (String) request.getAttribute("lang");
+        
+        String phoneNumber = request.getParameter("number"); // TODO broker
+        
+        for (JCRSubscribersList list : _subscriberListDAO.getSubscribersLists(siteName, lang))
+        {
+            _subscribeDAO.deleteNumber(phoneNumber, list.getId());
+        }
+        
+        return EMPTY_MAP;
+    }
+}
Index: main/plugin-sms/src/org/ametys/plugins/sms/broker/Broker.java
===================================================================
--- main/plugin-sms/src/org/ametys/plugins/sms/broker/Broker.java	(revision 0)
+++ main/plugin-sms/src/org/ametys/plugins/sms/broker/Broker.java	(revision 0)
@@ -0,0 +1,40 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.plugins.sms.broker;
+
+import java.util.Set;
+
+/**
+ * The broker interface
+ */
+public interface Broker
+{
+    /** For avalon service manager */
+    public static final String ROLE = Broker.class.getName();
+    
+    /**
+     * Send the message from the sender to all the phones in the list
+     * @param phonesList
+     * @param message
+     */
+    public void send(Set<String> phonesList, String message);
+
+    /**
+     * When a stop request is arriving from broker, the implementation extract the phone number associated
+     * @return the phone number from the 'stop' sender
+     */
+    public String getPhoneNumberFromStopRequest();
+}
Index: main/plugin-sms/src/org/ametys/plugins/sms/broker/LoggerBroker.java
===================================================================
--- main/plugin-sms/src/org/ametys/plugins/sms/broker/LoggerBroker.java	(revision 0)
+++ main/plugin-sms/src/org/ametys/plugins/sms/broker/LoggerBroker.java	(revision 0)
@@ -0,0 +1,52 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.plugins.sms.broker;
+
+import java.util.Set;
+
+import org.apache.avalon.framework.logger.AbstractLogEnabled;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
+
+import org.ametys.runtime.user.CurrentUserProvider;
+
+/**
+ * Log informations (message, receivers and sender)
+ */
+public class LoggerBroker extends AbstractLogEnabled implements Broker, Serviceable
+{
+    private CurrentUserProvider _userProvider;
+
+    public void service(ServiceManager manager) throws ServiceException
+    {
+        _userProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE);        
+    }
+    
+    public void send(Set<String> phoneNumbers, String message)
+    {
+        if (getLogger().isInfoEnabled())
+        {
+            getLogger().info("The user '" + _userProvider.getUser() + "' send a sms message to " + phoneNumbers.toString() + ": " + message);
+        }
+    }
+
+    public String getPhoneNumberFromStopRequest()
+    {
+        throw new UnsupportedOperationException("The LoggerBroker does not support STOP request");
+    }
+
+}
Index: main/plugin-sms/src/org/ametys/plugins/sms/PhoneNumberFileGenerator.java
===================================================================
--- main/plugin-sms/src/org/ametys/plugins/sms/PhoneNumberFileGenerator.java	(revision 26839)
+++ main/plugin-sms/src/org/ametys/plugins/sms/PhoneNumberFileGenerator.java	(working copy)
@@ -1,86 +0,0 @@
-/*
- *  Copyright 2011 Anyware Services
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-package org.ametys.plugins.sms;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.cocoon.ProcessingException;
-import org.apache.cocoon.environment.ObjectModelHelper;
-import org.apache.cocoon.environment.Request;
-import org.apache.cocoon.generation.AbstractGenerator;
-import org.apache.cocoon.servlet.multipart.Part;
-import org.apache.cocoon.servlet.multipart.PartOnDisk;
-import org.apache.cocoon.servlet.multipart.RejectedPart;
-import org.apache.cocoon.xml.XMLUtils;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang.StringUtils;
-import org.xml.sax.SAXException;
-
-/**
- * Read the content of an uploaded file.
- */
-public class PhoneNumberFileGenerator extends AbstractGenerator
-{
-
-    @Override
-    public void generate() throws IOException, SAXException, ProcessingException
-    {
-        Request request = ObjectModelHelper.getRequest(objectModel);
-
-        contentHandler.startDocument();
-        
-        Part part = (Part) request.get("importFile");
-        
-        String filename = part.getFileName();
-        
-        if (part instanceof RejectedPart)
-        {
-            XMLUtils.createElement(contentHandler, "error", "file-rejected");
-        }
-        else if (!filename.endsWith(".txt") && !filename.endsWith(".csv"))
-        {
-            XMLUtils.createElement(contentHandler, "error", "not-text-file");
-        }
-        else
-        {
-            PartOnDisk uploadedFilePart = (PartOnDisk) part;
-            File uploadedFile = uploadedFilePart.getFile();
-            
-            List<String> lines = IOUtils.readLines(new FileInputStream(uploadedFile));
-            List<String> filteredLines = new ArrayList<String>(lines.size());
-            
-            for (String line : lines)
-            {
-                if (StringUtils.isNotBlank(line))
-                {
-                    filteredLines.add(line.replaceAll("[^+0-9]*", ""));
-//                    filteredLines.add(StringUtils.replaceChars(line, ",. \"'", ""));
-                }
-            }
-            
-            String content = StringUtils.join(filteredLines, ',');
-            
-            XMLUtils.createElement(contentHandler, "content", content);
-        }
-        
-        contentHandler.endDocument();
-    }
-
-}
Index: main/plugin-sms/src/org/ametys/plugins/sms/service/SubscribersListEnumerator.java
===================================================================
--- main/plugin-sms/src/org/ametys/plugins/sms/service/SubscribersListEnumerator.java	(revision 0)
+++ main/plugin-sms/src/org/ametys/plugins/sms/service/SubscribersListEnumerator.java	(revision 0)
@@ -0,0 +1,90 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.plugins.sms.service;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.avalon.framework.context.Context;
+import org.apache.avalon.framework.context.ContextException;
+import org.apache.avalon.framework.context.Contextualizable;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
+import org.apache.cocoon.components.ContextHelper;
+import org.apache.cocoon.environment.Request;
+
+import org.ametys.plugins.sms.dao.JCRSubscribersList;
+import org.ametys.plugins.sms.dao.SubscriberListsDAO;
+import org.ametys.runtime.util.I18nizableText;
+import org.ametys.runtime.util.parameter.Enumerator;
+
+/**
+ * Enumerate all the SubscribersList
+ */
+public class SubscribersListEnumerator implements Enumerator, Serviceable, Contextualizable
+{
+    /** Subscriber list DAO */
+    protected SubscriberListsDAO _subscriberListDAO;
+    private Context _context;
+    
+    public void service(ServiceManager manager) throws ServiceException
+    {
+        _subscriberListDAO = (SubscriberListsDAO) manager.lookup(SubscriberListsDAO.ROLE);
+    }
+    
+    public void contextualize(Context context) throws ContextException
+    {
+        _context = context;
+    }
+
+    public I18nizableText getEntry(String value) throws Exception
+    {
+        return getEntries().get(value);
+    }
+
+    public Map<Object, I18nizableText> getEntries() throws Exception
+    {
+        Request request = ContextHelper.getRequest(_context); 
+        String siteName = (String) request.getAttribute("siteName");  
+        String lang = "fr"; //(String) request.getAttribute("lang"); FIXME
+
+        Map<Object, I18nizableText> entries = new HashMap<Object, I18nizableText>();
+        
+        // we get all the subscribers list
+        try
+        {
+            I18nizableText label = new I18nizableText("plugin.sms", "PLUGINS_SMS_SUBSCRIBTION_SERVICE_REGISTER_TYPE_USER_CHOICE");
+            entries.put("-", label);
+            
+            for (JCRSubscribersList subList : _subscriberListDAO.getSubscribersLists(siteName, lang))
+            {    
+                label = new I18nizableText(subList.getTitle());
+                entries.put(subList.getId(), label);
+            }
+        }
+        catch (RepositoryException e)
+        {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+        
+        return entries;
+    }
+
+}
Index: main/plugin-sms/src/org/ametys/plugins/sms/service/SubscribeGenerator.java
===================================================================
--- main/plugin-sms/src/org/ametys/plugins/sms/service/SubscribeGenerator.java	(revision 0)
+++ main/plugin-sms/src/org/ametys/plugins/sms/service/SubscribeGenerator.java	(revision 0)
@@ -0,0 +1,123 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.plugins.sms.service;
+
+import java.io.IOException;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.cocoon.ProcessingException;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Request;
+import org.apache.cocoon.generation.ServiceableGenerator;
+import org.apache.cocoon.xml.AttributesImpl;
+import org.apache.cocoon.xml.XMLUtils;
+import org.apache.commons.lang.StringUtils;
+import org.xml.sax.SAXException;
+
+import org.ametys.plugins.sms.dao.JCRSubscribersList;
+import org.ametys.plugins.sms.dao.SubscriberListsDAO;
+import org.ametys.runtime.config.Config;
+import org.ametys.web.repository.page.ZoneItem;
+
+
+/**
+ * Generate the list register xml form.
+ */
+public class SubscribeGenerator extends ServiceableGenerator
+{
+    /** Subscriber list DAO */
+    protected SubscriberListsDAO _subscriberListDAO;
+    
+    @Override
+    public void service(ServiceManager serviceManager) throws ServiceException
+    {
+        super.service(serviceManager);
+        _subscriberListDAO = (SubscriberListsDAO) serviceManager.lookup(SubscriberListsDAO.ROLE);
+    }
+
+    @Override
+    public void generate() throws IOException, SAXException, ProcessingException
+    {
+        Request request = ObjectModelHelper.getRequest(objectModel);
+        
+        String siteName = (String) request.getAttribute("site");
+        String lang = "fr"; //(String) request.getAttribute("lang"); FIXME
+
+        ZoneItem zoneItem = (ZoneItem) request.getAttribute(ZoneItem.class.getName());
+        String zoneItemId = zoneItem.getId();
+        
+        String list = zoneItem.getServiceParameters().getString("subscribers-list", "");
+
+        contentHandler.startDocument();
+        XMLUtils.startElement(contentHandler, "form");
+        
+        // we put the zoneID to get all the service parameter
+        XMLUtils.startElement(contentHandler, "zone");
+        XMLUtils.createElement(contentHandler, "zone-id", zoneItemId);
+        XMLUtils.endElement(contentHandler, "zone");
+
+        // we get the number exemple in the configuration
+        String samples = Config.getInstance().getValueAsString("org.ametys.plugins.sms.broker.sample");         
+        String[] tabSamples = StringUtils.split(samples, "\n");
+        
+        XMLUtils.startElement(contentHandler, "samples");
+        for (int i = 0; i < tabSamples.length; i++)
+        {
+            XMLUtils.createElement(contentHandler, "sample", tabSamples[i]);
+        }
+        XMLUtils.endElement(contentHandler, "samples");
+
+        XMLUtils.startElement(contentHandler, "lists");
+
+        // If the admin lets the user choose
+        if (list.equals("-"))
+        {
+            // we get all the subscribers list
+            try
+            {
+                for (JCRSubscribersList subList : _subscriberListDAO.getSubscribersLists(siteName, lang))
+                {
+                    AttributesImpl attr = new AttributesImpl();
+                    attr.addCDATAAttribute("id", subList.getId());
+                    
+                    XMLUtils.createElement(contentHandler, "list", attr, subList.getTitle());
+                }
+            }
+            catch (RepositoryException e)
+            {
+                // TODO Auto-generated catch block
+                e.printStackTrace();
+            }
+            
+        }
+        else
+        {
+            // we put the chosen list
+            AttributesImpl attr = new AttributesImpl();
+            attr.addCDATAAttribute("id", list);
+            
+            XMLUtils.createElement(contentHandler, "list", attr, list);
+        }
+      
+        XMLUtils.endElement(contentHandler, "lists");        
+        XMLUtils.endElement(contentHandler, "form");
+        
+        contentHandler.endDocument();
+    }
+}
Index: main/plugin-sms/src/org/ametys/plugins/sms/service/SubscribeAction.java
===================================================================
--- main/plugin-sms/src/org/ametys/plugins/sms/service/SubscribeAction.java	(revision 0)
+++ main/plugin-sms/src/org/ametys/plugins/sms/service/SubscribeAction.java	(revision 0)
@@ -0,0 +1,243 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.plugins.sms.service;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.cocoon.acting.ServiceableAction;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Redirector;
+import org.apache.cocoon.environment.Request;
+import org.apache.cocoon.environment.SourceResolver;
+import org.apache.commons.lang.StringUtils;
+
+import org.ametys.plugins.repository.AmetysObjectResolver;
+import org.ametys.plugins.sms.dao.SubscribeDAO;
+import org.ametys.runtime.captcha.CaptchaHelper;
+import org.ametys.runtime.config.Config;
+import org.ametys.runtime.util.I18nizableText;
+import org.ametys.runtime.util.cocoon.ActionResultGenerator;
+import org.ametys.web.repository.page.ZoneItem;
+
+/**
+ * Add or remove the phone number into a list
+ */
+public class SubscribeAction extends ServiceableAction
+{  
+    /** The pattern to finally check international phone number */
+    protected static final Pattern PHONE_NUMBER_INTERNATIONAL_VALIDATOR = Pattern.compile("^\\+[0-9]{6,}$");
+    
+    /** Ametys object resolver. */
+    protected AmetysObjectResolver _resolver;
+    
+    /** Subscribe DAO. */
+    protected SubscribeDAO _subscribeDAO;
+    
+    @Override
+    public void service(ServiceManager serviceManager) throws ServiceException
+    {
+        super.service(serviceManager);
+        _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
+        _subscribeDAO = (SubscribeDAO) serviceManager.lookup(SubscribeDAO.ROLE);
+    }
+    
+    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
+    {
+        Map<String, Object> result = new HashMap<String, Object>();
+        List<I18nizableText> errors = new ArrayList<I18nizableText>();
+
+        Request request = ObjectModelHelper.getRequest(objectModel);
+        
+        String captchaKey = request.getParameter("captcha-key");
+        String captchaValue = request.getParameter("captcha-value");
+        String phoneNumber = request.getParameter("phone");
+        
+        String zoneId = request.getParameter("zone-id");
+        ZoneItem zone = _resolver.resolveById(zoneId);
+        
+        // Get the register type
+        String register = zone.getServiceParameters().getString("register-type", "user-choice");
+        // We check if the admin choose it
+        if (register.equals("user-choice"))
+        {
+            // If not we get the register type chosen by the user
+            register = request.getParameter("register");
+            if (register == null)
+            {
+                errors.add(new I18nizableText("plugin.sms", "PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_ERROR_REGISTER"));
+            }
+        }
+        
+        // Get the subscribers list
+        String list = zone.getServiceParameters().getString("subscribers-list", "-");
+        // We check if the admin choose it
+        if (list.equals("-"))
+        {
+            // If not we get the list chosen by the user
+            list = request.getParameter("list");
+            if (list == null)
+            {
+                errors.add(new I18nizableText("plugin.sms", "PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_ERROR_DIRECTORY"));
+            }
+        }
+        
+        // We check if the captcha is correct
+        if (captchaKey == null || captchaValue == null || !CaptchaHelper.checkAndInvalidate(captchaKey, captchaValue)) 
+        {
+            errors.add(new I18nizableText("plugin.sms", "PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_ERROR_CAPTCHA"));
+        }
+        
+        // We check if the phone number is correct
+        if (phoneNumber == null)
+        {
+            errors.add(new I18nizableText("plugin.sms", "PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_ERROR_PHONE"));
+        }
+        else
+        {
+            phoneNumber = phoneNumber.trim();           
+            phoneNumber = _transformPhoneNumber(phoneNumber);
+
+            if (!_checkPhoneNumber(phoneNumber))
+            {   
+                errors.add(new I18nizableText("plugin.sms", "PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_ERROR_PHONE"));
+            }
+            
+            if (!PHONE_NUMBER_INTERNATIONAL_VALIDATOR.matcher(phoneNumber).matches())
+            {
+                errors.add(new I18nizableText("plugin.sms", "PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_ERROR_PHONE2"));                
+                getLogger().error("The phone number entered by the user: " + request.getParameter("phone") + ", we tried to insert this number: " + phoneNumber + ". This is not match the internationnal format. Look the site configuration.");
+            }
+        }
+        
+        // If there are no errors
+        if (errors.isEmpty())
+        {            
+            _process(result, phoneNumber, register, list);
+        }
+        
+        result.put("error", errors);
+        
+        request.setAttribute(ActionResultGenerator.MAP_REQUEST_ATTR, result);
+        
+        return EMPTY_MAP;
+    }
+
+    private void _process(Map<String, Object> result, String phoneNumber, String register, String list)
+    {
+        if (_subscribeDAO.numberAlreadyExists(phoneNumber, list))
+        {
+            if (register.equals("subscribe"))
+            {
+                result.put("messageError", new I18nizableText("plugin.sms", "PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_MESSAGE_ALREADY_RECORD"));
+            }
+            else
+            {
+                _subscribeDAO.deleteNumber(phoneNumber, list);
+                result.put("messageDelete", new I18nizableText("plugin.sms", "PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_MESSAGE_DELETE"));
+            }
+        }
+        else
+        {
+            if (register.equals("subscribe"))
+            {
+                _subscribeDAO.insertNumber(phoneNumber, list);
+                result.put("messageRecord", new I18nizableText("plugin.sms", "PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_MESSAGE_ADD"));
+            }
+            else
+            {
+                result.put("messageError", new I18nizableText("plugin.sms", "PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_MESSAGE_NOT_RECORD"));                    
+            }
+        }
+    }
+
+    // Transform the phoneNumber to the internationnal format defined by the admin
+    private String _transformPhoneNumber(String phoneNumber)
+    {
+        String regexConvert = Config.getInstance().getValueAsString("org.ametys.plugins.sms.broker.convert");
+        String[] tabRegexConvert = StringUtils.split(regexConvert, "\n");
+        String newPhoneNumber = phoneNumber;
+
+        for (int i = 0; i < tabRegexConvert.length; i++)
+        {
+            String[] regexParams = StringUtils.split(tabRegexConvert[i], "/");
+            String regex = regexParams[0];
+            String replaceExpr = regexParams[1];
+            
+            // ensure regexp takes the whole expression
+            if (!regex.startsWith("^"))
+            {
+                regex = "^" + regex;
+            }
+            if (!regex.endsWith("$"))
+            {
+                regex = regex + "$";
+            }
+             
+            // We transform the phone number to an internationnal phone number
+            Matcher m = Pattern.compile(regex).matcher(phoneNumber);
+            if (m.matches())
+            {
+                newPhoneNumber = replaceExpr;
+                
+                for (int c = 1; c <= m.groupCount(); c++)
+                {
+                    newPhoneNumber = newPhoneNumber.replaceAll("\\{" + c + "\\}", m.group(c));
+                }
+                break;
+            }
+        }
+        
+        return newPhoneNumber;
+    }
+    
+    // Check if the phoneNumber is in the internationnal format defined by the admin
+    private boolean _checkPhoneNumber(String phoneNumber)
+    {
+        String regexPhone = Config.getInstance().getValueAsString("org.ametys.plugins.sms.broker.autorized");         
+        String[] tabRegexPhone = StringUtils.split(regexPhone, "\n");
+        boolean matched = false;
+
+        for (String regex : tabRegexPhone)
+        {
+            // ensure regexp takes the whole expression
+            if (!regex.startsWith("^"))
+            {
+                regex = "^" + regex;
+            }
+            if (!regex.endsWith("$"))
+            {
+                regex = regex + "$";
+            }
+            
+            // We check if the phoneNumber format is correct
+            if (Pattern.compile(regex).matcher(phoneNumber).matches())
+            {
+                matched = true;
+                break;
+            }
+        }
+        
+        return matched;
+    }
+}
Index: main/plugin-sms/pages/services/subscribe/result/result.xsl
===================================================================
--- main/plugin-sms/pages/services/subscribe/result/result.xsl	(revision 0)
+++ main/plugin-sms/pages/services/subscribe/result/result.xsl	(revision 0)
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   Copyright 2014 Anyware Services
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+   -->
+<xsl:stylesheet version="1.0" 
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
+	xmlns:i18n="http://apache.org/cocoon/i18n/2.1">
+	
+	<xsl:template match="/ActionResult">
+		<html>
+			<head/>
+			
+			<body id="default">
+				<xsl:choose>
+					<xsl:when test="error">
+						<div class="form-errors">
+							<h1><i18n:text i18n:key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_ERROR_TITLE" i18n:catalogue="plugin.sms"/></h1>
+							<p><i18n:text i18n:key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_ERROR_DESCRIPTION" i18n:catalogue="plugin.sms"/></p>
+							<ul class="field-errors">
+								<xsl:for-each select="error">
+									<li><xsl:value-of select="."/></li>
+								</xsl:for-each>
+							</ul>
+							<noscript>
+								<p><i18n:text i18n:key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_BACK_ALT" i18n:catalogue="plugin.sms"/></p>
+							</noscript>
+							<script type="text/javascript">
+								document.write("&lt;p&gt;&lt;a href='javascript: history.back();'&gt;<i18n:text i18n:key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_BACK" i18n:catalogue="plugin.sms"/>&lt;/a&gt;&lt;/p&gt;")
+							</script>
+						</div>
+					</xsl:when>
+					<xsl:when test="messageDelete">
+						<div class="form-message">
+							<h1><i18n:text i18n:key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_MESSAGE_DELETED_TITLE" i18n:catalogue="plugin.sms"/></h1>
+							<p><xsl:value-of select="messageDelete"/></p>
+							<noscript>
+								<p><i18n:text i18n:key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_BACK_ALT" i18n:catalogue="plugin.sms"/></p>
+							</noscript>
+							<script type="text/javascript">
+								document.write("&lt;p&gt;&lt;a href='javascript: history.back();'&gt;<i18n:text i18n:key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_BACK" i18n:catalogue="plugin.sms"/>&lt;/a&gt;&lt;/p&gt;")
+							</script>
+						</div>
+					</xsl:when>
+					<xsl:when test="messageRecord">
+						<div class="form-message">		
+							<h1><i18n:text i18n:key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_MESSAGE_RECORD_TITLE" i18n:catalogue="plugin.sms"/></h1>
+							<p><xsl:value-of select="messageRecord"/></p>
+							<noscript>
+								<p><i18n:text i18n:key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_BACK_ALT" i18n:catalogue="plugin.sms"/></p>
+							</noscript>
+							<script type="text/javascript">
+								document.write("&lt;p&gt;&lt;a href='javascript: history.back();'&gt;<i18n:text i18n:key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_BACK" i18n:catalogue="plugin.sms"/>&lt;/a&gt;&lt;/p&gt;")
+							</script>
+						</div>
+					</xsl:when>
+					<xsl:otherwise>
+						<div class="form-message">	
+							<h1><i18n:text i18n:key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_MESSAGE_ISSUE_TITLE" i18n:catalogue="plugin.sms"/></h1>
+							<p><xsl:value-of select="messageError"/></p>
+							<noscript>
+								<p><i18n:text i18n:key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_BACK_ALT" i18n:catalogue="plugin.sms"/></p>
+							</noscript>
+							<script type="text/javascript">
+								document.write("&lt;p&gt;&lt;a href='javascript: history.back();'&gt;<i18n:text i18n:key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_BACK" i18n:catalogue="plugin.sms"/>&lt;/a&gt;&lt;/p&gt;")
+							</script>
+						</div>							
+					</xsl:otherwise>
+				</xsl:choose>
+			</body>
+		</html>
+	</xsl:template>
+	
+</xsl:stylesheet>
\ No newline at end of file
Index: main/plugin-sms/pages/services/subscribe/subscribe_1.2.xml
===================================================================
--- main/plugin-sms/pages/services/subscribe/subscribe_1.2.xml	(revision 0)
+++ main/plugin-sms/pages/services/subscribe/subscribe_1.2.xml	(revision 0)
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   Copyright 2010 Anyware Services
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+   -->
+<service>
+	<label i18n="true">PLUGINS_SMS_SUBSCRIBTION_SERVICE_LABEL</label>
+</service>
\ No newline at end of file
Index: main/plugin-sms/pages/services/subscribe/subscribe_1.2.xsl
===================================================================
--- main/plugin-sms/pages/services/subscribe/subscribe_1.2.xsl	(revision 0)
+++ main/plugin-sms/pages/services/subscribe/subscribe_1.2.xsl	(revision 0)
@@ -0,0 +1,402 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   Copyright 2010 Anyware Services
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+   -->
+<xsl:stylesheet version="1.0" 
+				xmlns:i18n="http://apache.org/cocoon/i18n/2.1"
+				xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+				xmlns:math="http://exslt.org/math" 
+				xmlns:ametys="org.ametys.web.transformation.xslt.AmetysXSLTHelper"
+				extension-element-prefixes="math ametys">
+				
+	<xsl:import href="service:web://pages/services/common.xsl"/>
+	<xsl:import href="plugin:web://stylesheets/helper/common_3.3.xsl"/>
+	
+	<xsl:variable name="uniqueId" select="concat('smsList_', substring-after(math:random(), '.'))"/>	
+	
+    <xsl:variable name="header" select="ametys:serviceParameter('title')"/>
+	<xsl:variable name="registerType" select="ametys:serviceParameter('register-type')"/>
+	
+	<xsl:template name="smsList-get-url">
+        <xsl:value-of select="concat(ametys:siteUriPrefix(), '/', ametys:lang(), '/_plugins/sms/page/service/do-subscribe')"/>
+    </xsl:template>
+	<!-- +
+         | BODY
+         + -->    
+    <!-- Defined in the service because of the id  -->
+    <xsl:template name="common-service-body-nonempty">
+        <div class="ametys-cms-service wrapper">
+            <xsl:call-template name="common-service-body-nonempty-content-title"/>
+            
+            <xsl:call-template name="common-service-body-nonempty-content-content"/>            
+        </div>
+    </xsl:template>           
+         
+    <!-- Used in common.xsl --> 
+    <xsl:template name="common-service-body-nonempty-content-title">
+       	<xsl:call-template name="common-service-body-nonempty-content-title-content">
+       		<xsl:with-param name="title" select="$header"/>
+       	</xsl:call-template>  
+    </xsl:template>         
+
+    <!-- Used in common.xsl --> 
+    <xsl:template name="common-service-body-nonempty-content-content">
+   		
+   		<xsl:variable name="smsList-url"><xsl:call-template name="smsList-get-url"/></xsl:variable>
+		<div>
+	   		<form method="post" action="{$smsList-url}">
+	   			<div>
+	   				<p>
+	   					<xsl:choose>
+	   						<xsl:when test="count(/form/lists/list) = 0">
+		   						<i18n:translate>
+									<i18n:text i18n:key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_NO_LIST" i18n:catalogue="plugin.sms"/>
+								</i18n:translate>
+	   						</xsl:when>
+		   					<xsl:when test="count(/form/lists/list) = 1">
+			   					<xsl:choose>
+				   					<xsl:when test="$registerType = 'user-choice'">
+					   					<i18n:translate>
+											<i18n:text i18n:key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_INFO" i18n:catalogue="plugin.sms"/>
+											<i18n:param><xsl:value-of select="/form/lists/list"/></i18n:param>
+										</i18n:translate>
+									</xsl:when>
+									<xsl:when test="$registerType = 'subscribe'">
+										<i18n:translate>
+											<i18n:text i18n:key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_SUBSCRIBE_INFO" i18n:catalogue="plugin.sms"/>
+											<i18n:param><xsl:value-of select="/form/lists/list"/></i18n:param>
+										</i18n:translate>
+									</xsl:when>
+									<xsl:when test="$registerType = 'unsubscribe'">
+										<i18n:translate>
+											<i18n:text i18n:key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_UNSUBSCRIBE_INFO" i18n:catalogue="plugin.sms"/>
+											<i18n:param><xsl:value-of select="/form/lists/list"/></i18n:param>
+										</i18n:translate>
+									</xsl:when>
+								</xsl:choose>
+								<br/>
+		   					</xsl:when>
+		   					<xsl:otherwise>
+			   					<xsl:choose>
+									<xsl:when test="$registerType = 'user-choice'">
+				   						<i18n:translate>
+											<i18n:text i18n:key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_INFO_LIST" i18n:catalogue="plugin.sms"/>
+										</i18n:translate>
+									</xsl:when>
+									<xsl:when test="$registerType = 'subscribe'">
+					   					<i18n:translate>
+											<i18n:text i18n:key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_SUBSCRIBE_INFO_LIST" i18n:catalogue="plugin.sms"/>
+										</i18n:translate>
+									</xsl:when>
+									<xsl:when test="$registerType = 'unsubscribe'">
+					   					<i18n:translate>
+											<i18n:text i18n:key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_UNSUBSCRIBE_INFO_LIST" i18n:catalogue="plugin.sms"/>
+										</i18n:translate>
+									</xsl:when>
+								</xsl:choose>
+								<br/>
+		   					</xsl:otherwise>
+						</xsl:choose>
+						<xsl:if test="count(/form/lists/list) > 0">
+							<i18n:text i18n:key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_MANDATORY_INFO" i18n:catalogue="plugin.sms"/>
+	   					</xsl:if>
+	   				</p>
+	   				<br/>
+	   			</div>  		
+	   			   		
+				<xsl:if test="count(/form/lists/list) > 0">	   		
+		   			<xsl:call-template name="smsList-form-field-lists"/> 
+			   		<xsl:call-template name="smsList-form-field-phone"/> 
+					<xsl:call-template name="smsList-form-field-register"/> 	   			
+					<xsl:call-template name="smsList-form-field-captcha"/>
+					<xsl:call-template name="smsList-form-field-submit"/>
+				</xsl:if>
+			</form>
+			
+            <xsl:call-template name="smsList-form-script"/> 
+		</div>
+    </xsl:template>
+	
+	<xsl:template name="smsList-form-script">
+        <xsl:variable name="uri-prefix">
+            <xsl:call-template name="get-uri-prefix"/>
+        </xsl:variable>
+        
+            <script type="text/javascript">           
+                function smsList_form_submit_<xsl:value-of select="$uniqueId"/>()
+                {                    
+                    var phone =  $j('#smsList-<xsl:value-of select="$uniqueId"/>-phone');
+                    var captchaKey = $j('#smsList-<xsl:value-of select="$uniqueId"/>-captcha-key');
+                    var captchaInput = $j('#smsList-<xsl:value-of select="$uniqueId"/>-captcha');
+                    var zoneId = $j('#smsList-<xsl:value-of select="$uniqueId"/>-zone-id');
+                    var list = $j('#smsList-<xsl:value-of select="$uniqueId"/>-list');
+                    var register = $j('#smsList-<xsl:value-of select="$uniqueId"/>-register');
+                    
+                    var errors = false;
+                    
+                    if (/^\s*$/.test(phone.val()))
+                    {
+                        errors = true;
+                        phone.parentsUntil('.field').parent().addClass('error');
+                    }
+                    else
+                    {
+                        phone.parentsUntil('.field').parent().removeClass('error');
+                    }
+                    
+                    if (register.val() == "")
+                    {
+                        errors = true;
+                        register.parentsUntil('.field').parent().addClass('error');
+                    }
+                    else
+                    {
+                        register.parentsUntil('.field').parent().removeClass('error');
+                    }
+                    
+                    if (list.val() == "")
+                    {
+                        errors = true;
+                        list.parentsUntil('.field').parent().addClass('error');
+                    }
+                    else
+                    {
+                        list.parentsUntil('.field').parent().removeClass('error');
+                    }
+                    
+                    if (!/^[a-z]{6}$/.test(captchaInput.val()))
+                    {
+                        errors = true;
+                        captchaInput.parentsUntil('.field').parent().addClass('error');
+                    }
+                    else
+                    {
+                        captchaInput.parentsUntil('.field').parent().removeClass('error');
+                    }
+
+                    if (!errors)
+                    {
+                        // send it in ajax
+                        var data = {
+                            "phone": phone.val(),
+                            "captcha-key": captchaKey.val(),
+                            "captcha-value": captchaInput.val(),
+                            "zone-id": zoneId.val(),
+                            "list": list.val(),
+                            "register": register.val(),
+                            "ajax": "true"
+                        };
+
+						function result(data)
+                        {
+                            var s = "";
+                            $j('errors error', data).each(function () { s += $j(this).text() + "\n"; });
+                            if (s == "")
+                            {
+                                var errors = $j('error', data);
+                                if (errors.size() == 0)
+                                {
+                                    var messageError = $j('messageError', data);
+                                    if (messageError.size() == 0)
+                                    {               
+                                        var messageDeleteInfo = $j('messageDelete', data);
+                                        var messageAddInfo = $j('messageRecord', data); 
+                                        if (messageDeleteInfo.size() != 0)
+                                        {
+                                            alert($j(messageDeleteInfo[0]).text());
+                                        }
+                                        else
+                                        {
+                                            alert($j(messageAddInfo[0]).text());
+                                        }
+                                        
+                                    }
+                                    else
+                                    {                                        
+                                        alert($j(messageError[0]).text());
+                                    }
+                                }
+                                else
+                                {
+                                    var errorInfo = "";
+                                    $j.each(errors, function(i) {
+                                        errorInfo += "\n* " + $j(errors[i]).text();
+                                    }); 
+                                    alert("<i18n:text i18n:key="PLUGINS_SOCIAL_NETWORKING_SEND_MAIL_ERROR_DESCRIPTION"  i18n:catalogue="plugin.socialnetworking"/>" + errorInfo)
+                                }
+                            }
+                            else
+                            {
+                                alert(s);
+                            }
+                            refresh_smsList_<xsl:value-of select="$uniqueId"/>();
+                        }
+                        
+                        function handleError(jqXHR, textStatus, errorThrown)
+                        {
+                            alert("<i18n:text i18n:key="PLUGINS_SOCIAL_NETWORKING_SEND_MAIL_ERROR_AJAX" i18n:catalogue="plugin.socialnetworking"/>");
+                        }
+
+                        $j.ajax({
+                            url: "<xsl:value-of select="$uri-prefix"/>/plugins/sms/service/do-subscribe",
+                            data: data,
+                            type: 'post',
+                            success: result,
+                            error: handleError
+                        });
+                    }
+
+                    return false;
+                }
+            </script>
+    </xsl:template> 
+	
+	<xsl:template name="smsList-form-field-phone">
+		<div class="field">
+			<div class="label">
+				<label for="smsList-{$uniqueId}-phone">
+					<i18n:text i18n:catalogue="plugin.sms" i18n:key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_PHONE"/>
+					<xsl:call-template name="smsList-form-mandatorymarker" />
+					<br/>
+					<xsl:text/>(<i18n:text i18n:catalogue="plugin.sms" i18n:key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_PHONE_INFO_1"/>
+					<xsl:variable name="lenght" select="count(/form/samples/sample)"/>
+					<xsl:for-each select="/form/samples/sample">
+					    <xsl:variable name="i" select="position()"/>
+					    <xsl:choose>
+   					    	<xsl:when test="$i = 1">
+							</xsl:when>	    
+					    	<xsl:when test="$i != $lenght">
+					    		<xsl:text/><i18n:text i18n:catalogue="plugin.sms" i18n:key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_PHONE_INFO_2"/>
+					    	</xsl:when>
+					    	<xsl:otherwise>
+					    		<xsl:text/><i18n:text i18n:catalogue="plugin.sms" i18n:key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_PHONE_INFO_3"/>
+					    	</xsl:otherwise>
+					    </xsl:choose>
+						<xsl:value-of select="."></xsl:value-of>
+					</xsl:for-each>
+					<xsl:text>)</xsl:text>
+				</label>
+			</div>
+			<div class="input">
+				<input type="text" name="phone" id="smsList-{$uniqueId}-phone"/>
+			</div>
+			<div class="field-end"></div>
+		</div>
+	</xsl:template>
+	
+	<xsl:template name="smsList-form-field-register">	
+		<xsl:if test="$registerType = 'user-choice'">
+			<div class="field">
+				<div class="label">
+					<label for="smsList-{$uniqueId}-register">
+						<i18n:text i18n:catalogue="plugin.sms" i18n:key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_REGISTER_TYPE_LABEL"/>
+						<xsl:call-template name="smsList-form-mandatorymarker" />
+					</label>
+				</div>
+				<div class="select">
+					<select id="smsList-{$uniqueId}-register" name="register">
+						<option value="subscribe">
+							<i18n:text i18n:catalogue="plugin.sms" i18n:key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_SUBSCRIBE"/>
+						</option>
+						<option value="unsubscribe">
+							<i18n:text i18n:catalogue="plugin.sms" i18n:key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_UNSUBSCRIBE"/>
+						</option>
+					</select>
+				</div>
+				<div class="field-end"></div>
+			</div>
+		</xsl:if>
+	</xsl:template>
+	
+	<xsl:template name="smsList-form-zone-id">
+		<xsl:value-of select="/form/zone/zone-id"/>
+	</xsl:template>
+	
+	<xsl:template name="smsList-form-field-lists">
+		<xsl:choose>
+		<xsl:when test="count(/form/lists/list) > 1">
+			<div class="field">
+				<div class="label">
+					<label for="smsList-{$uniqueId}-list">
+						<i18n:text i18n:catalogue="plugin.sms" i18n:key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_LIST"/>
+						<xsl:call-template name="smsList-form-mandatorymarker" />
+					</label>
+				</div>
+				<div class="select">
+					<select id="smsList-{$uniqueId}-list" name="list">
+						<option selected="true" value=""><i18n:text i18n:catalogue="plugin.sms" i18n:key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_LIST_CHOOSE"/></option>
+						<xsl:for-each select="/form/lists/list">
+							<xsl:sort select="."/>
+							<xsl:variable name="value" select="@id"/>
+							<option value="{$value}">
+								<xsl:value-of select="."/>
+							</option>
+						</xsl:for-each>
+					</select>
+				</div>
+				<div class="field-end"></div>
+			</div>
+		</xsl:when>
+		</xsl:choose>
+		<xsl:variable name="zone-id"><xsl:call-template name="smsList-form-zone-id"/></xsl:variable>
+		<div>
+  			<input type="hidden" name="zone-id" value="{$zone-id}" id="smsList-{$uniqueId}-zone-id"/>
+		</div>
+	</xsl:template>
+	
+	<xsl:template name="smsList-form-field-captcha">
+		<div class="field">
+			<div class="label">
+				<label for="smsList-{$uniqueId}-captcha">
+					<i18n:text i18n:catalogue="plugin.sms" i18n:key="PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_CAPTCHA"/>
+					<xsl:call-template name="smsList-form-mandatorymarker" />			
+				</label>
+			</div>
+			<div class="input">
+				<xsl:call-template name="captcha">
+					<xsl:with-param name="key-name">captcha-key</xsl:with-param>
+					<xsl:with-param name="key-id">smsList-<xsl:value-of select="$uniqueId"/>-captcha-key</xsl:with-param>
+
+					<xsl:with-param name="value-name">captcha-value</xsl:with-param>
+					<xsl:with-param name="value-id">smsList-<xsl:value-of select="$uniqueId"/>-captcha</xsl:with-param>
+					<xsl:with-param name="value-style"></xsl:with-param>
+					<xsl:with-param name="value-class"/>
+
+					<xsl:with-param name="image-style"></xsl:with-param>
+					<xsl:with-param name="image-class"/>
+					
+					<xsl:with-param name="js-funcname-torefresh">refresh_smsList_<xsl:value-of select="$uniqueId"/></xsl:with-param>
+				</xsl:call-template> 
+			</div>
+			<div class="field-end"></div>
+		</div>
+	</xsl:template>	
+	
+	<xsl:template name="smsList-form-field-submit">
+         <!-- Template for submit button -->
+         <div id="smsList-submit-{$uniqueId}">
+	         <xsl:call-template name="common-utils-input-submit">
+	             <xsl:with-param name="i18nkey" select="'plugin.sms:PLUGINS_SMS_SUBSCRIBTION_SERVICE_FORM_SUBMIT'" />                
+	             <xsl:with-param name="onclick">return smsList_form_submit_<xsl:value-of select="$uniqueId"/>();</xsl:with-param>
+             </xsl:call-template>
+         </div>
+	</xsl:template>	
+	
+	<xsl:template name="smsList-form-mandatorymarker">
+		<span class="mandatory-marker">
+			<i18n:text i18n:key="PLUGINS_WEB_FRONTOFICE_MANDATORY_FIELD" i18n:catalogue="plugin.web"/>
+		</span>
+	</xsl:template>	
+</xsl:stylesheet>
Index: main/plugin-sms/nodetypes/sms_nodetypes.xml
===================================================================
--- main/plugin-sms/nodetypes/sms_nodetypes.xml	(revision 0)
+++ main/plugin-sms/nodetypes/sms_nodetypes.xml	(revision 0)
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   Copyright 2014 Anyware Services
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+   -->
+<nodeTypes xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
+           xmlns:jcr="http://www.jcp.org/jcr/1.0"
+           xmlns:mix="http://www.jcp.org/jcr/mix/1.0"
+           xmlns:ametys="http://www.ametys.org/jcr/repository/1.0"
+           xmlns:ametys-internal="http://www.ametys.org/jcr/repository/internal/1.0">
+    <nodeType name="ametys:smslist" isMixin="false" hasOrderableChildNodes="false" primaryItemName="">
+        <supertypes>
+            <supertype>nt:base</supertype>
+            <supertype>ametys:object</supertype>
+        </supertypes>
+        <propertyDefinition name="ametys-internal:title" requiredType="String" autoCreated="false" mandatory="true" onParentVersion="COPY" protected="false" multiple="false"/>
+        <propertyDefinition name="ametys-internal:description" requiredType="String" autoCreated="false" mandatory="false" onParentVersion="COPY" protected="false" multiple="false"/>
+    </nodeType>
+</nodeTypes>
Index: main/plugin-sms/cms-ribbon.xml
===================================================================
--- main/plugin-sms/cms-ribbon.xml	(revision 0)
+++ main/plugin-sms/cms-ribbon.xml	(revision 0)
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   Copyright 2014 Anyware Services
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+   -->
+<ribbon  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+          xsi:noNamespaceSchemaLocation="http://ametys.org/cms/3.0/ribbon.xsd">
+
+	<tabs>
+		<tab label="plugin.sms:RIBBON_TABS_TAB_SMS_LIST_LABEL" id="org.ametys.plugins.sms.listTab" contextualColor="6" contextualGroup="N" contextualLabel="plugin.sms:RIBBON_TABS_TAB_SMS_LABEL">
+			<groups>
+				<group label="plugin.sms:RIBBON_TABS_TAB_SMS_GROUP_LIST_LABEL" icon="">
+					<large>
+						<control id="org.ametys.plugins.sms.list.Add"/>
+						<control id="org.ametys.plugins.sms.list.Edit"/>
+						<control id="org.ametys.plugins.sms.list.Delete"/>
+					</large>
+					<medium>
+						<control id="org.ametys.plugins.sms.list.Add"/>
+						<layout size="small">
+							<control id="org.ametys.plugins.sms.list.Edit"/>
+							<control id="org.ametys.plugins.sms.list.Delete"/>
+						</layout>
+					</medium>
+					<small>
+						<control id="org.ametys.plugins.sms.list.Add"/>
+						<layout size="very-small">
+							<control id="org.ametys.plugins.sms.list.Edit"/>
+							<control id="org.ametys.plugins.sms.list.Delete"/>
+						</layout>
+					</small>
+				</group>
+	 			<group label="plugin.sms:RIBBON_TABS_TAB_SMS_GROUP_LIST_LABEL" icon="">
+					<large/>
+					<medium>
+						<control id="org.ametys.sms.PhoneNumbers"/>
+					</medium>
+					<small/>
+				</group> 
+			</groups>
+		</tab>
+	</tabs>
+</ribbon>
\ No newline at end of file
Index: main/plugin-sms/resources/js/org/ametys/sms/bus/SmsMessageTargetFactory.i18n.js
===================================================================
--- main/plugin-sms/resources/js/org/ametys/sms/bus/SmsMessageTargetFactory.i18n.js	(revision 0)
+++ main/plugin-sms/resources/js/org/ametys/sms/bus/SmsMessageTargetFactory.i18n.js	(revision 0)
@@ -0,0 +1,75 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+Ext.namespace('org.ametys.sms.bus');
+
+/**
+ * @constructor
+ * @extends org.ametys.messagebus.bus.BasicMessageTargetFactory
+ * @class This is the implementation for pages. Creates a target and sub targets for contents
+ */
+org.ametys.sms.bus.SmsMessageTargetFactory = function ()
+{
+}
+
+Ext.extend(org.ametys.sms.bus.SmsMessageTargetFactory, org.ametys.messagebus.bus.impl.BasicMessageTargetFactory);
+
+/**
+ * The implementation creates sub targets for the page's contents.
+ * @param {String} type The type of target. Can not be null. 
+ * @param {Map&lt;id, value &gt;} parameters The parameters of the factory are :
+ * &lt;ul&gt;
+ *     &lt;li&gt; 'id' with the unique page identifier &lt;/li&gt;
+ * &lt;/ul&gt;
+ * @return {org.ametys.messagebus.message.MessageTarget} The target created
+ */
+org.ametys.sms.bus.SmsMessageTargetFactory.prototype.createTarget = function (type, parameters)
+{
+	var target = new org.ametys.messagebus.message.MessageTarget(type, parameters, []);
+	
+	var serverMessage = new org.ametys.servercomm.ServerMessage('sms', 
+			'/list/properties', 
+			{
+				'id': [parameters['id']]
+			}, 
+			org.ametys.servercomm.ServerComm.PRIORITY_SYNCHRONOUS, 
+			null, 
+			this, 
+			null);
+	var response = org.ametys.servercomm.ServerComm.getInstance().send(serverMessage);
+	
+	if (org.ametys.servercomm.ServerComm.handleBadResponse("<i18n:text i18n:key="PLUGINS_SMS_SMSLISTMESSAGETARGET_BUS_ERROR"/>", response, "org.ametys.sms.bus.SmsMessageTargetFactory"))
+	{
+       throw "org.ametys.sms.bus.SmsMessageTargetFactory request failed";
+	}	
+
+	var list = response.selectSingleNode("infos/lists/list");
+	parameters['label'] = list.getAttribute("label"); 
+	parameters['desc'] = list.getAttribute("desc");  
+	parameters['id'] = list.getAttribute('id');
+
+	var nodes = response.selectNodes("infos/user-rights/right");
+	var rights = [];
+	for (var n=0; n &lt; nodes.length; n++)
+	{
+		var node = nodes[n];
+		
+		rights.push(node.getAttribute("id"));
+	}
+	parameters['rights'] = rights;
+	
+	return target;
+}
\ No newline at end of file
Index: main/plugin-sms/resources/js/org/ametys/sms/SMSActions.i18n.js
===================================================================
--- main/plugin-sms/resources/js/org/ametys/sms/SMSActions.i18n.js	(revision 26839)
+++ main/plugin-sms/resources/js/org/ametys/sms/SMSActions.i18n.js	(working copy)
@@ -1,73 +0,0 @@
-/*
- *  Copyright 2011 Anyware Services
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-Ext.namespace('org.ametys.sms.SendSMS');
-
-/**
- * Open the SMS send dialog.
- */
-org.ametys.sms.SendSMS = function(id, config, pluginName)
-{
-    org.ametys.sms.SendSMS.pluginName = pluginName;
-    org.ametys.sms.SendSMS.showDialog();
-}
-
-org.ametys.sms.SendSMS.showDialog = function()
-{
-    if (!org.ametys.sms.SendSMS.delayedInitialize())
-    {
-        return;
-    }
-    
-    org.ametys.sms.SendSMS.dialog.emptyForm();
-    
-    org.ametys.sms.SendSMS.dialog.show();
-}
-
-org.ametys.sms.SendSMS.delayedInitialize = function()
-{
-    if (org.ametys.sms.SendSMS._initialized)
-    {
-        return true;
-    }
-    
-    org.ametys.sms.SendSMS.dialog = new org.ametys.sms.SendSmsDialog(
-        {
-            okCallback: org.ametys.sms.SendSMS.ok,
-            cancelCallback: org.ametys.sms.SendSMS.cancel
-        },
-        org.ametys.sms.SendSMS.pluginName
-    );
-    
-    org.ametys.sms.SendSMS._initialized = true;
-    
-    return true;
-}
-
-org.ametys.sms.SendSMS.ok = function(button, event)
-{
-    var form = org.ametys.sms.SendSMS.dialog.getForm();
-    if (form.isValid())
-    {
-        var params = form.getValues();
-        params.siteName = context.parameters.siteName;
-    }
-}
-
-org.ametys.sms.SendSMS.cancel = function(button, event)
-{
-    org.ametys.sms.SendSMS.dialog.hide();
-}
Index: main/plugin-sms/resources/js/org/ametys/sms/SendSMSDialog.i18n.js
===================================================================
--- main/plugin-sms/resources/js/org/ametys/sms/SendSMSDialog.i18n.js	(revision 26839)
+++ main/plugin-sms/resources/js/org/ametys/sms/SendSMSDialog.i18n.js	(working copy)
@@ -1,259 +0,0 @@
-/*
- *  Copyright 2011 Anyware Services
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-Ext.namespace('org.ametys.sms');
-
-org.ametys.sms.SendSmsDialog = function(config, pluginName)
-{
-    this._pluginName = pluginName;
-    this._formPanel = this._createFormPanel();
-    
-    var okCallback = config.okCallback;
-    var cancelCallback = config.cancelCallback;
-    
-    config = Ext.applyIf({
-        layout: 'fit',
-        
-        title: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_TITLE'/>",
-        icon: getPluginResourcesUrl(pluginName) + '/img/sms-phone-16.png',
-        
-        width: 425,
-        height: 420,
-        autoScroll: false,
-        
-        items: [ this._formPanel ],
-        
-        closeAction: 'hide',
-        buttons: [{
-            text: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_BUTTON_OK'/>",
-            handler: okCallback
-        }, {
-            text: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_BUTTON_CANCEL'/>",
-            handler: cancelCallback
-        }]        
-    }, config);
-    
-    org.ametys.sms.SendSmsDialog.superclass.constructor.call(this, config);
-};
-
-Ext.extend(org.ametys.sms.SendSmsDialog, org.ametys.DialogBox, {});
-
-/** The plugin name. */
-org.ametys.sms.SendSmsDialog.prototype._pluginName;
-/** The form panel containing the items. */
-org.ametys.sms.SendSmsDialog.prototype._formPanel;
-
-org.ametys.sms.SendSmsDialog.prototype._createFormPanel = function()
-{
-    var formPanel = new Ext.form.FormPanel({
-        labelWidth: 120,
-        defaultType: 'textfield',
-        
-        items: [
-            new org.ametys.HtmlContainer({
-                id: 'sms-hint',
-                    html: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_HINT'/>",
-                cls: 'dialog-text-hint'
-            }),
-            new org.ametys.form.TextAreaField({
-                id: 'sms-message',
-                name: 'message',
-                fieldLabel: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_FIELD_MESSAGE_LABEL'/>",
-                desc: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_FIELD_MESSAGE_DESCRIPTION'/>",
-                maxLength: 160,
-                width: 220,
-                height: 100
-            }),
-            new org.ametys.form.TextAreaField({
-                id: 'sms-phone-numbers',
-                name: 'phone-numbers',
-                fieldLabel: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_FIELD_PHONE_NUMBERS_LABEL'/>",
-                desc: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_FIELD_PHONE_NUMBERS_DESCRIPTION'/>",
-                width: 220,
-                height: 150
-            }),
-            new Ext.Button({
-                text: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_BUTTON_IMPORT'/>",
-                handler: this.importNumbers,
-                scope: this,
-                style: 'position:absolute; right:48px'
-            })
-        ],
-        
-        bodyStyle: 'padding:10px',
-        autoScroll: true
-    });
-    
-    return formPanel;
-}
-
-org.ametys.sms.SendSmsDialog.prototype.getFormPanel = function()
-{
-    return this._formPanel;
-}
-
-org.ametys.sms.SendSmsDialog.prototype.getForm = function()
-{
-    return this._formPanel.getForm();
-}
-
-org.ametys.sms.SendSmsDialog.prototype.emptyForm = function()
-{
-    var form = this._formPanel.getForm();
-    form.findField('sms-message').clearInvalid();
-    form.findField('sms-message').setValue('');
-    form.findField('sms-phone-numbers').clearInvalid();
-    form.findField('sms-phone-numbers').setValue('');
-}
-
-org.ametys.sms.SendSmsDialog.prototype.importNumbers = function(button, event)
-{
-    if (this._importDialog == null)
-    {
-        this._importDialog = this._createImportDialogBox(this._pluginName);
-    }
-    
-    this._importDialog.show();
-}
-
-org.ametys.sms.SendSmsDialog.prototype._createImportDialogBox = function(pluginName)
-{
-    var formPanel = new Ext.form.FormPanel( {
-        labelWidth: 60,
-        bodyStyle: 'padding:5px',
-        fileUpload: true,
-        
-        defaultType: 'textfield',
-        
-        autoScroll: true,
-        items: [
-            new org.ametys.HtmlContainer({
-                id: 'importCsvHint',
-                html: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_IMPORT_HINT'/>",
-                cls: 'dialog-text-hint'
-            }),
-            new org.ametys.form.FileUploadField({
-                id: 'importFile',
-                name: 'importFile',
-                fieldLabel: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_IMPORT_FILE_LABEL'/>",
-                desc: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_IMPORT_FILE_DESC'/>",
-                width: 300,
-                allowBlank: false,
-                emptyText: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_IMPORT_FILE_EMPTY_TEXT'/>",
-                buttonText: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_IMPORT_FILE_BUTTON'/>"
-            })
-        ]
-    });
-    
-    var dialog = new org.ametys.DialogBox({
-        layout: 'fit',
-        
-        title: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_IMPORT_TITLE'/>",
-        icon: getPluginResourcesUrl(pluginName) + '/img/sms-phone-16.png',
-        
-        width: 450,
-        height: 200,
-        autoScroll: false,
-        
-        items: [formPanel],
-        
-        defaultButton: formPanel.getForm().findField('importFile'),
-        closeAction: 'hide',
-        buttons: [{
-            text: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_IMPORT_BUTTON_OK'/>",
-            handler: this.importNumbersOk,
-            scope: this
-        }, {
-            text: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_IMPORT_BUTTON_CANCEL'/>",
-            handler: this.importNumbersCancel,
-            scope: this
-        }]        
-    });
-    
-    return dialog;
-}
-
-org.ametys.sms.SendSmsDialog.prototype.importNumbersOk = function(button, event)
-{
-    var form = this._importDialog.items.itemAt(0).getForm();
-    
-    if (form.isValid())
-    {
-        form.submit({
-            url: getPluginDirectUrl(this._pluginName) + '/read-phone-file',
-            params: {siteName: context.parameters.siteName},
-            
-            waitTitle: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_IMPORT_WAIT_TITLE'/>",
-            waitMsg: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_IMPORT_WAIT_MSG'/>",
-            
-            success: this.importNumbersSuccess,
-            failure: this.importNumbersFailure,
-            scope: this,
-            
-            errorReader: new Ext.data.XmlReader({
-                record: 'field',
-                success: '@success'
-            })
-        });
-    }
-}
-
-org.ametys.sms.SendSmsDialog.prototype.importNumbersCancel = function(button, event)
-{
-    this._importDialog.hide();
-}
-
-org.ametys.sms.SendSmsDialog.prototype.importNumbersSuccess = function(form, action)
-{
-    var phoneNumbers = action.result.content;
-    
-    while (phoneNumbers.indexOf(',') > -1)
-    {
-        phoneNumbers = phoneNumbers.replace(',', '\n');
-    }
-    
-    var form = this._formPanel.getForm();
-    
-    form.findField('sms-phone-numbers').setValue(phoneNumbers);
-    
-    this._importDialog.hide();
-}
-
-org.ametys.sms.SendSmsDialog.prototype.importNumbersFailure = function(form, action)
-{
-    var message = '';
-    var error = action.result.error;
-    
-    if (error == 'rejected-file')
-    {
-        message = "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_IMPORT_ERROR_REJECTED_FILE'/>";
-    }
-    else if (error == 'not-text-file')
-    {
-        message = "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_IMPORT_ERROR_NOT_TEXT'/>";
-    }
-    else
-    {
-        message = "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_IMPORT_ERROR_UNKNOWN'/>";
-    }
-    
-    Ext.Msg.show({
-        title: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_IMPORT_ERROR_TITLE'/>",
-        msg: message,
-        buttons: Ext.Msg.OK,
-        icon: Ext.MessageBox.ERROR
-    });
-}
Index: main/plugin-sms/resources/js/org/ametys/sms/button/AddListActionButton.i18n.js
===================================================================
--- main/plugin-sms/resources/js/org/ametys/sms/button/AddListActionButton.i18n.js	(revision 0)
+++ main/plugin-sms/resources/js/org/ametys/sms/button/AddListActionButton.i18n.js	(revision 0)
@@ -0,0 +1,71 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+Ext.namespace('org.ametys.sms.button.AddListActionButton');
+
+org.ametys.sms.button.AddListActionButton = function(id, configuration, pluginName) 
+{
+	org.ametys.sms.button.AddListActionButton.superclass.constructor.call(this, id, configuration, pluginName);
+};
+
+Ext.extend(org.ametys.sms.button.AddListActionButton, org.ametys.ribbon.control.button.ActionSelectionButton, {});
+
+org.ametys.sms.button.AddListActionButton.prototype.onMessage = function(messages)
+{
+	for (var i=0; i &lt; messages.length; i++)
+	{
+		if (messages[i].getType().getName() == org.ametys.ribbon.RibbonManager.EVENTTYPE_SELECTIONCHANGED)
+		{
+			var allTargets = messages[i].getTargets();
+			var targets = org.ametys.messagebus.message.MessageTargetHelper.findAll(allTargets, this._testTarget.createDelegate(this));
+
+			if (targets.length == 1 || targets.length &gt; 1 &amp;&amp; this.getConfiguration()['enable-multiselection'] != "false")
+			{	
+				this.getElements().each(function(element) { element.refreshing(); element.setDisabled(true); });
+				this.updateTooltip(this.getConfiguration()["refresh-description"]);
+
+				org.ametys.ribbon.RibbonManager.getInstance().refreshElement('control', this, {});
+			}
+			else
+			{
+				this.getElements().each(function(element) { element.disable(); });
+			}
+		}
+	}
+}
+
+org.ametys.sms.button.AddListActionButton.prototype.configurationError = function(response)
+{
+	this.getElements().each(function(element) { element.stopRefreshing(); element.setDisabled(true); });
+	this.updateTooltip(this.getConfiguration()["default-description"]);
+}
+
+org.ametys.sms.button.AddListActionButton.prototype.configurationChanged = function(params)
+{
+	this.getElements().each(function(element) { element.stopRefreshing(); });
+	if (params['sms-has-right-add'] != null)
+	{
+		this.updateTooltip(this.getConfiguration()["default-description"]);
+		this.getElements().each(function(element) { element.enable(); });
+	}
+	else
+	{
+		this.updateTooltip(this.getConfiguration()["no-right-description"]);
+		this.getElements().each(function(element) { element.disable(); });
+	}
+	
+	this._lastParams = params;
+}
Index: main/plugin-sms/resources/js/org/ametys/sms/list/ListActions.i18n.js
===================================================================
--- main/plugin-sms/resources/js/org/ametys/sms/list/ListActions.i18n.js	(revision 0)
+++ main/plugin-sms/resources/js/org/ametys/sms/list/ListActions.i18n.js	(revision 0)
@@ -0,0 +1,246 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+Ext.namespace('org.ametys.sms.list.Edit');
+Ext.namespace('org.ametys.sms.list.Delete');
+
+/**
+ * Create a new list
+ */
+org.ametys.sms.list.Edit.add = function(id, config, pluginName)
+{
+	org.ametys.sms.list.Edit._mode = "add";
+	org.ametys.sms.list.Edit._pluginName = pluginName;
+	
+	var targets = org.ametys.ribbon.RibbonManager.getInstance().getCurrentSelectionTargets();
+	var targetProvider = org.ametys.messagebus.message.MessageTargetHelper.findFirst(targets, function(target) { return target.getType() == "sms-list-provider" });
+
+	if (targetProvider == null)
+		return;
+	
+	org.ametys.sms.list.Edit._delayedInitialized();
+	org.ametys.sms.list.Edit._box.show();
+
+	org.ametys.sms.list.Edit._lang = targetProvider.getParameters().lang;
+	org.ametys.sms.list.Edit._inputTitle.setValue("");
+	org.ametys.sms.list.Edit._textDesc.setValue("");
+}
+
+org.ametys.sms.list.Edit._delayedInitialized = function()
+{	
+	if (!org.ametys.sms.list.Edit._box)
+	{		
+		org.ametys.sms.list.Edit._inputTitle = new org.ametys.form.TextField({
+        	id: 'sms-list-title',
+            name: 'title',
+            fieldLabel: "<i18n:text i18n:key='PLUGINS_SMS_ADD_LIST_FORM_TITLE_LABEL'/>",
+            desc: "<i18n:text i18n:key='PLUGINS_SMS_ADD_LIST_FORM_TITLE_DESC'/>",
+            width: 220,
+    	    allowBlank: false
+        });
+		
+		org.ametys.sms.list.Edit._textDesc = new org.ametys.form.TextAreaField({
+            id: 'sms-list-desc',
+            name: 'desc',
+            fieldLabel: "<i18n:text i18n:key='PLUGINS_SMS_ADD_LIST_FORM_DESC_LABEL'/>",
+            desc: "<i18n:text i18n:key='PLUGINS_SMS_ADD_LIST_FORM_DESC_DESC'/>",
+            width: 220,
+            height: 100
+        });
+
+		org.ametys.sms.list.Edit._formPanel = new Ext.form.FormPanel({
+	        labelWidth: 120,
+	        defaultType: 'textfield',
+	        
+	        items: [
+	            new org.ametys.HtmlContainer({
+	                id: 'sms-hint',
+	                html: "<i18n:text i18n:key='PLUGINS_SMS_ADD_LIST_FORM_HINT'/>",
+	                cls: 'dialog-text-hint'
+	            }),
+	            org.ametys.sms.list.Edit._inputTitle,
+	        	org.ametys.sms.list.Edit._textDesc
+	        ],
+	        
+	        bodyStyle: 'padding:10px',
+	        autoScroll: true
+	    });
+		
+		org.ametys.sms.list.Edit._box = new org.ametys.DialogBox({
+	        layout: 'fit',
+	        title: "<i18n:text i18n:key='PLUGINS_SMS_ADD_LIST_FORM_BOX_TITLE'/>",
+	        width: 435,
+	        height: 260,
+	        defaultButton: 'sms-list-title',
+			icon : getPluginResourcesUrl(org.ametys.sms.list.Edit._pluginName) + "/img/sms-phone-16.png",
+	        autoScroll: false,	        
+	        items: [ org.ametys.sms.list.Edit._formPanel ],        
+	        closeAction: 'hide',
+	        buttons: [{
+	            text: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_BUTTON_OK'/>",
+	            handler: org.ametys.sms.list.Edit._ok
+	        }, {
+	            text: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_BUTTON_CANCEL'/>",
+	            handler: org.ametys.sms.list.Edit._cancel
+	        }]        
+	    });
+	}
+};
+
+/**
+ * Edit a tag
+ */
+org.ametys.sms.list.Edit.edit = function(id, config, pluginName)
+{
+	org.ametys.sms.list.Edit._mode = "edit";
+	org.ametys.sms.list.Edit._pluginName = pluginName;
+
+	var targets = org.ametys.ribbon.RibbonManager.getInstance().getCurrentSelectionTargets();
+	var target = org.ametys.messagebus.message.MessageTargetHelper.findFirst(targets, function(target) { return target.getType() == "sms-list" });
+	
+	var targetProvider = org.ametys.messagebus.message.MessageTargetHelper.findFirst(targets, function(target) { return target.getType() == "sms-list-provider" });
+	
+	if (target == null || targetProvider == null)
+		return; 
+	
+	org.ametys.sms.list.Edit._delayedInitialized();
+	org.ametys.sms.list.Edit._box.show();
+	
+	org.ametys.sms.list.Edit._idList = target.getParameters().id;
+	org.ametys.sms.list.Edit._right = target.getParameters().right;
+	org.ametys.sms.list.Edit._lang = targetProvider.getParameters().lang;
+
+	var label = target.getParameters().label;
+	var desc = target.getParameters().desc;
+
+	org.ametys.sms.list.Edit._inputTitle.setValue(label);
+	org.ametys.sms.list.Edit._textDesc.setValue(desc);
+}
+
+org.ametys.sms.list.Edit._ok = function()
+{
+	if (org.ametys.sms.list.Edit._formPanel.getForm().isValid())
+	{
+		var params = {};
+		
+		params.title = org.ametys.sms.list.Edit._inputTitle.getValue();
+		params.desc = org.ametys.sms.list.Edit._textDesc.getValue();
+		params.mode = org.ametys.sms.list.Edit._mode;
+		params.id = org.ametys.sms.list.Edit._idList;
+		params.lang = org.ametys.sms.list.Edit._lang;
+		params.right = org.ametys.sms.list.Edit._right;
+		
+		var serverMessage = new org.ametys.servercomm.ServerMessage(org.ametys.sms.list.Edit._pluginName, '/add-edit-list', params, org.ametys.servercomm.ServerComm.PRIORITY_MAJOR, org.ametys.sms.list.Edit._okCB, this, []);
+		org.ametys.servercomm.ServerComm.getInstance().send(serverMessage);
+		
+		org.ametys.sms.list.Edit._box.showWaitMsg("<i18n:text i18n:key='PLUGINS_SMS_ADD_LIST_WAITING_MSG'/>");
+	}
+	else
+	{
+		Ext.Msg.show({
+			   title: "<i18n:text i18n:key='PLUGINS_SMS_SEND_FORM_ERROR_TITLE'/>",
+			   msg: "<i18n:text i18n:key='PLUGINS_SMS_ADD_LIST_FORM_ERROR_MESSAGE'/>", 
+			   buttons: Ext.Msg.OK,
+			   icon: Ext.MessageBox.ERROR
+		});
+	}
+	
+}
+
+org.ametys.sms.list.Edit._okCB = function(response, args)
+{
+	org.ametys.sms.list.Edit._box.hideWaitMsg();
+	
+	if (org.ametys.servercomm.ServerComm.handleBadResponse("<i18n:text i18n:key="PLUGINS_SMS_ADD_LIST_ADD_EDIT_ERROR"/>", response, "org.ametys.sms.list.Edit"))
+	{
+		return;
+	}
+
+	var id = response.selectSingleNode("ActionResult/id")[org.ametys.servercomm.ServerComm.xmlTextContent];
+	var target = org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget("sms-list", {'id': id});
+	
+	if (org.ametys.sms.list.Edit._mode == "edit")
+	{
+		var event = org.ametys.messagebus.bus.MessageBuilder.getInstance().createMessage(org.ametys.ribbon.RibbonManager.EVENTTYPE_METADATACHANGED, null, [target]);
+	}
+	else if (org.ametys.sms.list.Edit._mode == "add")
+	{
+		var event = org.ametys.messagebus.bus.MessageBuilder.getInstance().createMessage(org.ametys.ribbon.RibbonManager.EVENTTYPE_CREATED, null, [target]);
+	}
+	
+	org.ametys.messagebus.MessageBus.getInstance().fireMessages([event]);
+	
+	org.ametys.sms.list.Edit._box.hide();
+}
+
+org.ametys.sms.list.Edit._cancel = function()
+{
+	org.ametys.sms.list.Edit._box.hide();
+}
+
+/** ------------------------------------ */
+/** ------------------------------------ */
+/** ------------------------------------ */
+
+/**
+ * Delete a category
+ */
+org.ametys.sms.list.Delete.act = function (id, config, pluginName)
+{
+	org.ametys.sms.list.Delete._pluginName = pluginName;
+	
+	Ext.Msg.confirm("<i18n:text i18n:key="PLUGINS_SMS_ADD_LIST_HANDLE_LIST_DELETE"/>", 
+			"<i18n:text i18n:key="PLUGINS_SMS_ADD_LIST_HANDLE_LIST_DELETE_CONFIRM"/>", 
+			org.ametys.sms.list.Delete._doAction, 
+			this
+	);
+}
+
+org.ametys.sms.list.Delete._doAction = function(button)
+{
+	if (button == 'yes')
+	{
+		var targets = org.ametys.ribbon.RibbonManager.getInstance().getCurrentSelectionTargets();
+		var targetAll = org.ametys.messagebus.message.MessageTargetHelper.findAll(targets, function(target) { return target.getType() == "sms-list" });
+		if (targetAll == null)
+			return;
+		
+		var targetsIds = [];
+	    for (var i = 0; i &lt; targetAll.length; i++)
+	    {
+	        targetsIds.push(targetAll[i].getParameters()['id']);
+	    }
+
+		var params = {'ids':  targetsIds}; 
+		var serverMessage = new org.ametys.servercomm.ServerMessage(org.ametys.sms.list.Delete._pluginName, '/delete-list', params, org.ametys.servercomm.ServerComm.PRIORITY_MAJOR, org.ametys.sms.list.Delete._doActionCB, this, [targetAll]);
+		org.ametys.servercomm.ServerComm.getInstance().send(serverMessage);
+	
+	}
+}
+
+org.ametys.sms.list.Delete._doActionCB = function (response, args)
+{
+	if (org.ametys.servercomm.ServerComm.handleBadResponse("<i18n:text i18n:key="PLUGINS_SMS_ADD_LIST_DELETE_ERROR"/>", response, "org.ametys.sms.list.Delete"))
+	{
+		return;
+	}
+	
+	var targetAll = args[0];
+    var event = org.ametys.messagebus.bus.MessageBuilder.getInstance().createMessage(org.ametys.ribbon.RibbonManager.EVENTTYPE_DELETED, null, targetAll);
+	org.ametys.messagebus.MessageBus.getInstance().fireMessages([event]);
+
+	
+}
Index: main/plugin-sms/resources/js/org/ametys/sms/tool/SmsListTool.i18n.js
===================================================================
--- main/plugin-sms/resources/js/org/ametys/sms/tool/SmsListTool.i18n.js	(revision 0)
+++ main/plugin-sms/resources/js/org/ametys/sms/tool/SmsListTool.i18n.js	(revision 0)
@@ -0,0 +1,386 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+Ext.namespace('org.ametys.sms.tool');
+
+org.ametys.sms.tool.SmsListTool = function(id, pluginName) 
+{
+	org.ametys.sms.tool.SmsListTool.superclass.constructor.call(this, id, pluginName);
+};
+
+Ext.extend(org.ametys.sms.tool.SmsListTool, org.ametys.uitool.tool.Tool, {});
+
+org.ametys.sms.tool.SmsListTool._lang = 'fr';
+
+org.ametys.sms.tool.SmsListTool.prototype.createPanel = function()
+{
+	// The mapping for XML doc
+    var mapping = Ext.data.Record.create([
+        {name: 'value', mapping: '@id'},
+    	{name: 'label', mapping: 'label'},
+        {name: 'nbUser', mapping: 'nb-user'}
+    ]);
+
+    // HTTP proxy
+    var proxy = new Ext.data.HttpProxy({
+        url: getPluginDirectUrl('sms') + '/sms-list',
+        method: 'post',
+        timeout: 0
+    });
+    
+    // Data store
+    this._xmlStore = new Ext.data.Store({
+        proxy: proxy,
+        reader: new Ext.data.XmlReader({
+                record: 'list',
+                id: '@id'
+            },
+            mapping
+        ),
+        
+        sortInfo: {field: 'label', direction: 'ASC'}
+    });
+    
+    this._xmlStore.on('beforeload', this._onBeforeLoad, this);
+    this._xmlStore.on('load', this._onLoad, this);
+    
+	this._langCombo = this._drawLanguageCombo();
+	this._loadLanguages();
+	
+	var langComboLabel = new Ext.form.Label({
+		width: 60, 
+		bodyStyle: {'textAlign': 'center'},
+    	text: "<i18n:text i18n:key='PLUGINS_SMS_LIST_UITOOL_LANGUAGE'/>"
+	});
+	
+	var topPanel = new Ext.Panel({
+		region: 'north',
+		border: false,
+		height: 25,
+		layout: 'hbox',
+		cls: 'smslist_tool_form',
+		labelWidth: 30,
+		labelSeparator: '',
+        items: [langComboLabel, this._langCombo]
+    });
+	
+    this._grid = new Ext.grid.GridPanel({
+        region: 'center',        
+        store: this._xmlStore,
+        viewConfig: {
+        	forceFit: true,
+        	autoFill: true
+        },
+        columns: [
+            {header: "<i18n:text i18n:key='PLUGINS_SMS_LIST_UITOOL_COL_TITLE'/>", sortable: true, dataIndex: 'label'},
+            {header: "<i18n:text i18n:key='PLUGINS_SMS_LIST_UITOOL_COL_SUBSCRIBERS'/>", width: 70, sortable: true, dataIndex: 'nbUser', fixed: true }
+        ]
+    });
+    this._grid.getSelectionModel().on('selectionchange', this._onSelectLink, this);
+    this._grid.on('rowdblclick', this._openContent);
+
+    this._mainPanel = new Ext.Panel({
+        layout: 'border',
+        items: [topPanel, this._grid]
+    });
+    
+    this._mainPanel.addListener('focus', this._onFocus, this);
+    this._mainPanel.addListener('blur', this._onBlur, this);
+    this._mainPanel.addListener('afterrender', this._refresh, this);
+    this._mainPanel.addListener('close', this._onClose, this);
+    
+    return this._mainPanel;
+}
+
+/**
+ * Listens when the store has loaded records.
+ * @private
+ */
+org.ametys.sms.tool.SmsListTool.prototype._onLoad = function(store, records, options)
+{
+    this.upToDate();
+}
+
+/**
+ * Listens when the store is about to load or reload.
+ * @private
+ */
+org.ametys.sms.tool.SmsListTool.prototype._onBeforeLoad = function(store, options)
+{
+	// Let the load occur only if we know the language.
+	if (this._lang == null)
+	{
+		return false;
+	}
+	
+	options.params.lang = this._lang;
+	options.params.site = context.parameters.siteName;
+}
+
+org.ametys.sms.tool.SmsListTool.prototype._refresh = function(manual)
+{
+	this.refreshing();
+    this._xmlStore.load();
+}
+
+org.ametys.sms.tool.SmsListTool.prototype._drawLanguageCombo = function()
+{
+    var langStore = new Ext.data.SimpleStore({
+        fields: ['lang', 'langlabel', 'langimage', 'langfull'],
+        data: []
+    });
+    
+    var combo = new Ext.form.ComboBox({
+        flex: 1,
+        minListWidth: 120,
+        
+        forceSelection: true,
+        editable: false,
+        mode: 'local',
+        resizable: false,
+        triggerAction: 'all',
+        
+        store: langStore,
+        
+        name: 'lang',
+        valueField: 'lang',
+        displayField: 'langfull',
+        
+        tpl: new Ext.XTemplate(
+                '&lt;tpl for="."&gt;',
+                    '&lt;div class="x-combo-list-item combo-list-lang-item"&gt;',
+                        '&lt;img src="{langimage}" style="float:left; margin-right: 3px"/&gt;', 
+                        '{langlabel} &lt;em&gt;({lang})&lt;/em&gt;',
+                        '&lt;div class="x-clear"&gt;&lt;/div&gt;',
+                    '&lt;/div&gt;',
+                '&lt;/tpl&gt;')
+    });
+    
+    combo.addListener('select', this._langChange, this);
+    
+    return combo;
+}
+
+org.ametys.sms.tool.SmsListTool.prototype._loadLanguages = function()
+{
+    var serverMessage = new org.ametys.servercomm.ServerMessage('web', '/repository/languages.xml', {'siteName': context.parameters.siteName}, org.ametys.servercomm.ServerComm.PRIORITY_MAJOR, this._loadLanguagesCb, this, []);
+    org.ametys.servercomm.ServerComm.getInstance().send(serverMessage);
+}
+
+org.ametys.sms.tool.SmsListTool.prototype._loadLanguagesCb = function(response, args)
+{
+    if (org.ametys.servercomm.ServerComm.handleBadResponse("<i18n:text i18n:key='PLUGINS_SMS_LIST_GET_LANGUAGES_ERROR'/>", response, "org.ametys.linkdirectory.LinkDirectoryTool._loadLanguagesCb"))
+    {
+        return;
+    }
+    
+    var store = this._langCombo.getStore();
+    store.removeAll();	
+    
+    this.defaultLang = null;
+    var isThereEn = false;
+    var ok = false;
+    
+    var languages = response.selectNodes('sitemaps/sitemap');
+    for (var i=0; i &lt; languages.length; i++)
+    {
+        if (!ok)
+        {
+            // Find the default language
+            if (this._lang == languages[i].getAttribute('name'))
+            {
+                this.defaultLang = languages[i].getAttribute('name');
+                ok = true;
+            }
+            else if (context.parameters.userLocale == languages[i].getAttribute('name'))
+            {
+                this.defaultLang = context.parameters.userLocale;
+            }
+            if (languages[i].getAttribute('name') == 'en')
+            {
+                isThereEn = true;
+            }
+        }
+        
+        var data = {
+            lang: languages[i].getAttribute('name'),
+            langlabel: languages[i].selectSingleNode('label')[org.ametys.servercomm.ServerComm.xmlTextContent],
+            langimage: context.contextPath + languages[i].selectSingleNode ('small-icon')[org.ametys.servercomm.ServerComm.xmlTextContent],
+            langfull: languages[i].selectSingleNode('label')[org.ametys.servercomm.ServerComm.xmlTextContent] + ' (' + languages[i].getAttribute('name') + ')'
+        };
+        store.add(new store.recordType(data));
+    }
+    
+    if (this.defaultLang == null)
+    {
+        if (isThereEn)
+        {
+            this.defaultLang = 'en';
+        }
+        else
+        {
+            this.defaultLang = languages[0].getAttribute('name');
+        }
+    }
+    
+    // Initialize with default value
+    this._langCombo.setValue(this.defaultLang);
+    this._lang = this.defaultLang;
+    store.sort('lang', 'ASC');
+    this._refresh();
+
+}
+
+org.ametys.sms.tool.SmsListTool.prototype._langChange = function(combo, record, index)
+{
+    if (this._lang == record.data.lang)
+    {
+        return;
+    }
+    
+    this._lang = record.data.lang;
+    
+    // Refresh the tool content.
+    this._refresh();
+}
+
+/**
+ * Listens when the panel receives the focus.
+ * @private
+ */
+org.ametys.sms.tool.SmsListTool.prototype._onFocus = function(messages)
+{
+    this._hasFocus = true;
+    this._sendSelectionMessage();
+}
+
+/**
+ * Listens when the panel looses the focus.
+ * @private
+ */
+org.ametys.sms.tool.SmsListTool.prototype._onBlur = function(messages)
+{
+    this._hasFocus = false;
+}
+
+/**
+ * Listens when the panel is closed.
+ * @private
+ */
+org.ametys.sms.tool.SmsListTool.prototype._onClose = function(messages)
+{
+	if (this._hasFocus)
+	{
+		var event = org.ametys.messagebus.bus.MessageBuilder.getInstance().createMessage(org.ametys.ribbon.RibbonManager.EVENTTYPE_SELECTIONCHANGED, null, []);
+		var eventBus =  org.ametys.messagebus.MessageBus.getInstance();
+		eventBus.fireMessages([event]);
+	}
+}
+
+org.ametys.sms.tool.SmsListTool.prototype._sendSelectionMessage = function()
+{
+	var records = this._grid.getSelectionModel().getSelections();
+	var parentTarget = 	org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('sms-list-provider', {'lang': 'fr'}); // FIXME
+    for (var i=0; i &lt; records.length; i++)
+    {
+    	parentTarget.getSubTargets().push(org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('sms-list', {'id': records[i].id}));
+    }
+    
+    var event = org.ametys.messagebus.bus.MessageBuilder.getInstance().createMessage(org.ametys.ribbon.RibbonManager.EVENTTYPE_SELECTIONCHANGED, null, [parentTarget]);
+    org.ametys.messagebus.MessageBus.getInstance().fireMessages([event]);
+}
+
+/**
+ * Called when selection change in the grid.
+ * Send event selection on bus.
+ * @param Ext.grid.AbstractSelectionModel sm The selection model.
+ * @private
+ */
+org.ametys.sms.tool.SmsListTool.prototype._onSelectLink = function(sm)
+{
+	window.clearTimeout(this._sendTimeout);
+	this._sendTimeout = this._fireMessages.defer(10, this, [sm]);
+}
+
+org.ametys.sms.tool.SmsListTool.prototype._fireMessages = function (sm)
+{
+	this._sendSelectionMessage();
+}
+
+org.ametys.sms.tool.SmsListTool.prototype.onMessage = function (messages)
+{
+	for (var i=0; i &lt; messages.length; i++)
+	{
+		if (messages[i].getType().getName() == org.ametys.ribbon.RibbonManager.EVENTTYPE_CREATED)
+		{
+			var targets = messages[i].getTargets();
+			var target = org.ametys.messagebus.message.MessageTargetHelper.findFirst(targets, function(target) { return target.getType() == "sms-list";});
+			
+			if (target != null)
+			{
+				var data = {
+			            value: target.getParameters().id,
+			            label: target.getParameters().label,
+			            nbUser: '0'
+			        };
+				this._xmlStore.add(new this._xmlStore.recordType(data));
+				this._refresh();
+			}
+					
+		}
+		else if (messages[i].getType().getName() == org.ametys.ribbon.RibbonManager.EVENTTYPE_DELETED)
+		{
+			var targets = messages[i].getTargets();
+			var listTarget = org.ametys.messagebus.message.MessageTargetHelper.findAll(targets, function(target) { return target.getType() == "sms-list";});
+
+			var targetRecords = [];
+			for (var i=0; i &lt; listTarget.length; i++)
+			{
+				var record = this._xmlStore.getById(listTarget[i].getParameters().id);
+				targetRecords.push(record);
+			}
+			this._xmlStore.remove(targetRecords);
+			this._refresh();
+		}
+		else if (messages[i].getType().getName() == org.ametys.ribbon.RibbonManager.EVENTTYPE_METADATACHANGED)
+		{			
+			var targets = messages[i].getTargets();
+			var target = org.ametys.messagebus.message.MessageTargetHelper.findFirst(targets, function(target) { return target.getType() == "sms-list";});
+			
+			if (target != null)
+			{
+				var record = this._xmlStore.getById(target.getParameters().id);
+				record.set("label", target.getParameters().label);
+				
+				this._refresh();
+			}
+		}
+	}
+}
+
+org.ametys.sms.tool.SmsListTool.prototype._openContent = function (node, e)
+{
+	var targets = org.ametys.ribbon.RibbonManager.getInstance().getCurrentSelectionTargets();
+	var target = org.ametys.messagebus.message.MessageTargetHelper.findFirst(targets, function(target) { return target.getType() == "sms-list" });
+	
+	if (target == null)
+		return;
+	
+	var params = {};
+	params.id = target.getParameters().id;
+	params.title = target.getParameters().label;
+	org.ametys.uitool.ToolsManager.getInstance().openTool ("uitool-sms-phone-numbers", params);
+}
Index: main/plugin-sms/resources/js/org/ametys/sms/tool/SmsListToolFactory.js
===================================================================
--- main/plugin-sms/resources/js/org/ametys/sms/tool/SmsListToolFactory.js	(revision 0)
+++ main/plugin-sms/resources/js/org/ametys/sms/tool/SmsListToolFactory.js	(revision 0)
@@ -0,0 +1,37 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+Ext.namespace('org.ametys.sms.tool');
+
+/**
+ * Do not call this constructor. Declared to and used by the {@see org.ametys.uitool.ToolsManager}
+ * @constructor
+ * @extends org.ametys.uitool.tool.impl.UniqueInstanceToolFactory
+ * @class The factory to creates the sitemap tool.
+ * @param {String} id The unique id of the factory
+ * @param {Map} config Options 
+ * @param {String} pluginName The name of the plugin where the factory was declared
+ */
+org.ametys.sms.tool.SmsListToolFactory = function(id, config, pluginName) 
+{
+	org.ametys.sms.tool.SmsListToolFactory.superclass.constructor.call(this, id, config, pluginName);
+};
+Ext.extend(org.ametys.sms.tool.SmsListToolFactory, org.ametys.uitool.tool.impl.UniqueInstanceToolFactory, {});
+
+org.ametys.sms.tool.SmsListToolFactory.prototype.createTool = function (id)
+{
+	return new org.ametys.sms.tool.SmsListTool(id, this.getPluginName());
+}
Index: main/plugin-sms/resources/js/org/ametys/sms/tool/PhoneNumbersToolFactory.js
===================================================================
--- main/plugin-sms/resources/js/org/ametys/sms/tool/PhoneNumbersToolFactory.js	(revision 0)
+++ main/plugin-sms/resources/js/org/ametys/sms/tool/PhoneNumbersToolFactory.js	(revision 0)
@@ -0,0 +1,30 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+Ext.namespace('org.ametys.sms.tool');
+
+org.ametys.sms.tool.PhoneNumbersToolFactory = function(id, config, pluginName) 
+{
+	org.ametys.sms.tool.PhoneNumbersToolFactory.superclass.constructor.call(this, id, config, pluginName);
+};
+
+Ext.extend(org.ametys.sms.tool.PhoneNumbersToolFactory, org.ametys.uitool.tool.impl.UniqueInstanceToolFactory,
+{
+	createTool : function (id)
+	{
+		return new org.ametys.sms.tool.PhoneNumbersTool (id, this.getPluginName());
+	}
+});
\ No newline at end of file
Index: main/plugin-sms/resources/js/org/ametys/sms/tool/PhoneNumbersTool.i18n.js
===================================================================
--- main/plugin-sms/resources/js/org/ametys/sms/tool/PhoneNumbersTool.i18n.js	(revision 0)
+++ main/plugin-sms/resources/js/org/ametys/sms/tool/PhoneNumbersTool.i18n.js	(revision 0)
@@ -0,0 +1,227 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+Ext.namespace('org.ametys.sms.tool');
+
+/**
+ * Do not call this constructor. Creates a content Tool.
+ * @constructor
+ * @extends org.ametys.uitool.tool.Tool
+ * @class 
+ */
+org.ametys.sms.tool.PhoneNumbersTool = function(id, pluginName) 
+{
+	org.ametys.sms.tool.PhoneNumbersTool.superclass.constructor.call(this, id, pluginName);
+};
+
+Ext.extend(org.ametys.sms.tool.PhoneNumbersTool, org.ametys.uitool.tool.Tool, {});
+
+org.ametys.sms.tool.PhoneNumbersTool.prototype.createPanel = function ()
+{
+	// The mapping for XML doc
+    var mapping = Ext.data.Record.create([
+        {name: 'phone-number', mapping: '@value'},
+        {name: 'date', mapping: '@date'}
+    ]);
+
+    // HTTP proxy
+    var proxy = new Ext.data.HttpProxy({
+        url: getPluginDirectUrl('sms') + '/subcriber-phone-number',
+        method: 'post',
+        timeout: 0
+    });
+    
+    // Data store
+    this._store = new Ext.data.Store({
+        proxy: proxy,
+        reader: new Ext.data.XmlReader({
+                record: 'phoneNumber'
+        	},
+            mapping
+        )
+    });
+    this._store.on('beforeload', this._onBeforeLoad, this);
+    this._store.on('load', this._onLoad, this);
+    
+	
+	this._grid = new Ext.grid.GridPanel({
+		
+		store :this._store,
+		region: 'center',
+	    columns: [
+	        {header: "<i18n:text i18n:key="PLUGINS_SMS_NUMBER_UITOOL_COL_TITLE"/>", width: 400, sortable: true, dataIndex: 'phone-number', hideable: false},
+	        {header: "<i18n:text i18n:key="PLUGINS_SMS_NUMBER_UITOOL_COL_DATE"/>", width: 400, sortable: true, dataIndex: 'date', hideable: false, renderer: this.renderDate}
+		],
+	    
+        cls: 'mask-below-menu'
+	});
+	this._grid.getSelectionModel().on('selectionchange', this._onSelectionChange, this);
+	
+	this._mainPanel = new Ext.Panel({
+		tool : this,
+		layout: 'border',
+		border: false,
+		cls: 'smslist-subscribers-tool',
+		items:[ 
+		       {
+		   		   cls: 'smslist-subscribers-tool',
+		    	   region: 'north',
+		    	   html: "<i18n:text i18n:key="PLUGINS_SMS_NUMBER_UITOOL_LIST_SELECT"/>",
+		    	   hidden: true,
+		    	   border: false
+		       },
+		       this._grid
+		]
+	});
+	
+	return this._mainPanel;
+}
+
+org.ametys.sms.tool.PhoneNumbersTool.prototype.setParams = function (params)
+{
+	if (params.id)
+	{
+		org.ametys.sms.tool.PhoneNumbersTool._idList = params.id;
+		org.ametys.sms.tool.PhoneNumbersTool._titleList = params.title;
+	}
+	else
+	{
+		var targets = org.ametys.ribbon.RibbonManager.getInstance().getCurrentSelectionTargets();
+		var target = org.ametys.messagebus.message.MessageTargetHelper.findFirst(targets, function(target) { return target.getType() == "sms-list" });
+		
+		if (target == null)
+			return;
+		
+		org.ametys.sms.tool.PhoneNumbersTool._idList = target.getParameters().id;
+		org.ametys.sms.tool.PhoneNumbersTool._titleList = target.getParameters().label;
+	}
+    this.refresh();
+}
+
+/**
+ * Listens when the store is about to load or reload.
+ * @private
+ */
+org.ametys.sms.tool.PhoneNumbersTool.prototype._onBeforeLoad = function(store, options)
+{	
+	options.params.id = org.ametys.sms.tool.PhoneNumbersTool._idList;
+}
+
+org.ametys.sms.tool.PhoneNumbersTool.prototype.refresh = function()
+{
+	if (org.ametys.sms.tool.PhoneNumbersTool._idNextList)
+	{
+		org.ametys.sms.tool.PhoneNumbersTool._idList = org.ametys.sms.tool.PhoneNumbersTool._idNextList;
+	}
+	
+	if (org.ametys.sms.tool.PhoneNumbersTool._titleNextList)
+	{
+		org.ametys.sms.tool.PhoneNumbersTool._titleList = org.ametys.sms.tool.PhoneNumbersTool._titleNextList;
+	}
+	
+	this._title = this.getConfiguration()['title'] + (org.ametys.sms.tool.PhoneNumbersTool._titleList != null ? (" : " + org.ametys.sms.tool.PhoneNumbersTool._titleList) : "");
+	this._updateTooltip();
+	this._repaintTooltip();
+	
+	this._mainPanel.items.get(0).hide();
+	this._mainPanel.items.get(1).show();
+	this._mainPanel.ownerCt.doLayout();
+	
+	this.refreshing();
+    this._store.load();
+}
+
+/**
+ * Listens when the store has loaded records.
+ * @private
+ */
+org.ametys.sms.tool.PhoneNumbersTool.prototype._onLoad = function(store, records, options)
+{
+	this.upToDate();
+}
+
+org.ametys.sms.tool.PhoneNumbersTool.prototype.onMessage = function (messages)
+{
+	for (var i=messages.length - 1; i &gt;= 0; i--)
+	{
+		if (messages[i].getType().getName() == org.ametys.ribbon.RibbonManager.EVENTTYPE_SELECTIONCHANGED)
+		{
+			var targets = messages[i].getTargets();
+			var target = org.ametys.messagebus.message.MessageTargetHelper.findFirst(targets, function(target) { return target.getType() == "sms-list";});
+			
+			if (target != null &amp;&amp; org.ametys.sms.tool.PhoneNumbersTool._idList != target.getParameters().id)
+			{
+				// target has changed
+				org.ametys.sms.tool.PhoneNumbersTool._idNextList = target.getParameters().id;
+				org.ametys.sms.tool.PhoneNumbersTool._titleNextList = target.getParameters().label;
+				this.outOfDate(true);
+			}
+			else if (target == null)
+			{
+				// No more target
+				org.ametys.sms.tool.PhoneNumbersTool._idList = null;
+				org.ametys.sms.tool.PhoneNumbersTool._titleList = null;
+				org.ametys.sms.tool.PhoneNumbersTool._idNextList = null;
+				org.ametys.sms.tool.PhoneNumbersTool._titleNextList = null;
+				
+				this._setInNoSelectionState();
+			}
+			return;
+		}
+	}
+}
+
+org.ametys.sms.tool.PhoneNumbersTool.prototype._setInNoSelectionState = function ()
+{
+	this._currentCategory = null;
+	this._mainPanel.items.get(0).show();
+	this._mainPanel.items.get(1).hide();
+	this._mainPanel.ownerCt.doLayout();
+}
+
+/**
+ * Call when selection change in the grid
+ * @param Ext.grid.AbstractSelectionModel sm The selection model
+ * @private
+ */
+org.ametys.sms.tool.PhoneNumbersTool.prototype._onSelectionChange = function (sm)
+{
+	if (org.ametys.sms.tool.PhoneNumbersTool._idList != null)
+	{
+		var target = org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget("sms-list", {'id': org.ametys.sms.tool.PhoneNumbersTool._idList});
+		
+		var records = sm.getSelections();
+		var subtargets = target.getSubTargets();
+		for (var i=0; i &lt; records.length; i++)
+		{
+			subtargets.push(org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget("sms-list-phone-number", {'phoneNumber': records[i].data.number}));
+		}
+		
+		var message = org.ametys.messagebus.bus.MessageBuilder.getInstance().createMessage(org.ametys.ribbon.RibbonManager.EVENTTYPE_SELECTIONCHANGED, null, target != null ? [target] : []);
+		org.ametys.messagebus.MessageBus.getInstance().fireMessages([message]);
+	}
+	
+}
+
+/**
+ * Render a date time
+ * @static
+ */
+org.ametys.sms.tool.PhoneNumbersTool.prototype.renderDate = function (value, p, record)
+{
+	var dateFormat = "<i18n:text i18n:key="PLUGINS_SMS_NUMBER_UITOOL_DATE_FORMAT"/>";
+	return Ext.util.Format.date(value, dateFormat);
+}
Index: main/plugin-sms/resources/js/org/ametys/sms/SendSMSAction.i18n.js
===================================================================
--- main/plugin-sms/resources/js/org/ametys/sms/SendSMSAction.i18n.js	(working copy)
+++ main/plugin-sms/resources/js/org/ametys/sms/SendSMSAction.i18n.js	(working copy)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2011 Anyware Services
+ *  Copyright 2014 Anyware Services
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -14,246 +14,179 @@
  *  limitations under the License.
  */
 
-Ext.namespace('org.ametys.sms');
-
-org.ametys.sms.SendSmsDialog = function(config, pluginName)
-{
-    this._pluginName = pluginName;
-    this._formPanel = this._createFormPanel();
-    
-    var okCallback = config.okCallback;
-    var cancelCallback = config.cancelCallback;
-    
-    config = Ext.applyIf({
-        layout: 'fit',
-        
-        title: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_TITLE'/>",
-        icon: getPluginResourcesUrl(pluginName) + '/img/sms-phone-16.png',
-        
-        width: 425,
-        height: 420,
-        autoScroll: false,
-        
-        items: [ this._formPanel ],
-        
-        closeAction: 'hide',
-        buttons: [{
-            text: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_BUTTON_OK'/>",
-            handler: okCallback
-        }, {
-            text: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_BUTTON_CANCEL'/>",
-            handler: cancelCallback
-        }]        
-    }, config);
-    
-    org.ametys.sms.SendSmsDialog.superclass.constructor.call(this, config);
-};
-
-Ext.extend(org.ametys.sms.SendSmsDialog, org.ametys.DialogBox, {});
+Ext.namespace('org.ametys.sms.SendSmsDialog');
 
 /** The plugin name. */
-org.ametys.sms.SendSmsDialog.prototype._pluginName;
-/** The form panel containing the items. */
-org.ametys.sms.SendSmsDialog.prototype._formPanel;
-
-org.ametys.sms.SendSmsDialog.prototype._createFormPanel = function()
-{
-    var formPanel = new Ext.form.FormPanel({
-        labelWidth: 120,
-        defaultType: 'textfield',
-        
-        items: [
-            new org.ametys.HtmlContainer({
-                id: 'sms-hint',
-                    html: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_HINT'/>",
-                cls: 'dialog-text-hint'
-            }),
-            new org.ametys.form.TextAreaField({
-                id: 'sms-message',
-                name: 'message',
-                fieldLabel: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_FIELD_MESSAGE_LABEL'/>",
-                desc: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_FIELD_MESSAGE_DESCRIPTION'/>",
-                maxLength: 160,
-                width: 220,
-                height: 100
-            }),
-            new org.ametys.form.TextAreaField({
-                id: 'sms-phone-numbers',
-                name: 'phone-numbers',
-                fieldLabel: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_FIELD_PHONE_NUMBERS_LABEL'/>",
-                desc: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_FIELD_PHONE_NUMBERS_DESCRIPTION'/>",
-                width: 220,
-                height: 150
-            }),
-            new Ext.Button({
-                text: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_BUTTON_IMPORT'/>",
-                handler: this.importNumbers,
-                scope: this,
-                style: 'position:absolute; right:48px'
-            })
-        ],
-        
-        bodyStyle: 'padding:10px',
-        autoScroll: true
-    });
-    
-    return formPanel;
-}
-
-org.ametys.sms.SendSmsDialog.prototype.getFormPanel = function()
-{
-    return this._formPanel;
-}
-
-org.ametys.sms.SendSmsDialog.prototype.getForm = function()
-{
-    return this._formPanel.getForm();
-}
+org.ametys.sms.SendSmsDialog._pluginName;
 
-org.ametys.sms.SendSmsDialog.prototype.emptyForm = function()
+org.ametys.sms.SendSmsDialog = function(id, config, pluginName)
 {
-    var form = this._formPanel.getForm();
-    form.findField('sms-message').clearInvalid();
-    form.findField('sms-message').setValue('');
-    form.findField('sms-phone-numbers').clearInvalid();
-    form.findField('sms-phone-numbers').setValue('');
+	org.ametys.sms.SendSmsDialog._pluginName = pluginName;
+	org.ametys.sms.SendSmsDialog._delayedInitialized();
+	org.ametys.sms.SendSmsDialog._box.show();
 }
 
-org.ametys.sms.SendSmsDialog.prototype.importNumbers = function(button, event)
-{
-    if (this._importDialog == null)
-    {
-        this._importDialog = this._createImportDialogBox(this._pluginName);
-    }
-    
-    this._importDialog.show();
-}
+org.ametys.sms.SendSmsDialog._delayedInitialized = function()
+{	
+	if (!org.ametys.sms.SendSmsDialog._box)
+	{
+		var maxLength = 160;
+		
+		function updateSMSTextCounter()
+		{
+			var length = this.getValue().length;
+			
+			var counter = document.getElementById(this.id + "-counter-val");
+			counter.innerHTML = length;
+			
+			if (length > maxLength)
+			{
+				counter.style.color = "#ff0000";
+			}
+			else
+			{
+				counter.style.color = "";
+			}
+		}
+		
+		org.ametys.sms.SendSmsDialog._textMessage = new org.ametys.form.TextAreaField({
+            id: 'sms-message',
+            name: 'message',
+            fieldLabel: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_FIELD_MESSAGE_LABEL'/>",
+            desc: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_FIELD_MESSAGE_DESCRIPTION'/>",
+            maxLength: maxLength,
+            width: 220,
+            height: 100,
+    	    allowBlank: false,
+    	    enableKeyEvents: true,
+    	    
+    	    listeners: {
+    	    	'render': function() {
+    	    		(this.el.next() || this.el).insertSibling(
+    	    				{
+    	    					id: this.id + '-counter',
+    	    					tag:'div',
+    	    					style: 'margin-left: 7px; padding: 2px; font-size: 0.8em; background-color: #CCCCCC; clear: both; width:' + (this.initialConfig.width - 4) + 'px',
+    	    					html: '&lt;span style="padding-left: 5px; padding-right: 5px; color: #222222;"&gt;'
+    	    						 +    '<i18n:text i18n:key="WORKSPACE_CMS_TOOLS_WIDGET_CARACTERS_COUNTER_1" i18n:catalogue="workspace.cms"/> '
+    	    						 +    '&lt;span id="' + this.id + '-counter-val' + '"&gt;0&lt;/span&gt;'
+    	    						 +    (maxLength == null ? '' : (' <i18n:text i18n:key="WORKSPACE_CMS_TOOLS_WIDGET_CARACTERS_COUNTER_2" i18n:catalogue="workspace.cms"/> ' + maxLength))
+    	    						 + '&lt;/span&gt;'
+    	    				}, 
+    	    				'after');
+    	    	},
+    	    	'change': updateSMSTextCounter,
+    	    	'keyup': updateSMSTextCounter
+    	    }
+        });
+		
+		org.ametys.sms.SendSmsDialog._comboList = new org.ametys.form.ComboField({
+    	    id: 'sms-numbers-list',
+    	    name: 'numbers-list',
+    	    fieldLabel: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_FIELD_LIST_LABEL'/>",
+    	    desc: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_FIELD_LIST_DESCRIPTION'/>",
+    	    store: new Ext.data.XmlStore({
+    	        url: getPluginDirectUrl(org.ametys.sms.SendSmsDialog._pluginName) + '/sms-list',
+    	        record: 'list',
+    	        autoLoad: true,
+    	        baseParams: {'site': context.parameters.siteName, 'lang': 'fr'},
+    	        fields: [
+    	            {name: 'value', mapping: '@id'},
+    	            {name: 'label', mapping: 'label'}
+    	        ]
+    	    }),
+    	    minChars: 1,
+    	    valueField: 'value',
+    	    displayField: 'label',
+    	    triggerAction: 'all',
+    	    allowBlank: false,
+    	    mode: 'local',
+    	    forceSelection: true,
+    	    width: 220,
+    	    height: 150
+    	});
+		
+		org.ametys.sms.SendSmsDialog._formPanel = new Ext.form.FormPanel({
+	        labelWidth: 120,
+	        defaultType: 'textfield',
+	        
+	        items: [
+	            new org.ametys.HtmlContainer({
+	                id: 'sms-hint',
+	                html: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_HINT'/>",
+	                cls: 'dialog-text-hint'
+	            }),
+	            org.ametys.sms.SendSmsDialog._comboList,
+	        	org.ametys.sms.SendSmsDialog._textMessage,
+	        	new org.ametys.HtmlContainer({
+	                id: 'sms-hint2',
+	                html: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_HINT2'/>",
+	                cls: 'dialog-text-hint'
+	            })
+	        ],
+	        
+	        bodyStyle: 'padding:10px',
+	        autoScroll: true
+	    });
+		
+		org.ametys.sms.SendSmsDialog._box = new org.ametys.DialogBox({
+	        layout: 'fit',
+	        title: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_TITLE'/>",
+	        icon: getPluginResourcesUrl(org.ametys.sms.SendSmsDialog._pluginName) + '/img/sms-phone-16.png',        
+	        width: 435,
+	        height: 342,
+	        autoScroll: false,	        
+	        items: [ org.ametys.sms.SendSmsDialog._formPanel ],        
+	        closeAction: 'hide',
+	        buttons: [{
+	            text: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_BUTTON_OK'/>",
+	            handler: org.ametys.sms.SendSmsDialog._send
+	        }, {
+	            text: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_BUTTON_CANCEL'/>",
+	            handler: org.ametys.sms.SendSmsDialog._cancel
+	        }]        
+	    });
+	}
+};
 
-org.ametys.sms.SendSmsDialog.prototype._createImportDialogBox = function(pluginName)
+org.ametys.sms.SendSmsDialog._cancel = function()
 {
-    var formPanel = new Ext.form.FormPanel( {
-        labelWidth: 60,
-        bodyStyle: 'padding:5px',
-        fileUpload: true,
-        
-        defaultType: 'textfield',
-        
-        autoScroll: true,
-        items: [
-            new org.ametys.HtmlContainer({
-                id: 'importCsvHint',
-                html: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_IMPORT_HINT'/>",
-                cls: 'dialog-text-hint'
-            }),
-            new org.ametys.form.FileUploadField({
-                id: 'importFile',
-                name: 'importFile',
-                fieldLabel: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_IMPORT_FILE_LABEL'/>",
-                desc: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_IMPORT_FILE_DESC'/>",
-                width: 300,
-                allowBlank: false,
-                emptyText: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_IMPORT_FILE_EMPTY_TEXT'/>",
-                buttonText: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_IMPORT_FILE_BUTTON'/>"
-            })
-        ]
-    });
-    
-    var dialog = new org.ametys.DialogBox({
-        layout: 'fit',
-        
-        title: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_IMPORT_TITLE'/>",
-        icon: getPluginResourcesUrl(pluginName) + '/img/sms-phone-16.png',
-        
-        width: 450,
-        height: 200,
-        autoScroll: false,
-        
-        items: [formPanel],
-        
-        defaultButton: formPanel.getForm().findField('importFile'),
-        closeAction: 'hide',
-        buttons: [{
-            text: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_IMPORT_BUTTON_OK'/>",
-            handler: this.importNumbersOk,
-            scope: this
-        }, {
-            text: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_IMPORT_BUTTON_CANCEL'/>",
-            handler: this.importNumbersCancel,
-            scope: this
-        }]        
-    });
-    
-    return dialog;
+	org.ametys.sms.SendSmsDialog._box.hide();
 }
 
-org.ametys.sms.SendSmsDialog.prototype.importNumbersOk = function(button, event)
+org.ametys.sms.SendSmsDialog._send = function()
 {
-    var form = this._importDialog.items.itemAt(0).getForm();
-    
-    if (form.isValid())
-    {
-        form.submit({
-            url: getPluginDirectUrl(this._pluginName) + '/read-phone-file',
-            params: {siteName: context.parameters.siteName},
-            
-            waitTitle: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_IMPORT_WAIT_TITLE'/>",
-            waitMsg: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_IMPORT_WAIT_MSG'/>",
-            
-            success: this.importNumbersSuccess,
-            failure: this.importNumbersFailure,
-            scope: this,
-            
-            errorReader: new Ext.data.XmlReader({
-                record: 'field',
-                success: '@success'
-            })
-        });
-    }
+	if (org.ametys.sms.SendSmsDialog._formPanel.getForm().isValid())
+	{
+		var params = {};
+	
+		params.message = org.ametys.sms.SendSmsDialog._textMessage.getValue();
+		params.phoneList = org.ametys.sms.SendSmsDialog._comboList.getValue();
+	
+		var serverMessage = new org.ametys.servercomm.ServerMessage(org.ametys.sms.SendSmsDialog._pluginName, '/send-sms', params, org.ametys.servercomm.ServerComm.PRIORITY_MAJOR, org.ametys.sms.SendSmsDialog._sendCB, this, []);
+		org.ametys.servercomm.ServerComm.getInstance().send(serverMessage);
+		
+		org.ametys.sms.SendSmsDialog._box.showWaitMsg("<i18n:text i18n:key='PLUGINS_SMS_SEND_WAITING_MSG'/>");
+	}
+	else
+	{
+		Ext.Msg.show({
+			   title: "<i18n:text i18n:key='PLUGINS_SMS_SEND_FORM_ERROR_TITLE'/>",
+			   msg: "<i18n:text i18n:key='PLUGINS_SMS_SEND_FORM_ERROR_MESSAGE'/>", 
+			   buttons: Ext.Msg.OK,
+			   icon: Ext.MessageBox.ERROR
+		});
+	}
 }
 
-org.ametys.sms.SendSmsDialog.prototype.importNumbersCancel = function(button, event)
+org.ametys.sms.SendSmsDialog._sendCB = function(response, args)
 {
-    this._importDialog.hide();
-}
+	org.ametys.sms.SendSmsDialog._box.hideWaitMsg();
 
-org.ametys.sms.SendSmsDialog.prototype.importNumbersSuccess = function(form, action)
-{
-    var phoneNumbers = action.result.content;
-    
-    while (phoneNumbers.indexOf(',') > -1)
-    {
-        phoneNumbers = phoneNumbers.replace(',', '\n');
-    }
-    
-    var form = this._formPanel.getForm();
-    
-    form.findField('sms-phone-numbers').setValue(phoneNumbers);
-    
-    this._importDialog.hide();
-}
+	if (org.ametys.servercomm.ServerComm.handleBadResponse("<i18n:text i18n:key="PLUGINS_SMS_SEND_ERROR"/>", response, "org.ametys.sms.SendSmsDialog"))
+	{
+		return;
+	}
 
-org.ametys.sms.SendSmsDialog.prototype.importNumbersFailure = function(form, action)
-{
-    var message = '';
-    var error = action.result.error;
-    
-    if (error == 'rejected-file')
-    {
-        message = "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_IMPORT_ERROR_REJECTED_FILE'/>";
-    }
-    else if (error == 'not-text-file')
-    {
-        message = "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_IMPORT_ERROR_NOT_TEXT'/>";
-    }
-    else
-    {
-        message = "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_IMPORT_ERROR_UNKNOWN'/>";
-    }
-    
-    Ext.Msg.show({
-        title: "<i18n:text i18n:key='PLUGINS_SMS_SEND_DIALOG_IMPORT_ERROR_TITLE'/>",
-        msg: message,
-        buttons: Ext.Msg.OK,
-        icon: Ext.MessageBox.ERROR
-    });
+	org.ametys.sms.SendSmsDialog._box.hide();
 }
Index: main/plugin-sms/resources/css/smslist-tool.css
===================================================================
--- main/plugin-sms/resources/css/smslist-tool.css	(revision 0)
+++ main/plugin-sms/resources/css/smslist-tool.css	(revision 0)
@@ -0,0 +1,72 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+.smslist_tool_form .x-panel-body{
+    background-color: #eaeaea;
+    color:#404040;
+    padding: 1px 0 1px 0;
+}
+
+.smslist_tool_form.x-form-item {
+    font-family: Arial, sans-serif !important;
+    font-size: 11px;
+    margin-bottom: 2px !important;
+}
+
+.smslist_tool_form .x-form-field-trigger-wrap,
+.smslist_tool_form .x-form-trigger,
+.smslist_tool_form .x-form-field 
+{
+    vertical-align: top
+}
+
+.smslist_tool_form label, .smslist_tool_form input {
+    color:#404040;
+    font-size: 11px !important;
+    font-family: Arial, sans-serif !important;
+}
+
+.smslist_tool_form label {
+    padding-left:5px !important;
+    float: left;
+    padding-top: 5px;
+}
+
+.smslist-subscribers-tool .x-panel-body {
+    background-color:#FFFFFF !important;
+    font-family:Arial;
+    font-size:11px;
+}
+
+.smslist-subscribers-no-selection .x-panel-body {
+    padding: 10px;
+}
+
+div.ametys-form-widget-wrap input.x-form-text.ametys-form-widget-text {
+    visibility: hidden;
+    position: absolute !important;
+    top: -10000px;
+    left: -10000px;
+}
+
+.frequency {
+    font-style:italic;
+    color: #999999;
+}
+
+.auto-newsletter-dialog .hint {
+    padding: 10px 10px 0 10px;
+}
\ No newline at end of file
Index: ivy.xml
===================================================================
--- ivy.xml	(revision 26839)
+++ ivy.xml	(working copy)
@@ -40,7 +40,7 @@
     <dependency org="org.ametys.tools" name="tools" rev="1.1.8" conf="deliver->default"/>
     <dependency org="atlassian" name="clover" rev="2.6.3" conf="deliver->default"/>
     
-  	<dependency org="org.ametys.cms" name="cms" branch="3.3.x" rev="latest.integration" conf="default->default;runtime->runtime;resources->resources"/>
+  	<dependency org="org.ametys.web" name="web" branch="3.6.x" rev="latest.integration" conf="default->default;runtime->runtime;resources->resources"/>
 
     <!-- test configuration -->
     <dependency org="junit" name="junit" rev="4.4" conf="test->default"/>
