[spip-dev] Formulaire CVT : bug ou idiotie de ma part ?

Bonjour à tous,

  Je suis en train d'étendre la gestion des droits visiteurs à l'aide du
plugin champ extra et des formulaires CVT et il y a un petit quelque
chose que je ne saisis pas.

  Sur le principe, un visiteur peut modifier ses données une fois qu'il
est authentifié. Lorsqu'il s'inscrit sur le site en question, je lui
demande un email et un identifiant. Une fois l'accès ouvert, il doit
remplir d'autres champs, en particulier sa catégorie
socio-professionnelle et un autre champ qui est un 'checkbox'.

  Le formulaire fonctionne parfaitement, les données sont enregistrées
dans la base de données. J'ai vérifié en ligne de commande, pas de
problème, c'est bien le cas.

  Or, lorsque l'utilisateur recharge le formulaire pour le modifier, tous
les champs sont rechargés à l'exception de la valeur de la checkbox.
Plus précisément, cette valeur est lu à l'ouverture de la session et
plus jamais remise à jour plus tard.

  Je viens d'afficher le tableau contenant la session avec
var_dump($GLOBALS['visiteur_session']) dans le script de chargement.
Cela me renvoie :

array(37)
{ ["prefs"]=> array(5)
    { ["couleur"]=> int(9) ["display"]=> int(2) ["display_navigation"]=>
string(22) "navigation_avec_icones" ["display_outils"]=> string(3) "oui"
["cnx"]=> string(5) "perma" }
    ["id_auteur"]=> int(1)
    ["nom"]=> string(18) "xxxx"
    ["bio"]=> string(0) ""
    ["email"]=> string(28) "xxx@yyy.fr"
    ["nom_site"]=> string(0) ""
    ["url_site"]=> string(0) ""
    ["login"]=> string(18) "xxxxx"
    ["statut"]=> string(9) "0minirezo"
    ["webmestre"]=> string(3) "oui"
    ["maj"]=> string(19) "2020-12-16 17:17:50"
    ["pgp"]=> string(0) ""
    ["en_ligne"]=> string(19) "0000-00-00 00:00:00"
    ["source"]=> string(4) "spip"
    ["lang"]=> string(0) ""
    ["civilite"]=> string(1) "1"
    ["identite_nom"]=> string(5) "xxx"
    ["identite_prenom"]=> string(12) "yyyy"
    ["cat_socio"]=> string(2) "21"
    ["membre_adherent"]=> string(1) "0" <- dans la base, j'ai un 'oui'
    ["code_postal"]=> string(0) ""
    ["situation_famille"]=> string(0) ""
    ["charte"]=> string(0) ""
    ["reglement"]=> string(0) ""
    ["membre_participant"]=> string(0) ""
    ["parcours_scolaire"]=> string(0) ""
    ["parcours_pro"]=> string(0) ""
    ["activites_asso"]=> string(0) ""
    ["interets"]=> string(0) ""
    ["profession"]=> string(0) ""
    ["validite_adhesion"]=> string(19) "0000-00-00 00:00:00"
    ["auth"]=> string(4) "spip"
    ["cookie"]=> string(3) "oui"
    ["hash_env"]=> string(32) "5b01fd3bd374a4e12916af51d948468e"
    ["ip_change"]=> bool(false)
    ["date_session"]=> int(1608135477)
    ["restreint"]=> array(0) { } }

  Chose étonnante, les champs texte (identite_nom et identite_prenom)
sont bien écrits dans la base et relus pas la fonction de chargement
(l'utilisateur peut changer son prénom et au prochain rechargement du
formulaire, le nouveau prénom est bien là). Mais pas membre_adherent qui
reste dans l'état initial obtenu lors de l'ouverture de la session.

  Ma fonction de chargement est pourtant triviale :

