Browse Source

Suite de la refonte de la messagerie :

- la base évolue pour inclure un champ 'destinataires' qui conserve une trace en liste texte separee par des virgules des id_auteur ou email des destinataires. Permet notamment de le memoriser sur les messages en statut prepa
- gestion complète de l'envoi/modification des messages, penses-betes et annonce
- notification des auteurs qui ont une adresse mail, en complement de la diffusion du message dans la messagerie interne
- l'envoi de messages vers des email externes (email comme destinatire) est egalement possible si la configuration est activee (le form de config est à completer)
- gestion des messages lus, des alertes reception message
- choix des destinataires par saisie libre+autocompletion (mix entre code du selecteur generique et du plugin messagerie)
- gestion des messages lus

Il reste principalement :
- la gestion de la suppression des messages
- l'interface de saisie des messages "RDV" qui comportent une date de debut+date de fin
svn/root/tags/plugins/organiseur/0.5.0 v0.5.0
cedric@yterium.com 11 years ago
parent
commit
f1519e31fa
  1. 12
      .gitattributes
  2. 202
      action/editer_message.php
  3. 26
      action/quete_autocomplete.php
  4. 4
      base/organiseur.php
  5. 61
      formulaires/editer_message.html
  6. 39
      formulaires/editer_message.js
  7. 114
      formulaires/editer_message.php
  8. 13
      formulaires/inc-destinataires-message.html
  9. 175
      inc/messages.php
  10. 808
      javascript/jquery.autocomplete.js
  11. 23
      lang/organiseur_fr.php
  12. 61
      notifications/instituermessage.php
  13. 21
      notifications/message_affich_publie.html
  14. 24
      notifications/message_normal_publie.html
  15. 61
      organiseur_administrations.php
  16. 24
      organiseur_autoriser.php
  17. 11
      organiseur_fonctions.php
  18. 39
      organiseur_pipelines.php
  19. 11
      paquet.xml
  20. 9
      prive/objets/contenu/message.html
  21. 7
      prive/objets/liste/messages-envoyes.html
  22. 2
      prive/objets/liste/messages-recus.html
  23. 10
      prive/squelettes/contenu/message.html
  24. 24
      prive/squelettes/contenu/message_edit.html
  25. 12
      prive/squelettes/contenu/messages.html
  26. 5
      prive/squelettes/inclure/organiseur-autocomplete-auteur.html
  27. 2
      prive/squelettes/inclure/organiseur-envoi.html
  28. 2
      prive/squelettes/inclure/organiseur-interventions.html
  29. 10
      prive/squelettes/inclure/organiseur-reception.html
  30. 29
      prive/squelettes/navigation/messages.html
  31. 21
      prive/style_prive_plugin_organiseur.html

12
.gitattributes vendored

@ -1,5 +1,5 @@
* text=auto !eol
action/editer_message.php -text
action/quete_autocomplete.php -text
action/quete_calendrier.php -text
base/organiseur.php -text
/calendrier.css -text
@ -10,8 +10,13 @@ exec/message.php -text
exec/message_edit.php -text
formulaires/configurer_messagerie_agenda.html -text
formulaires/configurer_messagerie_agenda.php -text
formulaires/editer_message.html -text
formulaires/editer_message.js -text
formulaires/editer_message.php -text
formulaires/inc-destinataires-message.html -text
inc/messages.php -text
inc/quete_calendrier.php -text
javascript/jquery.autocomplete.js -text
lang/organiseur.xml -text
lang/organiseur_ar.php -text
lang/organiseur_de.php -text
@ -22,6 +27,9 @@ lib/fullcalendar/fullcalendar.js -text
lib/fullcalendar/fullcalendar.min.js -text
lib/fullcalendar/fullcalendar.print.css -text
lib/fullcalendar/gcal.js -text
notifications/instituermessage.php -text
notifications/message_affich_publie.html -text
notifications/message_normal_publie.html -text
/organiseur_administrations.php -text
/organiseur_autoriser.php -text
/organiseur_fonctions.php -text
@ -32,8 +40,10 @@ prive/objets/liste/messages-recus.html -text
prive/rss/messagerie.html -text
prive/squelettes/contenu/calendrier.html -text
prive/squelettes/contenu/message.html -text
prive/squelettes/contenu/message_edit.html -text
prive/squelettes/contenu/messages.html -text
prive/squelettes/inclure/configurer_messagerie.html -text
prive/squelettes/inclure/organiseur-autocomplete-auteur.html -text
prive/squelettes/inclure/organiseur-envoi.html -text
prive/squelettes/inclure/organiseur-interventions.html -text
prive/squelettes/inclure/organiseur-message.html -text

202
action/editer_message.php

