diff --git a/doc/conf/LSattribute/LSattr_html/LSattr_html_mail.docbook b/doc/conf/LSattribute/LSattr_html/LSattr_html_mail.docbook
index 9806862f..3361bc14 100644
--- a/doc/conf/LSattribute/LSattr_html/LSattr_html_mail.docbook
+++ b/doc/conf/LSattribute/LSattr_html/LSattr_html_mail.docbook
@@ -1,13 +1,51 @@
LSattr_html_mail
Ce type est utilisé pour la gestion des attributs dont la valeur est
- une adresse e-mail. Il propose directement dans l'interface, la possibilité
- d'envoyer des mails à l'adresse saisie.
+ une adresse e-mail. Il offre les fonctionnalités suivantes :
+
+ la possibilité d'envoyer des mails directement depuis l'interface
+ de l'application ;
+ l'autocomplétion lors de la saisie d'une adresse.
+
+
Structure...
array(
- 'disableMailSending' => [booléen]
+ 'disableMailSending' => [booléen],
+
+ // Autocomplétion pour un type d'LSobject donné
+ 'autocomplete' => array (
+ 'object_type' => '[Type d'LSobject]',
+ 'mail_attributes' => array (
+ 'mail',
+ 'mailAlternateAddress',
+ [...]
+ ),
+ 'filter' => '[filtre LDAP]',
+ 'basedn' => '[base DN spécifique]',
+ 'scope' => '[scope de recherche]',
+ 'displayFormat' => '[LSformat]',
+ 'onlyAccessible' => [booléen],
+ ),
+
+ // Autocomplétion sur la base d'une recherche LDAP brute
+ 'autocomplete' => array (
+ 'mail_attributes' => array (
+ 'mail',
+ 'mailAlternateAddress',
+ [...]
+ ),
+ 'filter' => '[filtre LDAP]',
+ 'basedn' => '[base DN spécifique]',
+ 'scope' => '[scope de recherche]',
+ 'displayFormat' => '[LSformat]',
+ 'onlyAccessible' => [booléen],
+ ),
+
+ // Autocomplétion (par défaut)
+ 'autocomplete' => true,
+
),]]>
...
@@ -25,9 +63,89 @@
+
+ autocomplete
+
+ Paramètrage de l'autocomplétion des adresses mails saisies : Il peut s'agir
+ d'un tableau configurant les paramètres de l'autocomplétion ou simplement
+ true pour activer l'autocomplétion par défaut, c'est à dire la recherche brute
+ dans l'annuaire de n'importe quel objet ayant l'attribut mail.
+ En cas de configuration avancée, il est possible de faire une recherche :
+
+ Sur la base d'un type d'&LSobject; donné : l'autocomplétion se fera
+ alors comme n'importe quelle recherche d'un type d'objet donné.
+ Sur la base d'une recherche brute dans l'annuaire : l'autocomplétion se
+ fera alors sur la valeur de l'adresse mail recherchée et au travers une recherche brute dans
+ l'annuaire sur n'importe quels objets ayant une adresse email correspondant.
+
+
+ Les paramètres associés à ces deux cas de figure sont décrits ci-dessous :
+
+
+
+ object_type
+
+ Le type d'&LSobject; recherché.
+
+
+
+
+ mail_attributes
+
+ Le(s) nom de l'attribut stockant les adresses emails recherchées. Il peut s'agir d'une chaîne
+ de caractères ou d'un tableau s'il y a plusieurs attributs.
+
+
+
+
+ filter
+
+ Un filtre de recherche falcultatif venant en plus de celui calculé automatiquement à partir
+ du mot clé de recherche.
+
+
+
+
+ basedn
+
+ Le basedn de la recherche. Paramètre
+ facultatif.
+
+
+
+
+ scope
+
+ Le scope de la recherche. Paramètre
+ facultatif, par défaut : sub.
+
+
+
+
+ displayFormat
+
+ Le &LSformat; d'affichage des objets trouvés. Ce paramètre est facultatif et par défaut,
+ il s'agira du format d'affichage propre au type d'&LSobject; (si défini) et à défaut, l'adresse
+ mail trouvée sera affichée.
+
+
+
+
+ onlyAccessible
+
+ Booléen falcultatif définissant si seul les &LSobjects; auxquels l'utilisateur connecté à accès
+ doivent être considérés comme sélectionnables (Faux par défaut). Ce paramètre n'est appliqué que dans
+ le cas d'une recherche pour un type d'&LSobject; donné.
+
+
+
+
+
+
+
- Ce type d'attribut HTML est dérivé du type
+ Ce type d'attribut HTML est dérivé du type
text. Il profite donc de toutes
les fonctionnalités d'un champ de ce type (autogénération, ...).
diff --git a/public_html/css/default/LSformElement_mail.css b/public_html/css/default/LSformElement_mail.css
new file mode 100644
index 00000000..fb9c014c
--- /dev/null
+++ b/public_html/css/default/LSformElement_mail.css
@@ -0,0 +1,29 @@
+ul.LSformElement_mail_autocomplete {
+ border: 1px solid #ccc;
+ width: 200px;
+ margin: 0;
+ margin-top: 0.1em;
+ max-height: 10em;
+ overflow: auto;
+ padding: 0;
+ list-style-type: none;
+}
+
+li.LSformElement_mail_autocomplete {
+ cursor: pointer;
+ border-bottom: 1px dotted #ccc;
+ font-size: 0.8em;
+}
+
+li.LSformElement_mail_autocomplete:last-of-type {
+ border: none;
+}
+
+li.LSformElement_mail_autocomplete_over {
+ background-color: #ccc;
+}
+
+li.LSformElement_mail_autocomplete_current {
+ font-style: italic;
+ color: #777;
+}
diff --git a/public_html/includes/class/class.LSformElement_mail.php b/public_html/includes/class/class.LSformElement_mail.php
index c5c14f6f..9faee999 100644
--- a/public_html/includes/class/class.LSformElement_mail.php
+++ b/public_html/includes/class/class.LSformElement_mail.php
@@ -34,16 +34,21 @@ LSsession :: loadLSclass('LSformElement_text');
class LSformElement_mail extends LSformElement_text {
var $JSscripts = array(
+ 'LSformElement_mail_field.js',
'LSformElement_mail.js'
);
-
+
+ var $CSSfiles = array(
+ 'LSformElement_mail.css',
+ );
+
var $fetchVariables = array(
'uriClass' => 'LSformElement_mail',
'uriPrefix' => 'mailto:'
);
-
+
var $fieldTemplate = 'LSformElement_uri_field.tpl';
-
+
public function getDisplay() {
LSsession :: addHelpInfos (
'LSformElement_mail',
@@ -54,6 +59,9 @@ class LSformElement_mail extends LSformElement_text {
if (LSsession :: loadLSclass('LSmail')) {
LSmail :: loadDependenciesDisplay();
}
+ if (!$this -> isFreeze() && $this -> getParam('html_options.autocomplete')) {
+ LSsession :: addJSconfigParam('LSformElement_mail_autocomplete_noResultLabel', _('No result'));
+ }
return parent :: getDisplay();
}
@@ -61,8 +69,135 @@ class LSformElement_mail extends LSformElement_text {
if ($this -> getParam('html_options.disableMailSending', false, 'bool')) {
$this -> fetchVariables['uriClass'] .= " LSformElement_mail_disableMailSending";
}
+ if ($this -> getParam('html_options.autocomplete', false, 'bool')) {
+ $this -> fetchVariables['uriClass'] .= " LSformElement_mail_autocomplete";
+ }
return parent :: fetchTemplate($template,$variables);
}
-}
+ /**
+ * Autocomplete email
+ *
+ * @param[in] $pattern The pattern of the search
+ *
+ * @retval array(mail -> displayName) Found emails
+ */
+ public function autocomplete($pattern) {
+ $ret = array();
+ if ($this -> getParam('html_options.autocomplete')) {
+ $mail_attributes = $this -> getParam('html_options.autocomplete.mail_attributes', array('mail'));
+ if (!is_array($mail_attributes)) $mail_attributes = array($mail_attributes);
+ $obj_type = $this -> getParam('html_options.autocomplete.object_type');
+ if ($obj_type) {
+ // Search with a specific objectType
+ if (LSsession :: loadLSobject($obj_type)) {
+ $obj = new $obj_type();
+ $filters = array();
+ foreach($mail_attributes as $attr) {
+ $filters[] = Net_LDAP2_Filter::create($attr, 'present');
+ }
+ $filter = (count($filters)==1?$filters[0]:Net_LDAP2_Filter::combine('or', $filters));
+ if ($this -> getParam('html_options.autocomplete.filter')) {
+ $filter = Net_LDAP2_Filter::combine(
+ 'and',
+ array(
+ Net_LDAP2_Filter::parse($this -> getParam('html_options.autocomplete.filter')),
+ $filter,
+ )
+ );
+ }
+ $sparams = array(
+ 'pattern' => $pattern,
+ 'attributes' => $mail_attributes,
+ 'displayFormat' => $this -> getParam('html_options.autocomplete.display_name_format'),
+ 'filter' => $filter,
+ 'onlyAccessible' => $this -> getParam('html_options.autocomplete.onlyAccessible', false, 'bool'),
+ );
+ LSdebug($filter->as_string());
+ $search = new LSsearch(
+ $obj_type,
+ 'LSformElement_mail::autocomplete',
+ $sparams,
+ true
+ );
+ $search -> run();
+ foreach($search -> getSearchEntries() as $e) {
+ foreach($mail_attributes as $attr) {
+ $mails = $e->get($attr);
+ if (!$mails) continue;
+ if (!is_array($mails)) $mails = array($mails);
+ foreach($mails as $mail)
+ $ret[$mail] = $e->displayName;
+ }
+ }
+ }
+ }
+ else {
+ $filters = array();
+ foreach($mail_attributes as $attr) {
+ $filters[] = Net_LDAP2_Filter::create($attr, 'contains', $pattern);
+ }
+ $filter = (count($filters)==1?$filters[0]:Net_LDAP2_Filter::combine('or', $filters));
+ if ($this -> getParam('html_options.autocomplete.filter')) {
+ $filter = Net_LDAP2_Filter::combine(
+ 'and',
+ array(
+ Net_LDAP2_Filter::parse($this -> getParam('html_options.autocomplete.filter')),
+ $filter,
+ )
+ );
+ }
+
+ $displayNameFormat = $this -> getParam('html_options.autocomplete.display_name_format', false);
+ $attributes = $mail_attributes;
+ if ($displayNameFormat)
+ foreach(getFieldInFormat($displayNameFormat) as $attr)
+ if(!in_array($attr, $attributes))
+ $attributes[] = $attr;
+
+ $objects = LSldap :: search (
+ $filter,
+ $this -> getParam('html_options.autocomplete.basedn', null),
+ array (
+ 'attributes' => $attributes,
+ 'scope' => $this -> getParam('html_options.autocomplete.scope', 'sub'),
+ )
+ );
+
+ if (is_array($objects)) {
+ foreach($objects as $object) {
+ $displayName = ($displayNameFormat?getFData($displayNameFormat, $object['attrs']):null);
+ foreach($mail_attributes as $attr) {
+ if (!isset($object['attrs'][$attr])) continue;
+ $mails = $object['attrs'][$attr];
+ if (!is_array($mails)) $mails = array($mails);
+ foreach($mails as $mail)
+ $ret[$mail] = ($displayName?$displayName:$mail);
+ }
+ }
+ }
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * This ajax method is used by the autocomplete function of the form element.
+ *
+ * @param[in] $data The address to the array of data witch will be return by the ajax request
+ *
+ * @retval void
+ **/
+ public static function ajax_autocomplete(&$data) {
+ if ((isset($_REQUEST['attribute'])) && (isset($_REQUEST['objecttype'])) && (isset($_REQUEST['pattern'])) && (isset($_REQUEST['idform'])) ) {
+ if (LSsession ::loadLSobject($_REQUEST['objecttype'])) {
+ $object = new $_REQUEST['objecttype']();
+ $form = $object -> getForm($_REQUEST['idform']);
+ $field=$form -> getElement($_REQUEST['attribute']);
+ $data['mails'] = $field -> autocomplete($_REQUEST['pattern']);
+ }
+ }
+ }
+
+}
diff --git a/public_html/includes/js/LSformElement_mail.js b/public_html/includes/js/LSformElement_mail.js
index 35678dd1..9edc8aeb 100644
--- a/public_html/includes/js/LSformElement_mail.js
+++ b/public_html/includes/js/LSformElement_mail.js
@@ -1,18 +1,19 @@
var LSformElement_mail = new Class({
initialize: function(){
+ this.fields = [];
this.initialiseLSformElement_mail();
if (typeof(varLSform) != "undefined") {
varLSform.addModule("LSformElement_mail",this);
}
this.LSmail_open = 0;
},
-
+
initialiseLSformElement_mail: function(el) {
if (typeof(el) == 'undefined') {
el = document;
}
el.getElements('input.LSformElement_mail').each(function(input) {
- if (!input.hasClass('LSformElement_mail_disableMailSending')) {
+ if (!input.hasClass('LSformElement_mail_disableMailSending')) {
this.addBtnAfter.bind(this)(input);
}
}, this);
@@ -21,8 +22,18 @@ var LSformElement_mail = new Class({
this.addBtnAfter.bind(this)(a);
}
}, this);
+ var getName = /^(.*)\[\]$/;
+ el.getElements('input.LSformElement_mail_autocomplete').each(function(input) {
+ this.fields.push(
+ new LSformElement_mail_field(
+ getName.exec(input.name)[1],
+ input
+ )
+ );
+ }, this);
+
},
-
+
addBtnAfter: function(el) {
var btn = new Element('img');
btn.setProperties({
@@ -33,12 +44,12 @@ var LSformElement_mail = new Class({
btn.addEvent('click',this.onBtnClick.bind(this,btn));
varLSdefault.addHelpInfo(btn,'LSformElement_mail','mail');
},
-
+
reinitialize: function(el) {
varLSform.initializeModule('LSformElement_text',el);
this.initialiseLSformElement_mail(el);
},
-
+
onBtnClick: function(btn) {
if (this.LSmail_open==0) {
var mail = btn.getParent().getFirst().innerHTML;
@@ -58,12 +69,12 @@ var LSformElement_mail = new Class({
}
}
},
-
+
onLSmailClose: function(LSmail) {
LSdebug('LSformElement_mail : close LSmail');
this.LSmail_open = 0;
},
-
+
onLSmailValid: function(LSmail) {
LSdebug('LSformElement_mail : valid LSmail');
LSmail.send();
diff --git a/public_html/includes/js/LSformElement_mail_field.js b/public_html/includes/js/LSformElement_mail_field.js
new file mode 100644
index 00000000..7579e833
--- /dev/null
+++ b/public_html/includes/js/LSformElement_mail_field.js
@@ -0,0 +1,157 @@
+var LSformElement_mail_field = new Class({
+ initialize: function(name, input){
+ this.name = name;
+ this.input = input;
+ this.ul = input.getParent('ul');
+ this.li = input.getParent('li');
+ this.keyUpTimer = null;
+ this.lastKeyUpValue = null;
+ this.lastAutocompletePattern = null;
+ this.lastAutocompleteMails = null;
+ this.initialiseLSformElement_mail_field();
+ },
+
+ initialiseLSformElement_mail_field: function() {
+ this.input.addEvent('keyup',this.onKeyUp.bindWithEvent(this));
+ this.input.addEvent('keydown',this.onKeyDown.bindWithEvent(this));
+ },
+
+ onKeyDown: function(event) {
+ event = new Event(event);
+ if (event.key=='tab' && this.input.value) {
+ event.stop();
+ if (this.keyUpTimer) {
+ clearTimeout(this.keyUpTimer);
+ }
+ this.launchAutocomplete(this.input.value);
+ }
+ },
+
+ onKeyUp: function(event) {
+ this.lastKeyUpValue = this.input.value;
+ if (this.keyUpTimer) {
+ clearTimeout(this.keyUpTimer);
+ }
+ if (this.lastKeyUpValue) {
+ this.keyUpTimer = this.onkeyUpTimeout.delay(800, this);
+ }
+ },
+
+ onkeyUpTimeout: function() {
+ this.keyUpTimer = null;
+ if (this.lastKeyUpValue == this.input.value) {
+ this.launchAutocomplete(this.input.value);
+ }
+ },
+
+ launchAutocomplete: function(pattern) {
+ if (this.lastAutocompletePattern == pattern) {
+ if (!this.autocompleteIsOpen()) this.showAutocompleteMails();
+ return true;
+ }
+ this.input.set('disabled', 'disabled');
+ this.lastAutocompletePattern=pattern;
+ var data = {
+ template: 'LSformElement_mail',
+ action: 'autocomplete',
+ attribute: this.name,
+ objecttype: varLSform.objecttype,
+ idform: varLSform.idform,
+ pattern: pattern
+ };
+ data.imgload=varLSdefault.loadingImgDisplay(this.input);
+ new Request({url: 'index_ajax.php', data: data, onSuccess: this.onAutocompleteComplete.bind(this)}).send();
+ },
+
+ onAutocompleteComplete: function(responseText, responseXML) {
+ var data = JSON.decode(responseText);
+ this.input.erase('disabled');
+ if ( varLSdefault.checkAjaxReturn(data) ) {
+ this.lastAutocompleteMails = new Hash(data.mails);
+ this.showAutocompleteMails();
+ }
+ },
+
+ showAutocompleteMails: function() {
+ if (!this.lastAutocompleteMails) return;
+ if (!$type(this.autocompleteUl)) {
+ this.autocompleteUl = new Element('ul');
+ this.autocompleteUl.addClass('LSformElement_mail_autocomplete');
+ this.autocompleteUl.injectInside(this.li);
+ document.addEvent('click', this.closeAutocompleteIfOpen.bind(this));
+ }
+ this.autocompleteUl.empty();
+ if (this.lastAutocompleteMails) {
+ this.lastAutocompleteMails.each(this.addAutocompleteLi, this);
+ }
+ this.addAutocompleteNoValueLabelIfEmpty();
+
+ this.autocompleteUl.setStyle('display','block');
+ },
+
+ addAutocompleteLi: function(name, mail) {
+ var current = 0;
+ this.ul.getElements("input").each(function(input){
+ if (input.value==mail && input != this.input) {
+ current=1;
+ }
+ },this);
+
+ var li = new Element('li');
+ li.addClass('LSformElement_mail_autocomplete');
+ li.set('data-mail', mail);
+ li.set('html', name);
+ li.addEvent('mouseenter',this.onAutocompleteLiMouseEnter.bind(this,li));
+ li.addEvent('mouseleave',this.onAutocompleteLiMouseLeave.bind(this,li));
+ if (current) {
+ li.addClass('LSformElement_mail_autocomplete_current');
+ }
+ else {
+ li.addEvent('click',this.onAutocompleteLiClick.bind(this,li));
+ }
+ li.injectInside(this.autocompleteUl);
+ },
+
+ addAutocompleteNoValueLabelIfEmpty: function() {
+ if (this.autocompleteUl.getElement('li') == null) {
+ var li = new Element('li');
+ li.addClass('LSformElement_mail_autocomplete');
+ li.set('html', varLSdefault.LSjsConfig['LSformElement_mail_autocomplete_noResultLabel']);
+ li.injectInside(this.autocompleteUl);
+ }
+ },
+
+ onAutocompleteLiMouseEnter: function(li) {
+ li.addClass('LSformElement_mail_autocomplete_over');
+ },
+
+ onAutocompleteLiMouseLeave: function(li) {
+ li.removeClass('LSformElement_mail_autocomplete_over');
+ },
+
+ onAutocompleteLiClick: function(li) {
+ this.closeAutocomplete();
+ if (li.get('data-mail')) {
+ this.input.value = li.get('data-mail');
+ }
+ },
+
+ autocompleteIsOpen: function() {
+ return ($type(this.autocompleteUl) == 'element' && this.autocompleteUl.getStyle('display') != 'none');
+ },
+
+ closeAutocomplete: function() {
+ if (!this.autocompleteIsOpen()) return true;
+ this.autocompleteUl.setStyle('display', 'none');
+ },
+
+ closeAutocompleteIfOpen: function(event) {
+ event = new Event(event);
+ if (!this.autocompleteIsOpen())
+ return true;
+ if (event.target==this.input || event.target==this.autocompleteUl)
+ return true;
+ this.closeAutocomplete();
+ },
+
+});