function
formulaires_membre_sympathisant_charger_dist()
{
    // Lecture des données dans la table auteur pour
    // préremplir le formulaire.
    // Les valeurs courantes sont contenues dans le tableau
    // $GLOBALS['visiteur_session'][champ de la table auteur]

    //actualiser_sessions($GLOBALS['visiteur_session']['id_auteur']);
    //L'actualisation ne change rien au problème de checkbox.

    $valeurs = array(
            'prenom' => $GLOBALS['visiteur_session']['identite_prenom'],
            'nom' => $GLOBALS['visiteur_session']['identite_nom'],
            'civilite' => $GLOBALS['visiteur_session']['civilite'],
            'sociopro' => $GLOBALS['visiteur_session']['cat_socio'],
            'membre_adherent' =>
                    $GLOBALS['visiteur_session']['membre_adherent']);
    echo $GLOBALS['visiteur_session']['membre_adherent'];
    return $valeurs;
}

  Je ne sais pas si j'ai fait quelque part une boulette, mais je ne
saisis pas bien ce qu'il se passe. Toute idée sera la bienvenue.

  Bien cordialement,

  JB

Idiotie de ma part. En fait, aucun champ n'est mis à jour dans les
variables $GLOBALS['visiteur_session']. Tant qu'on ne quitte par le
formulaire, ça va, mais si on va entre temps sur une autre page, on
s'aperçoit que les données affichées ne sont plus celles qui étaient
dans la base.

  Y a-t-il une solution plus élégante que de taper dans la base de
données à grands coups de SQL ?

  Bien cordialement,

  JB

Hello,

Il y a une fonction actualiser_session()pour pousser une valeur dans n’importe quel session en cours:

ex:

actualiser_sessions(array(‘id_auteur’ => $id_auteur, ‘mavar’ => ‘hello’));

De plus tu devras prévoir de peupler la session à la connection de l’utilisateur avec les nouvelles valeurs issues de la bdd
ça se fait dans le pipeline preparer_fichier_session();

function monplugin_preparer_fichier_session($flux){
if ($id_auteur=$flux[‘data’][‘id_auteur’]){
if ($mavar = sql_getfetsel(‘monchamp’,‘spip_auteurs’,‘id_auteur=’ .intval($id_auteur))){
$flux[‘data’][‘mavar’] = $mavar;
}
}
return $flux;
}

Voilà.

Bonjour,

Il y a une fonction actualiser_session()pour pousser une valeur dans n'importe quel session en cours:

ex:
actualiser_sessions(array('id_auteur' => $id_auteur, 'mavar' => 'hello'));

Et ça, ça peut se placer dans formulaire_xxx_traiter_dist ?

Au rechargement du formulaire la variable de session contient cette info ?

Stephane Santon a écrit :

Bonjour,

Il y a une fonction actualiser_session()pour pousser une valeur dans
n'importe quel session en cours:

ex:
actualiser_sessions(array('id_auteur' => $id_auteur, 'mavar' =>
'hello'));

Et ça, ça peut se placer dans formulaire_xxx_traiter_dist ?

  J'ai bien essayé de rajouter ceci dans le traitement :

    actualiser_sessions(array(
            'id_auteur' => $GLOBALS['visiteur_session']['id_auteur'],
            'identite_nom' => $donnees['identite_nom'],
            'identite_prenom' => $donnees['identite_prenom'],
            'civilite' => $donnees['civilite'],
            'cat_socio' => $donnees['cat_socio'],
            'membre_adherent' => $donnees['membre_adherent']));

Au rechargement du formulaire la variable de session contient cette info ?

  Et non.

  Bien cordialement,

  JB

Ah ok, moi non plus.

J'utilise session_set et là ça marche.

https://code.spip.net/autodoc/tree/ecrire/inc/session.php.html#function_session_set

Et ça, ça peut se placer dans formulaire_xxx_traiter_dist ?
Au rechargement du formulaire la variable de session contient cette info ?

ça peut se placer n’importe où… Mais généralement on l’exécute dans une fonction traiter oui.

Le merge des cles/valeurs se passe ici:

https://git.spip.net/SPIP/spip/src/branch/master/ecrire/inc/session.php#L512

J’utilise session_set et là ça marche.

Oui mais non, c’est pas la même chose ; actualiser_sessions() permet de forcer la valeur dans n’importe quelle session d’auteur connecté, Pas forcément la session courante.

Avec actualiser_sessions() utilisateur A peut modifier la session de utilisateur B.

cf: edition d’une fiche auteur par un admin.

Stephane Santon a écrit :