@ -1,202 +0,0 @@
<?php
/***************************************************************************\
* SPIP, Systeme de publication pour l'internet *
* *
* Copyright (c) 2001-2011 *
* Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
* *
* Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
* Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
\***************************************************************************/
if (!defined('_ECRIRE_INC_VERSION')) return;
include_spip('inc/filtres');
// http://doc.spip.org/@action_editer_message_dist
function action_editer_message_dist($arg=null) {
if (is_null($arg)){
$securiser_action = charger_fonction('securiser_action', 'inc');
$arg = $securiser_action();
}
if (preg_match(',^(\d+)$,', $arg, $r))
action_editer_message_post_vieux($arg);
elseif (preg_match(',^-(\d+)$,', $arg, $r))
action_editer_message_post_supprimer($r[1]);
elseif (preg_match(',^(\d+)\W$,', $arg, $r))
action_editer_message_post_choisir($r[1]);
elseif (preg_match(',^(\d+)\W@(\d+)$,', $arg, $r))
action_editer_message_post_ajouter($r[1], $r[2]);
elseif (preg_match(',^(\d+)\W:(\d+)$,', $arg, $r))
action_editer_message_post_vu($r[1], $r[2]);
elseif (preg_match(',^(\d+)\W-(\d+)$,', $arg, $r))
action_editer_message_post_retirer($r[1], $r[2]);
elseif (preg_match(',^(\d+)\W(\w+)$,', $arg, $r))
action_editer_message_post_envoyer($r[1], $r[2]);
elseif (preg_match(',^(\w+)$,', $arg, $r))
action_editer_message_post_nouveau($arg);
elseif (preg_match(',^(\w+)\W(\d+)$,', $arg, $r))
action_editer_message_post_nouveau($r[1], $r[2]);
elseif (preg_match(',^(\w+)\W(\d+-\d+-\d+)$,', $arg, $r))
action_editer_message_post_nouveau($r[1], '', $r[2]);
else spip_log("action_editer_message_dist $arg pas compris");
}
// http://doc.spip.org/@action_editer_message_post_supprimer
function action_editer_message_post_supprimer($id_message) {
sql_delete("spip_messages", "id_message=".intval($id_message));
include_spip('action/editer_auteur');
auteur_dissocier('*', array('message'=>$id_message));
}
// http://doc.spip.org/@action_editer_message_post_vu
function action_editer_message_post_vu($id_message, $id_auteur) {
include_spip('action/editer_auteur');
auteur_qualifier($id_auteur,array('message'=>$id_message),array("vu" => 'oui'));
}
// http://doc.spip.org/@action_editer_message_post_retirer
function action_editer_message_post_retirer($id_message, $id_auteur) {
include_spip('action/editer_auteur');
auteur_dissocier($id_auteur, array('message'=>$id_message));
}
// http://doc.spip.org/@action_editer_message_post_ajouter
function action_editer_message_post_ajouter($id_message, $id_auteur) {
include_spip('action/editer_auteur');
auteur_associer($id_auteur, array('message'=>$id_message),array('vu'=>'non'));
// Ne pas notifier ici, car si on se trompe d'auteur, on veut avoir le temps
// de supprimer celui qu'on vient d'ajouter... c'est fait en cron
}
// http://doc.spip.org/@action_editer_message_post_choisir
function action_editer_message_post_choisir($id_message) {
if ($id_auteur = _request('nouv_auteur'))
action_editer_message_post_ajouter($id_message, $id_auteur);
else {
include_spip('inc/ressembler');
include_spip('inc/charsets'); // pour tranlitteration
$id_auteur = $GLOBALS['visiteur_session']['id_auteur'];
$cherche_auteur= _request('cherche_auteur');
$query = sql_select("id_auteur, nom", "spip_auteurs", "messagerie<>'non' AND id_auteur<>'$id_auteur' AND pass<>'' AND login<>''");
$table_auteurs = array();
$table_ids = array();
while ($row = sql_fetch($query)) {
$table_auteurs[] = $row['nom'];
$table_ids[] = $row['id_auteur'];
}
$res = mots_ressemblants($cherche_auteur, $table_auteurs, $table_ids);
$n = count($res);
if ($n == 1)
# Bingo
action_editer_message_post_ajouter($id_message, $res[0]);
# renvoyer la valeur ==> formulaire de choix si n !=1
# notification que $res[0] a ete rajoute sinon
redirige_par_entete(parametre_url(urldecode(_request('redirect')),
'cherche_auteur', $cherche_auteur, '&'));
}
}
// http://doc.spip.org/@action_editer_message_post_envoyer
function action_editer_message_post_envoyer($id_message, $statut) {
sql_updateq("spip_messages", array("statut" => $statut), "id_message=$id_message");
sql_updateq("spip_messages", array("date_heure" => date('Y-m-d H:i:s')), "id_message=$id_message AND rv<>'oui'");
}
// http://doc.spip.org/@action_editer_message_post_nouveau
function action_editer_message_post_nouveau($type, $dest='', $rv='')
{
$id_auteur = $GLOBALS['visiteur_session']['id_auteur'];
$mydate = date("YmdHis", time() - 2 * 24 * 3600);
sql_delete("spip_messages", "(statut = 'redac') AND (date_heure < $mydate)");
if ($type == 'pb') $statut = 'publie';
else $statut = 'redac';
$titre = filtrer_entites(_T('texte_nouveau_message'));
$vals = array('titre' => $titre,
'statut' => $statut,
'type' => $type,
'id_auteur' => $id_auteur);
if (!$rv)
$vals['date_heure'] = date('Y-m-d H:i:s');
else {
$vals['date_heure'] = "$rv 12:00:00";
$vals['date_fin'] = "$rv 13:00:00";
$vals['rv'] = 'oui';
}
$id_message = sql_insertq("spip_messages", $vals);
include_spip('action/editer_auteur');
if ($type != "affich"){
auteur_associer($id_auteur, array('message'=>$id_message),array('vu'=>'oui'));
if ($dest)
auteur_associer($dest, array('message'=>$id_message),array('vu'=>'non'));
}
redirige_url_ecrire('message_edit', "id_message=$id_message&new=oui&dest=$dest");
}
// http://doc.spip.org/@action_editer_message_post_vieux
function action_editer_message_post_vieux($id_message)
{
sql_updateq('spip_messages', array('titre'=>_request('titre'), 'texte' => _request('texte')), "id_message=$id_message");
sql_updateq('spip_messages', array('rv' => _request('rv')), "id_message=$id_message");
if (_request('jour'))
change_date_message($id_message, _request('heures'),_request('minutes'),_request('mois'), _request('jour'), _request('annee'), _request('heures_fin'),_request('minutes_fin'),_request('mois_fin'), _request('jour_fin'), _request('annee_fin'));
}
// Convertir dates a calendrier correct
// (exemple: 31 fevrier devient debut mars, 24h12 devient 00h12 du lendemain)
// http://doc.spip.org/@change_date_message
function change_date_message($id_message, $heures,$minutes,$mois, $jour, $annee, $heures_fin,$minutes_fin,$mois_fin, $jour_fin, $annee_fin)
{
$date = date("Y-m-d H:i:s", mktime($heures,$minutes,0,$mois, $jour, $annee));
$jour = journum($date);
$mois = mois($date);
$annee = annee($date);
$heures = heures($date);
$minutes = minutes($date);
// Verifier que la date de fin est bien posterieure au debut
$unix_debut = date("U", mktime($heures,$minutes,0,$mois, $jour, $annee));
$unix_fin = date("U", mktime($heures_fin,$minutes_fin,0,$mois_fin, $jour_fin, $annee_fin));
if ($unix_fin <= $unix_debut) {
$jour_fin = $jour;
$mois_fin = $mois;
$annee_fin = $annee;
$heures_fin = $heures + 1;
$minutes_fin = $minutes;
}
$date_fin = date("Y-m-d H:i:s", mktime($heures_fin,$minutes_fin,0,$mois_fin, $jour_fin, $annee_fin));
$jour_fin = journum($date_fin);
$mois_fin = mois($date_fin);
$annee_fin = annee($date_fin);
$heures_fin = heures($date_fin);
$minutes_fin = minutes($date_fin);
sql_updateq('spip_messages', array('date_heure'=>"$annee-$mois-$jour $heures:$minutes:00", 'date_fin'=>"$annee_fin-$mois_fin-$jour_fin $heures_fin:$minutes_fin:00"), "id_message=$id_message");
}
?>