Il y a une fonction actualiser_session()pour pousser une valeur dans
n'importe quel session en cours:

actualiser_sessions(array('id_auteur' => $id_auteur, 'mavar' =>
'hello'));

Et ça, ça peut se placer dans formulaire_xxx_traiter_dist ?

J&#39;ai bien essayé de rajouter ceci dans le traitement :

 actualiser\_sessions\(array\(
         &#39;id\_auteur&#39; =&gt; $GLOBALS\[&#39;visiteur\_session&#39;\]\[&#39;id\_auteur&#39;\],
         &#39;identite\_nom&#39; =&gt; $donnees\[&#39;identite\_nom&#39;\],
         &#39;identite\_prenom&#39; =&gt; $donnees\[&#39;identite\_prenom&#39;\],
         &#39;civilite&#39; =&gt; $donnees\[&#39;civilite&#39;\],
         &#39;cat\_socio&#39; =&gt; $donnees\[&#39;cat\_socio&#39;\],
         &#39;membre\_adherent&#39; =&gt; $donnees\[&#39;membre\_adherent&#39;\]\)\);

Au rechargement du formulaire la variable de session contient cette
info ?

Et non\.

Ah ok, moi non plus.

J'utilise session_set et là ça marche.

https://code.spip.net/autodoc/tree/ecrire/inc/session.php.html#function_session_set

  Oui mais non :wink:

  J'ai essayé hier soir d'utiliser cette fonction dans le traitement
comme ceci :

    $donnees['identite_nom'] = _request('nom');
    $donnees['identite_prenom'] = _request('prenom');
    $donnees['civilite'] = _request('civilite');
    $donnees['cat_socio'] = _request('sociopro');

    if (_request('membre_adherent'))
    {
        $donnees['membre_adherent'] = _request('membre_adherent');
    }
    else
    {
        $donnees['membre_adherent'] = 'non';
    }

    foreach($donnees as $k => $v)
    {
        session_set($k, $v);
    }

  J'ai naturellement vérifié que $k et $v contenaient les bonnes
informations. Au rechargement de la page, j'ai toujours les valeurs
initiales et non les valeurs modifiées.

  Bien cordialement,

  JB

Bon, bon, bon...

  C'est plus grave que ce que je pensais.

  Dans ma base, j'ai l'enregistrement suivant :

identite_nom | identite_prenom | civilite | sociopro | membre_adherent
BERTRAND | Joëlsdlgj | 1 | 11 | oui

  J'ai écrit dans la fonction charger :

    if ($session = sql_select(array('identite_prenom', 'identite_nom',
            'civilite', 'cat_socio', 'membre_adherent'),
            'spipactesvrais_auteurs',
            'id_auteur = '.$GLOBALS['visiteur_session']['id_auteur']))
    {
        echo 'lecture base<br/>';
        $resultat = sql_fetch($session);
        $valeurs = array(
                'prenom' => $resultat['identite_prenom'],
                'nom' => $resultat['identite_nom'],
                'civilite' => $resultat['civilite'],
                'sociopro' => $resultat['cat_socio'],
                'membre_adherent' => $resultat['membre_adherent']);
        sql_free($select);
        var_dump($valeurs);
    }
    else
    {
        echo 'valeur par défaut';
        $valeurs = array(
                'prenom' => $GLOBALS['visiteur_session']['identite_prenom'],
                'nom' => $GLOBALS['visiteur_session']['identite_nom'],
                'civilite' => $GLOBALS['visiteur_session']['civilite'],
                'sociopro' => $GLOBALS['visiteur_session']['cat_socio'],
                'membre_adherent' =>
                        $GLOBALS['visiteur_session']['membre_adherent']);
    }

  Et sur mon formulaire, j'obtiens :

lecture base
array(5) { ["prenom"]=> string(5) "Joël" ["nom"]=> string(8) "BERTRAND"
["civilite"]=> string(1) "1" ["sociopro"]=> string(2) "11"
["membre_adherent"]=> string(3) "non" }

  J'ai naturellement vérifié que je tape bien sur la bonne base, sur le
bon serveur. Je ne comprends pas pourquoi je ne lis pas les valeurs
écrites dans la base au coup précédent.

  Sauf à ce que j'ai raté quelque chose, les valeurs 'prenom' et
'membre_adherent' ne contiennent pas les bonnes valeurs (il s'agit ici
des valeurs initiales et non des valeurs relues dans la base).

  C'est de plus en plus incompréhensible pour moi.

  JB

J'ai l'impression qu'il y a un cache derrière la fonction sql_select()
puisque je retrouve les informations fautives dans le cache. Existe-t-il
une manière propre de forcer sql_select() à lire le contenu de la base
de données plutôt que le cache qui n'est pas à jour ?

  J'ai bien essayé de rajouter la modification du champ 'maj' dans la
table auteur, ça ne change rien au problème, les données sont relues
depuis le cache et non depuis la base de données.

  Toute idée sera la bienvenue,

  JB

BERTRAND Joël a écrit :

  J'ai l'impression qu'il y a un cache derrière la fonction sql_select()
puisque je retrouve les informations fautives dans le cache. Existe-t-il
une manière propre de forcer sql_select() à lire le contenu de la base
de données plutôt que le cache qui n'est pas à jour ?

  J'ai bien essayé de rajouter la modification du champ 'maj' dans la
table auteur, ça ne change rien au problème, les données sont relues
depuis le cache et non depuis la base de données.

  Confirmation. Au chargement de la page, les logs de mariadb indiquent :

201217 11:58:06 88 Connect actes-vrais@localhost as anonymous on
                    88 Init DB actes-vrais
                    88 Query set sql_mode=''
                    88 Query SET NAMES 'utf8'
                    88 Query SELECT nom, valeur
FROM `actes-vrais`.spipactesvrais_meta
WHERE ((nom IN ('alea_ephemere','alea_ephemere_ancien')))
                    88 Query SELECT nom,valeur FROM
`actes-vrais`.spipactesvrais_meta
                    88 Query SELECT id_objet
FROM `actes-vrais`.spipactesvrais_auteurs_liens
WHERE id_auteur=5 AND objet='rubrique' AND id_objet!=0
                    88 Query SELECT id_rubrique,statut
FROM `actes-vrais`.spipactesvrais_articles
WHERE id_article=88
                    88 Query SELECT id_rubrique,statut
FROM `actes-vrais`.spipactesvrais_articles
WHERE id_article=88
                    88 Quit

alors qu'en même temps, la page est censée effectuer une requête sur la
table spip_auteurs. Je précise que le cache est interdit dans spip.

Pas simple, mais j'ai trouvé en tâtonnant. Il faut invalider le cache
associé à l'auteur courant. La fonction traiter() devient :

function
formulaires_membre_sympathisant_traiter_dist()
{
    $message = array();
    $donnees = array();

    $donnees['identite_nom'] = _request('nom');
    $donnees['identite_prenom'] = _request('prenom');
    $donnees['civilite'] = _request('civilite');
    $donnees['cat_socio'] = _request('sociopro');

    if (_request('membre_adherent'))
    {
        $donnees['membre_adherent'] = _request('membre_adherent');
    }
    else
    {
        $donnees['membre_adherent'] = 'non';
    }

    $donnees['maj'] = date("Y-m-d H:i:s");

    // Cette requête ne peut échouer (sauf si le disque est plein
    // auquel cas je ne peux rien faire).

    if (sql_updateq('spip_auteurs', $donnees,
        'id_auteur = '.$GLOBALS['visiteur_session']['id_auteur']))
    {
        $message['message_ok'] =
                'La modification des données a été prise en compte.';
    }
    else
    {
        $message['message_ok'] =
                'Erreur d\'enregistrement dans la base de données.';
    }

    include_spip('inc/invalideur');

suivre_invalideur("id='auteur/".$GLOBALS['visiteur_session']['id_auteur']
            ."'");

    return $message;
}

  À cette condition, spip effectue une requête sur la table 'auteur' et
recharge les bonnes données. Sinon, il les prend dans le cache (mais
seulement lorsque l'utilisateur a quitté la page et y revient).

  Ça vaudrait peut-être le coup d'avoir une fonction sql qui passe outre
le cache pour éviter ce genre de comportement.

  Bien cordialement,

  JB