26
action/quete_autocomplete.php

@ -0,0 +1,26 @@
<?php
/***************************************************************************\
* SPIP, Systeme de publication pour l'internet *
* *
* Copyright (c) 2001-2011 *
* Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
* *
* Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
* Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
\***************************************************************************/
if (!defined('_ECRIRE_INC_VERSION')) return;
function action_quete_autocomplete_dist(){
$securiser_action = charger_fonction('securiser_action','inc');
$arg = $securiser_action();
if ($arg
AND $arg==$GLOBALS['visiteur_session']['id_auteur']){
include_spip('inc/actions');
echo ajax_retour(
recuperer_fond('prive/squelettes/inclure/organiseur-autocomplete-auteur',array('q'=>_request('q'))),
'text/plain'
);
}
}

4
base/organiseur.php

@ -35,7 +35,7 @@ function organiseur_declarer_tables_objets_sql($tables){
'info_nb_objets' => 'info_nb_messages',
'principale' => 'oui',
'champs_editables' => array('titre', 'texte', 'type','date_heure', 'date_fin', 'rv', 'id_auteur'),
'champs_editables' => array('titre', 'texte', 'type','date_heure', 'date_fin', 'rv', 'id_auteur', 'destinataires'),
'field' => array(
"id_message" => "bigint(21) NOT NULL",
"titre" => "text DEFAULT '' NOT NULL",
@ -43,13 +43,13 @@ function organiseur_declarer_tables_objets_sql($tables){
// normal,
// pb (pense bete)
// affich (annonce publique)
// genera (message general envoye a tout le monde)
"type" => "varchar(6) DEFAULT '' NOT NULL",
"date_heure" => "datetime DEFAULT '0000-00-00 00:00:00' NOT NULL",
"date_fin" => "datetime DEFAULT '0000-00-00 00:00:00' NOT NULL",
"rv" => "varchar(3) DEFAULT '' NOT NULL",
"statut" => "varchar(6) DEFAULT '0' NOT NULL",
"id_auteur" => "bigint(21) NOT NULL",
"destinataires" => "text DEFAULT '' NOT NULL",
"maj" => "TIMESTAMP"
),
'key' => array(

61
formulaires/editer_message.html

@ -0,0 +1,61 @@
<div class="ajax formulaire_spip formulaire_editer formulaire_#FORM formulaire_#FORM-#ENV{id,nouveau}">
[<p class="reponse_formulaire reponse_formulaire_ok">(#ENV**{message_ok})</p>]
[<p class="reponse_formulaire reponse_formulaire_erreur">(#ENV*{message_erreur})</p>]
[(#ENV{editable})
<form method='post' action='#ENV{action}'><div>
[(#REM) declarer les hidden qui declencheront le service du formulaire
parametre : url d'action ]
#ACTION_FORMULAIRE{#ENV{action}}
#SET{fl,organiseur}
<ul>
[(#ENV{_destiner}|oui)
#SET{name,destinataires}#SET{obli,'obligatoire'}#SET{erreurs,#ENV**{erreurs}|table_valeur{#GET{name}}}
<li class="editer editer_[(#GET{name})][ (#GET{obli})][ (#GET{erreurs}|oui)erreur]">
<label for="#GET{name}">[(#GET{fl}|concat{':label_',#GET{name}}|_T)]</label>[
<span class='erreur_message'>(#GET{erreurs})</span>
]
#INCLURE{fond=formulaires/inc-destinataires-message,name=#GET{name},env}
</li>
]
#SET{name,titre}#SET{obli,'obligatoire'}#SET{erreurs,#ENV**{erreurs}|table_valeur{#GET{name}}}
<li class="editer editer_[(#GET{name})][ (#GET{obli})][ (#GET{erreurs}|oui)erreur]">
<label for="#GET{name}">[(#GET{fl}|concat{':label_',#GET{name}}|_T)]</label>[
<span class='erreur_message'>(#GET{erreurs})</span>
]<input type="text" class="text" name="#GET{name}" value="#ENV*{#GET{name}}" id="#GET{name}" [(#HTML5|et{#GET{obli}})required='required']/>
</li>
#SET{name,texte}#SET{obli,'obligatoire'}#SET{erreurs,#ENV**{erreurs}|table_valeur{#GET{name}}}
<li class="editer editer_[(#GET{name})][ (#GET{obli})][ (#GET{erreurs}|oui)erreur]">
<label for="#GET{name}">[(#GET{fl}|concat{':label_',#GET{name}}|_T)]</label>[
<span class='erreur_message'>(#GET{erreurs})</span>
]<textarea class="textarea" name="#GET{name}" rows="10">
#ENV*{#GET{name}}</textarea>
</li>
[(#REM) Piege a robots spammeurs ]
#SET{name,antispam}#SET{obli,''}#SET{erreurs,#ENV**{erreurs}|table_valeur{#GET{name}}}
<li class="editer none editer_[(#GET{name})][ (#GET{obli})][ (#GET{erreurs}|oui)erreur]">
<label for="nobotnobot-#ID"><:antispam_champ_vide:></label>[
<span class='erreur_message'>(#GET{erreurs})</span>
]<input type="text" class="text" name="#GET{name}" value="#ENV*{#GET{name}}" id="nobotnobot-#ID" />
</li>
</ul>
<!--extra-->
<p class='boutons'><span class='image_loading'>&nbsp;</span>
[(#ENV{statut}|=={publie}|non)
<input type='submit' class='submit' name="draft" value='<:organiseur:bouton_enregistrer_brouillon:>' />
]
<input type='submit' class='submit' name="send" value='<:organiseur:bouton_envoyer_message:>' />
</p>
</div></form>
]
</div>
[(#ENV{_destiner}|oui)
<script type="text/javascript">
if (typeof formulaire_editer_message_init=="undefined"){
var formulaire_editer_message_init=''; // eviter double dl si plusieurs forms dans la page
var url_trouver_destinataire = '[(#URL_ACTION_AUTEUR{quete_autocomplete,#SESSION{id_auteur}}|replace{&amp;,&})]';
jQuery.getScript('#CHEMIN{javascript/jquery.autocomplete.js}',function(){
jQuery.getScript('#CHEMIN{formulaires/editer_message.js}');
});
}
</script>
]

39
formulaires/editer_message.js

@ -0,0 +1,39 @@
function formulaire_editer_message_set_dest(input,data,value){
console.log(data);
console.log(value);
var id_auteur;
var box = jQuery(input).siblings('.selected');
if (data[1]) {
id_auteur = data[1];
var nom = value;
if (box.find('input[value='+id_auteur+']').length==0){
box.find('.on').removeClass('on');
box.append(" <span class='dest on'>"
+ value
+"<input type='hidden' name='"
+ jQuery(input).attr('data-name')
+ "' value='"+id_auteur+"' /> "
+ $(box).find('span.dest:first').html()
+"</span>");
}
else
box.find('input[value='+id_auteur+']').closest('span').addClass('on').siblings('.on').removeClass('on');
}
jQuery(input).attr('value','');//.get(0).focus();
}
function formulaire_editer_message_init(){
jQuery("input.destinataires:not(.autocompleted)").each(function(){
var me = this;
jQuery(me)
.autocomplete(url_trouver_destinataire, {minChars:2, mustMatchOrEmpty:1,autoFill:true,matchSubset:0, matchContains:1, cacheLength:10 })
.bind('result',function(e,data,value){return formulaire_editer_message_set_dest(me,data,value);})
.parent().bind('click',function(){jQuery(me).get(0).focus();});
})
.addClass('autocompleted');
}
if (window.jQuery){
jQuery(function(){
formulaire_editer_message_init();
onAjaxLoad(formulaire_editer_message_init);
});
}

114
formulaires/editer_message.php

@ -0,0 +1,114 @@
<?php
/***************************************************************************\
* SPIP, Systeme de publication pour l'internet *
* *
* Copyright (c) 2001-2011 *
* Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
* *
* Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
* Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
\***************************************************************************/
if (!defined('_ECRIRE_INC_VERSION')) return;
include_spip('inc/editer');
function formulaires_editer_message_charger_dist($id_message='new',$type='message',$retour='',$accepter_email='oui',$destinataires='',$titre='',$texte=''){
if (!autoriser('envoyermessage',$type))
return false;
$valeurs = formulaires_editer_objet_charger('message',$id_message,0,0,$retour,'');
// les destinataires sont stockes en chaine separe par une virgule dans la base
if (strlen($valeurs['destinataires']))
$valeurs['destinataires'] = explode(",",$valeurs['destinataires']);
if (!intval($id_message)){
$valeurs['type'] = $type;
$valeurs['destinataires'] = ($destinataires ? explode(",",$destinataires):array());
$valeurs['titre'] = $titre;
$valeurs['texte'] = $texte;
}
if (in_array($valeurs['type'],array('pb','affich')))
$valeurs['_destiner'] = '';
else
$valeurs['_destiner'] = ' ';
return $valeurs;
}
function formulaires_editer_message_verifier_dist($id_message='new',$type='message',$retour='',$accepter_email='oui',$destinataires='',$titre='',$texte=''){
$oblis = array('titre');
if (!_request('draft'))
$oblis[] = 'texte';
if (intval($id_message) AND $t=sql_getfetsel('type','spip_messages','id_message='.intval($id_message)))
$type = $t;
if (!in_array($type,array('pb','affich'))
// pas de destinataire obligatoire pour un brouillon
AND !_request('draft'))
$oblis['destinataires'] = 'destinataires';
if ($d=_request('destinataires'))
set_request('destinataires',implode(',',$d));
$erreurs = formulaires_editer_objet_verifier('message',$id_message,$oblis);
if ($d)
set_request('destinataires',$d);
include_spip('inc/messages');
if (!$erreurs['destinataires']
AND isset($oblis['destinataires'])
AND $e = messagerie_verifier_destinataires(_request('destinataires'),array('accepter_email'=>($accepter_email=='oui'))))
$erreurs['destinataires'] = implode(', ',$e);
return $erreurs;
}
function formulaires_editer_message_traiter_dist($id_message='new',$type='message',$retour='',$accepter_email='oui',$destinataires='',$titre='',$texte=''){
// preformater le post
// fixer le type de message
// sans modifier le type d'un message existant
if (intval($id_message) AND $t=sql_getfetsel('type','spip_messages','id_message='.intval($id_message)))
$type = $t;
set_request('type',$type);
// formater les destinataires
$d = _request('destinataires');
if (!$d)
$d = array();
include_spip('inc/messages');
$d = messagerie_nettoyer_destinataires($d);
// si email non acceptes, extraire les seuls id_auteur de la liste proposee
if ($accepter_email!=='oui'){
// separer id_auteur et email
$d = messagerie_destiner($d);
// ne conserver que les id_auteur
$d = reset($d);
}
// reinjecter sous forme de chaine
set_request('destinataires',implode(',',$d));
// fixer l'auteur !
set_request('id_auteur',$GLOBALS['visiteur_session']['id_auteur']);
// on gere par les traitements standard
// la diffusion du message se fait par pipeline post_edition sur instituer
// et notification
$res = formulaires_editer_objet_traiter('message',$id_message,0,0,$retour,'');
if ($id_message = $res['id_message']
AND !_request('draft')){
include_spip('action/editer_objet');
objet_modifier('message',$id_message,array('statut'=>'publie'));
// apres en message envoyes, retourner sur la boite d'envoi plutot que sur le message
if ($res['redirect']==generer_url_ecrire('message','id_message='.$id_message))
$res['redirect'] = generer_url_ecrire('messages','quoi=envoi');
}
set_request('destinataires',explode(',',_request('destinataires')));
return $res;
}

13
formulaires/inc-destinataires-message.html

@ -0,0 +1,13 @@
<div class="fake-input clearfix">
<span class="selected">
[(#REM) Premier element vide et cache qui sert de modele pour le js]
<span class="dest" style="display: none;"><img
src='#CHEMIN_IMAGE{supprimer-12.png}' width="12" height="12" onclick="jQuery(this).parent().remove();" /></span>
<BOUCLE_d(POUR){tableau #ENV{#ENV{name}}}>
<span class="dest"><input type="hidden" name="#ENV{name}[]" value="#VALEUR" />
<BOUCLE_a(AUTEURS){id_auteur=#VALEUR}{tout}>#NOM</BOUCLE_a>#VALEUR<//B_a>&nbsp;<img
src='#CHEMIN_IMAGE{supprimer-12.png}' width="12" height="12" onclick="jQuery(this).parent().remove();" /></span>
</BOUCLE_d>
</span>
<input type="text" class="text destinataires" data-name="#ENV{name}[]" value="" id="#GET{name}" />
</div>

175
inc/messages.php

@ -19,53 +19,82 @@ if (!defined('_EMAIL_GENERAL'))
define('_EMAIL_GENERAL','general'); // permet aux admin d'envoyer un email a tout le monde
/**
* Fonction generique de verification de la saisie
* Lister les statuts des auteurs pouvant recevoir un message
* c'est tous les auteurs au moins redacteur
*
* @return array
*/
function messagerie_statuts_destinataires_possibles(){
include_spip('inc/filtres_ecrire');
return auteurs_lister_statuts('redacteurs',false);
}
/**
* Nettoyer une liste de destinataires
* @param $destinataires
* @return array
*/
function messagerie_nettoyer_destinataires($destinataires){
foreach ($destinataires as $k=>$id){
// il se peut que l'id recupere l'ancre qui suit avec certains ie ... :(
if (preg_match(',^[0-9]+#[a-z_0-9]+,',$id))
$destinataires[$k] = intval($id);
}
return $destinataires;
}
/**
* Fonction generique de verification des destinataires
* lors de l'envoi d'un message ou de recommander
* un destinataire peut etre un id_auteur numerique
* ou une adresse mail valide, si l'options accepter_email est true
*
* @param array $obligatoires
* @param array $destinataires
* @param array $options
* @return array
*/
function messagerie_verifier($obligatoires = array()){
function messagerie_verifier_destinataires($destinataires,$options=array('accepter_email'=>true)){
$erreurs = array();
foreach($obligatoires as $obli)
if (!_request($obli))
$erreurs[$obli] = (isset($erreurs[$obli])?$erreurs[$obli]:'') . _T('formulaires:info_obligatoire_rappel');
$dests = _request('destinataires');
if (!count($dests)
AND !count( $dests = pipeline('messagerie_destiner',$dests)))
$erreurs[$obli='destinataire'] = (isset($erreurs[$obli])?$erreurs[$obli]:'') . _T('formulaires:info_obligatoire_rappel');
$destinataires = messagerie_nettoyer_destinataires($destinataires);
foreach ($destinataires as $id){
if (is_numeric($id)){
if (!$id)
$erreurs[] = _T('organiseur:erreur_destinataire_invalide',array('dest'=>$id));
}
else {
if (!$options['accepter_email']
OR !email_valide($id))
$erreurs[] = _T('organiseur:erreur_destinataire_invalide',array('dest'=>$id));
}
}
return $erreurs;
}
/**
* Selectionner les destinataires en distinguant emails et id_auteur
*
* @param unknown_type $dests
* @return unknown
* @param array $dests
* @return array
*/
function messagerie_destiner($dests){
$dests = pipeline('messagerie_destiner',$dests);
// separer les destinataires auteur des destinataires email
$auteurs_dest = array();
$email_dests = array();
$dests = messagerie_nettoyer_destinataires($dests);
foreach ($dests as $id){
// il se peut que l'id recupere l'ancre qui suit avec certains ie ... :(
if (preg_match(',[0-9]+#[a-z_0-9]+,',$id))
$id = intval($id);
if (is_numeric($id))
$auteurs_dest[] = $id;
elseif ($id!=_EMAIL_GENERAL)
elseif (defined('_MESSAGERIE_EMAIL_GENERAL') AND $id!=_MESSAGERIE_EMAIL_GENERAL)
$email_dests[] = $id;
}
if (count($email_dests)) {
// retrouver les id des emails
$res = sql_select('id_auteur,email','spip_auteurs',sql_in('email', $email_dests).')');
// retrouver les id des emails pour ceux qui sont en base
$res = sql_select('id_auteur,email','spip_auteurs',sql_in('email', $email_dests));
$auteurs_dest_found = array();
while ($row = spip_fetch_array($res)){
while ($row = sql_fetch($res)){
$auteurs_dest_found[] = $row['id_auteur'];
}
$auteurs_dest = array_merge($auteurs_dest,$auteurs_dest_found);
@ -74,66 +103,43 @@ function messagerie_destiner($dests){
}
/**
* Envoyer un message par la messagerie interne
* Diffuser un message par la messagerie interne
*
* @param string $objet
* @param string $texte
* @param int $id_message
* @param array $auteurs_dest
* @param bool $type
* @return int
* @return bool|int
*/
function messagerie_messager($objet, $texte, $auteurs_dest=array(),$type = ''){
$type = substr($type,0,6);
if (!in_array($type,array('normal','genera','pb','affich')))
$type = 'normal';
function messagerie_diffuser_message($id_message, $auteurs_dest=array()){
$out = false;
if (count($auteurs_dest) OR $general){
// envoyons le message
$id_message = sql_insertq('spip_messages',array(
'titre' => safehtml($objet),
'texte' => safehtml($texte),
'type' => $type,
'date_heure' => 'NOW()',
'date_fin' => 'NOW()',
'rv' => 'non',
'statut' => 'publie',
'id_auteur' => $GLOBALS['visiteur_session']['id_auteur'],
));
if ($id_message) {
$insert = array();
if (!$general) {
foreach($auteurs_dest as $id)
$insert[] = array('id_objet'=>$id_message,'objet'=>'message','id_auteur'=>$id,'vu'=>'non');
}
else {
$res = sql_select('id_auteur','spip_auteurs');
while ($row = sql_fetch($res))
$insert[] = array('id_objet'=>$id_message,'objet'=>'message','id_auteur'=>$row['id_auteur'],'vu'=>'non');
}
sql_insertq_multi('spip_auteurs_liens',$insert);
$out = $id_message;
}
if ($id_message=intval($id_message)
AND count($auteurs_dest)){
include_spip('action/editer_liens');
$out = objet_associer(array('auteur'=>$auteurs_dest),array('message'=>$id_message),array('vu'=>'non'));
}
return $out;
}
/**
* Envoyer un message par mail
* Envoyer un message par mail pour les destinataires externes
*
* @param string $objet
* @param string $texte
* @param int $id_message
* @param array $emails_dest
* @return bool
*/
function messagerie_mailer($objet, $texte, $emails_dest=array()){
if (count($emails_dest)) {
$from = sql_getfetsel('email','spip_auteurs','id_auteur='.intval($GLOBALS['visiteur_session']['id_auteur']));
$envoyer_mail = charger_fonction('envoyer_mail','inc');
foreach($emails_dest as $email)
$envoyer_mail($email,$objet,$texte,$from);
return true;
function messagerie_mailer_message($id_message, $emails_dest=array()){
if ($id_message=intval($id_message)
AND count($emails_dest)) {
if ($row = sql_fetsel('titre,texte,id_auteur','spip_messages','id_message='.intval($id_message))){
$from = sql_getfetsel('email','spip_auteurs','id_auteur='.$row['id_auteur']);
foreach($emails_dest as $email)
job_queue_add(
'envoyer_mail',
'messagerie mail',
array($email,$row['titre'],array('texte'=>$row['texte'],'from'=>$from)),
'inc/'
);
return true;
}
}
return false;
}
@ -145,8 +151,19 @@ function messagerie_mailer($objet, $texte, $emails_dest=array()){
* @param array $liste
*/
function messagerie_marquer_lus($id_auteur,$liste){
$liste = array_map('intval',$liste);
sql_updateq('spip_auteurs_liens',array('vu'=>'oui'),array('id_auteur='.intval($id_auteur),"objet='message'",sql_in('id_message',$liste)));
include_spip('action/editer_liens');
if (!is_array($liste))
$liste = array($liste);
// completer les liens qui n'existent pas encore
// ex : pour marquer lue une annonce, on ajoute le lien d'abord (n'existe pas)
// puis on le marque 'oui'
$liens = objet_trouver_liens(array('auteur'=>$id_auteur),array('message'=>$liste));
$l = array();
foreach($liens as $lien)
$l[] = $lien['message'];
objet_associer(array('auteur'=>$id_auteur),array('message'=>array_diff($liste,$l)),array('vu'=>'oui'));
// puis les marquer tous lus
objet_qualifier_liens(array('auteur'=>$id_auteur),array('message'=>$liste),array('vu'=>'oui'));
include_spip('inc/invalideur');
suivre_invalideur("message/".implode(',',$liste));
}
@ -158,21 +175,21 @@ function messagerie_marquer_lus($id_auteur,$liste){
* @param array $liste
*/
function messagerie_marquer_non_lus($id_auteur,$liste){
$liste = array_map('intval',$liste);
sql_updateq('spip_auteurs_liens',array('vu'=>'non'),array('id_auteur='.intval($id_auteur),"objet='message'",sql_in('id_message',$liste)));
include_spip('action/editer_liens');
objet_qualifier_liens(array('auteur'=>$id_auteur),array('message'=>$liste),array('vu'=>'non'));
include_spip('inc/invalideur');
suivre_invalideur("message/".implode(',',$liste));
}
/**
* Effacer un message
* Effacer un message recu
*
* @param int $id_auteur
* @param array $liste
*/
function messagerie_effacer($id_auteur,$liste){
$liste = array_map('intval',$liste);
sql_updateq('spip_auteurs_liens',array('vu'=>'pou'),array('id_auteur='.intval($id_auteur),"objet='message'",sql_in('id_message',$liste)));
function messagerie_effacer_recu($id_auteur,$liste){
include_spip('action/editer_liens');
objet_dissocier(array('auteur'=>$id_auteur),array('message'=>$liste));
include_spip('inc/invalideur');
suivre_invalideur("message/".implode(',',$liste));
}

808
javascript/jquery.autocomplete.js

@ -0,0 +1,808 @@
/*
* jQuery Autocomplete plugin 1.1
*
* Copyright (c) 2009 Jorn Zaefferer
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
* Revision: $Id: jquery.autocomplete.js 15 2009-08-22 10:30:27Z joern.zaefferer $
*/
;(function($) {
$.fn.extend({
autocomplete: function(urlOrData, options) {
var isUrl = typeof urlOrData == "string";
options = $.extend({}, $.Autocompleter.defaults, {
url: isUrl ? urlOrData : null,
data: isUrl ? null : urlOrData,
delay: isUrl ? $.Autocompleter.defaults.delay : 10,
max: options && !options.scroll ? 10 : 150
}, options);
// if highlight is set to false, replace it with a do-nothing function
options.highlight = options.highlight || function(value) { return value; };
// if the formatMatch option is not specified, then use formatItem for backwards compatibility
options.formatMatch = options.formatMatch || options.formatItem;
return this.each(function() {
new $.Autocompleter(this, options);
});
},
result: function(handler) {
return this.bind("result", handler);
},
search: function(handler) {
return this.trigger("search", [handler]);
},
flushCache: function() {
return this.trigger("flushCache");
},
setOptions: function(options){
return this.trigger("setOptions", [options]);
},
unautocomplete: function() {
return this.trigger("unautocomplete");
}
});
$.Autocompleter = function(input, options) {
var KEY = {
UP: 38,
DOWN: 40,
DEL: 46,
TAB: 9,
RETURN: 13,
ESC: 27,
COMMA: 188,
PAGEUP: 33,
PAGEDOWN: 34,
BACKSPACE: 8
};
// Create $ object for input element
var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass);
var timeout;
var previousValue = "";
var cache = $.Autocompleter.Cache(options);
var hasFocus = 0;
var lastKeyPressCode;
var config = {
mouseDownOnSelect: false
};
var select = $.Autocompleter.Select(options, input, selectCurrent, config);
var blockSubmit;
// prevent form submit in opera when selecting with return key
$.browser.opera && $(input.form).bind("submit.autocomplete", function() {
if (blockSubmit) {
blockSubmit = false;
return false;
}
});
// only opera doesn't trigger keydown multiple times while pressed, others don't work with keypress at all
$input.bind(($.browser.opera ? "keypress" : "keydown") + ".autocomplete", function(event) {
// a keypress means the input has focus
// avoids issue where input had focus before the autocomplete was applied
hasFocus = 1;
// track last key pressed
lastKeyPressCode = event.keyCode;
switch(event.keyCode) {
case KEY.UP:
event.preventDefault();
if ( select.visible() ) {
select.prev();
} else {
onChange(0, true);
}
break;
case KEY.DOWN:
event.preventDefault();
if ( select.visible() ) {
select.next();
} else {
onChange(0, true);
}
break;
case KEY.PAGEUP:
event.preventDefault();
if ( select.visible() ) {
select.pageUp();
} else {
onChange(0, true);
}
break;
case KEY.PAGEDOWN:
event.preventDefault();
if ( select.visible() ) {
select.pageDown();
} else {
onChange(0, true);
}
break;
// matches also semicolon
case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA:
case KEY.TAB:
case KEY.RETURN:
if( selectCurrent() ) {
// stop default to prevent a form submit, Opera needs special handling
event.preventDefault();
blockSubmit = true;
return false;
}
break;
case KEY.ESC:
select.hide();
break;
default:
clearTimeout(timeout);
timeout = setTimeout(onChange, options.delay);
break;
}
}).focus(function(){
// track whether the field has focus, we shouldn't process any
// results if the field no longer has focus
hasFocus++;
}).blur(function() {
hasFocus = 0;
if (!config.mouseDownOnSelect) {
hideResults();
}
}).click(function() {
// show select when clicking in a focused field
if ( hasFocus++ > 1 && !select.visible() ) {
onChange(0, true);
}
}).bind("search", function() {
// TODO why not just specifying both arguments?
var fn = (arguments.length > 1) ? arguments[1] : null;
function findValueCallback(q, data) {
var result;
if( data && data.length ) {
for (var i=0; i < data.length; i++) {
if( data[i].result.toLowerCase() == q.toLowerCase() ) {
result = data[i];
break;
}
}
}
if( typeof fn == "function" ) fn(result);
else $input.trigger("result", result && [result.data, result.value]);
}
$.each(trimWords($input.val()), function(i, value) {
request(value, findValueCallback, findValueCallback);
});
}).bind("flushCache", function() {
cache.flush();
}).bind("setOptions", function() {
$.extend(options, arguments[1]);
// if we've updated the data, repopulate
if ( "data" in arguments[1] )
cache.populate();
}).bind("unautocomplete", function() {
select.unbind();
$input.unbind();
$(input.form).unbind(".autocomplete");
});
function selectCurrent() {
var selected = select.selected();
if( !selected )
return false;
var v = selected.result;
previousValue = v;
if ( options.multiple ) {
var words = trimWords($input.val());
if ( words.length > 1 ) {
var seperator = options.multipleSeparator.length;
var cursorAt = $(input).selection().start;
var wordAt, progress = 0;
$.each(words, function(i, word) {
progress += word.length;
if (cursorAt <= progress) {
wordAt = i;
return false;
}
progress += seperator;
});
words[wordAt] = v;
// TODO this should set the cursor to the right position, but it gets overriden somewhere
//$.Autocompleter.Selection(input, progress + seperator, progress + seperator);
v = words.join( options.multipleSeparator );
}
v += options.multipleSeparator;
}
$input.val(v);
hideResultsNow();
$input.trigger("result", [selected.data, selected.value]);
return true;
}
function onChange(crap, skipPrevCheck) {
if( lastKeyPressCode == KEY.DEL ) {
select.hide();
return;
}
var currentValue = $input.val();
if ( !skipPrevCheck && currentValue == previousValue )
return;
previousValue = currentValue;
currentValue = lastWord(currentValue);
if ( currentValue.length >= options.minChars) {
$input.addClass(options.loadingClass);
if (!options.matchCase)
currentValue = currentValue.toLowerCase();
request(currentValue, receiveData, hideResultsNow);
} else {
stopLoading();
select.hide();
}
};
function trimWords(value) {
if (!value)
return [""];
if (!options.multiple)
return [$.trim(value)];
return $.map(value.split(options.multipleSeparator), function(word) {
return $.trim(value).length ? $.trim(word) : null;
});
}
function lastWord(value) {
if ( !options.multiple )
return value;
var words = trimWords(value);
if (words.length == 1)
return words[0];
var cursorAt = $(input).selection().start;
if (cursorAt == value.length) {
words = trimWords(value)
} else {
words = trimWords(value.replace(value.substring(cursorAt), ""));
}
return words[words.length - 1];
}
// fills in the input box w/the first match (assumed to be the best match)
// q: the term entered
// sValue: the first matching result
function autoFill(q, sValue){
// autofill in the complete box w/the first match as long as the user hasn't entered in more data
// if the last user key pressed was backspace, don't autofill
if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) {
// fill in the value (keep the case the user has typed)
$input.val($input.val() + sValue.substring(lastWord(previousValue).length));
// select the portion of the value not typed by the user (so the next character will erase)
$(input).selection(previousValue.length, previousValue.length + sValue.length);
}
};
function hideResults() {
clearTimeout(timeout);
timeout = setTimeout(hideResultsNow, 200);
};
function hideResultsNow() {
var wasVisible = select.visible();
select.hide();
clearTimeout(timeout);
stopLoading();
if (options.mustMatch) {
// call search and run callback
$input.search(
function (result){
// if no value found, clear the input box
if( !result ) {
if (options.multiple) {
var words = trimWords($input.val()).slice(0, -1);
$input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") );
}
else {
$input.val( "" );
$input.trigger("result", null);
}
}
}
);
}
};
function receiveData(q, data) {
if ( data && data.length && hasFocus ) {
stopLoading();
select.display(data, q);
autoFill(q, data[0].value);
select.show();
} else {
hideResultsNow();
}
};
function request(term, success, failure) {
if (!options.matchCase)
term = term.toLowerCase();
var data = cache.load(term);
// recieve the cached data
if (data && data.length) {
success(term, data);
// if an AJAX url has been supplied, try loading the data now
} else if( (typeof options.url == "string") && (options.url.length > 0) ){
var extraParams = {
timestamp: +new Date()
};
$.each(options.extraParams, function(key, param) {
extraParams[key] = typeof param == "function" ? param() : param;
});
$.ajax({
// try to leverage ajaxQueue plugin to abort previous requests
mode: "abort",
// limit abortion to this input
port: "autocomplete" + input.name,
dataType: options.dataType,
url: options.url,
data: $.extend({
q: lastWord(term),
limit: options.max
}, extraParams),
success: function(data) {
var parsed = options.parse && options.parse(data) || parse(data);
cache.add(term, parsed);
success(term, parsed);
}
});
} else {
// if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match
select.emptyList();
failure(term);
}
};
function parse(data) {
var parsed = [];
var rows = data.split("\n");
for (var i=0; i < rows.length; i++) {
var row = $.trim(rows[i]);
if (row) {
row = row.split("|");
parsed[parsed.length] = {
data: row,
value: row[0],
result: options.formatResult && options.formatResult(row, row[0]) || row[0]
};
}
}
return parsed;
};
function stopLoading() {
$input.removeClass(options.loadingClass);
};
};
$.Autocompleter.defaults = {
inputClass: "ac_input",
resultsClass: "ac_results",
loadingClass: "ac_loading",
minChars: 1,
delay: 400,
matchCase: false,
matchSubset: true,
matchContains: false,
cacheLength: 10,
max: 100,
mustMatch: false,
extraParams: {},
selectFirst: true,
formatItem: function(row) { return row[0]; },
formatMatch: null,
autoFill: false,
width: 0,
multiple: false,
multipleSeparator: ", ",
highlight: function(value, term) {
return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
},
scroll: true,
scrollHeight: 180
};
$.Autocompleter.Cache = function(options) {
var data = {};
var length = 0;
function matchSubset(s, sub) {
if (!options.matchCase)
s = s.toLowerCase();
var i = s.indexOf(sub);
if (options.matchContains == "word"){
i = s.toLowerCase().search("\\b" + sub.toLowerCase());
}
if (i == -1) return false;
return i == 0 || options.matchContains;
};
function add(q, value) {
if (length > options.cacheLength){
flush();
}
if (!data[q]){
length++;
}
data[q] = value;
}
function populate(){
if( !options.data ) return false;
// track the matches
var stMatchSets = {},
nullData = 0;
// no url was specified, we need to adjust the cache length to make sure it fits the local data store
if( !options.url ) options.cacheLength = 1;
// track all options for minChars = 0
stMatchSets[""] = [];
// loop through the array and create a lookup structure
for ( var i = 0, ol = options.data.length; i < ol; i++ ) {
var rawValue = options.data[i];
// if rawValue is a string, make an array otherwise just reference the array
rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue;
var value = options.formatMatch(rawValue, i+1, options.data.length);
if ( value === false )
continue;
var firstChar = value.charAt(0).toLowerCase();
// if no lookup array for this character exists, look it up now
if( !stMatchSets[firstChar] )
stMatchSets[firstChar] = [];
// if the match is a string
var row = {
value: value,
data: rawValue,
result: options.formatResult && options.formatResult(rawValue) || value
};
// push the current match into the set list
stMatchSets[firstChar].push(row);
// keep track of minChars zero items
if ( nullData++ < options.max ) {
stMatchSets[""].push(row);
}
};
// add the data items to the cache
$.each(stMatchSets, function(i, value) {
// increase the cache size
options.cacheLength++;
// add to the cache
add(i, value);
});
}
// populate any existing data
setTimeout(populate, 25);
function flush(){
data = {};
length = 0;
}
return {
flush: flush,
add: add,
populate: populate,
load: function(q) {
if (!options.cacheLength || !length)
return null;
/*
* if dealing w/local data and matchContains than we must make sure
* to loop through all the data collections looking for matches
*/
if( !options.url && options.matchContains ){
// track all matches
var csub = [];
// loop through all the data grids for matches
for( var k in data ){
// don't search through the stMatchSets[""] (minChars: 0) cache
// this prevents duplicates
if( k.length > 0 ){
var c = data[k];
$.each(c, function(i, x) {
// if we've got a match, add it to the array
if (matchSubset(x.value, q)) {
csub.push(x);
}
});
}
}
return csub;
} else
// if the exact item exists, use it
if (data[q]){
return data[q];
} else
if (options.matchSubset) {
for (var i = q.length - 1; i >= options.minChars; i--) {
var c = data[q.substr(0, i)];
if (c) {
var csub = [];
$.each(c, function(i, x) {
if (matchSubset(x.value, q)) {
csub[csub.length] = x;
}
});
return csub;
}
}
}
return null;
}
};
};
$.Autocompleter.Select = function (options, input, select, config) {
var CLASSES = {
ACTIVE: "ac_over"
};
var listItems,
active = -1,
data,
term = "",
needsInit = true,
element,
list;
// Create results
function init() {
if (!needsInit)
return;
element = $("<div/>")
.hide()
.addClass(options.resultsClass)
.css("position", "absolute")
.appendTo(document.body);
list = $("<ul/>").appendTo(element).mouseover( function(event) {
if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') {
active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event));
$(target(event)).addClass(CLASSES.ACTIVE);
}
}).click(function(event) {
$(target(event)).addClass(CLASSES.ACTIVE);
select();
// TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus
input.focus();
return false;
}).mousedown(function() {
config.mouseDownOnSelect = true;
}).mouseup(function() {
config.mouseDownOnSelect = false;
});
if( options.width > 0 )
element.css("width", options.width);
needsInit = false;
}
function target(event) {
var element = event.target;
while(element && element.tagName != "LI")
element = element.parentNode;
// more fun with IE, sometimes event.target is empty, just ignore it then
if(!element)
return [];
return element;
}
function moveSelect(step) {
listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE);
movePosition(step);
var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE);
if(options.scroll) {
var offset = 0;
listItems.slice(0, active).each(function() {
offset += this.offsetHeight;
});
if((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) {
list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight());
} else if(offset < list.scrollTop()) {
list.scrollTop(offset);
}
}
};
function movePosition(step) {
active += step;
if (active < 0) {
active = listItems.size() - 1;
} else if (active >= listItems.size()) {
active = 0;
}
}
function limitNumberOfItems(available) {
return options.max && options.max < available
? options.max
: available;
}
function fillList() {
list.empty();
var max = limitNumberOfItems(data.length);
for (var i=0; i < max; i++) {
if (!data[i])
continue;
var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term);
if ( formatted === false )
continue;
var li = $("<li/>").html( options.highlight(formatted, term) ).addClass(i%2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0];
$.data(li, "ac_data", data[i]);
}