487 lines
15 KiB
PHP
487 lines
15 KiB
PHP
|
<?php
|
||
|
/***************************************************************************\
|
||
|
* SPIP, Systeme de publication pour l'internet *
|
||
|
* *
|
||
|
* Copyright (c) 2001-2019 *
|
||
|
* 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. *
|
||
|
\***************************************************************************/
|
||
|
|
||
|
/**
|
||
|
* Gestion du formulaire d'édition de liens
|
||
|
*
|
||
|
* @package SPIP\Core\Formulaires
|
||
|
**/
|
||
|
if (!defined('_ECRIRE_INC_VERSION')) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Retrouve la source et l'objet de la liaison
|
||
|
*
|
||
|
* À partir des 3 premiers paramètres transmis au formulaire,
|
||
|
* la fonction retrouve :
|
||
|
* - l'objet dont on utilise sa table de liaison (table_source)
|
||
|
* - l'objet et id_objet sur qui on lie des éléments (objet, id_objet)
|
||
|
* - l'objet que l'on veut lier dessus (objet_lien)
|
||
|
*
|
||
|
* @param string $a
|
||
|
* @param string|int $b
|
||
|
* @param int|string $c
|
||
|
* @return array
|
||
|
* ($table_source,$objet,$id_objet,$objet_lien)
|
||
|
*/
|
||
|
function determine_source_lien_objet($a, $b, $c) {
|
||
|
$table_source = $objet_lien = $objet = $id_objet = null;
|
||
|
// auteurs, article, 23 :
|
||
|
// associer des auteurs à l'article 23, sur la table pivot spip_auteurs_liens
|
||
|
if (is_numeric($c) and !is_numeric($b)) {
|
||
|
$table_source = table_objet($a);
|
||
|
$objet_lien = objet_type($a);
|
||
|
$objet = objet_type($b);
|
||
|
$id_objet = $c;
|
||
|
}
|
||
|
// article, 23, auteurs
|
||
|
// associer des auteurs à l'article 23, sur la table pivot spip_articles_liens
|
||
|
if (is_numeric($b) and !is_numeric($c)) {
|
||
|
$table_source = table_objet($c);
|
||
|
$objet_lien = objet_type($a);
|
||
|
$objet = objet_type($a);
|
||
|
$id_objet = $b;
|
||
|
}
|
||
|
|
||
|
return array($table_source, $objet, $id_objet, $objet_lien);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Chargement du formulaire d'édition de liens
|
||
|
*
|
||
|
* #FORMULAIRE_EDITER_LIENS{auteurs,article,23}
|
||
|
* pour associer des auteurs à l'article 23, sur la table pivot spip_auteurs_liens
|
||
|
* #FORMULAIRE_EDITER_LIENS{article,23,auteurs}
|
||
|
* pour associer des auteurs à l'article 23, sur la table pivot spip_articles_liens
|
||
|
* #FORMULAIRE_EDITER_LIENS{articles,auteur,12}
|
||
|
* pour associer des articles à l'auteur 12, sur la table pivot spip_articles_liens
|
||
|
* #FORMULAIRE_EDITER_LIENS{auteur,12,articles}
|
||
|
* pour associer des articles à l'auteur 12, sur la table pivot spip_auteurs_liens
|
||
|
*
|
||
|
* @param string $a
|
||
|
* @param string|int $b
|
||
|
* @param int|string $c
|
||
|
* @param array|bool $options
|
||
|
* - Si array, tableau d'options
|
||
|
* - Si bool : valeur de l'option 'editable' uniquement
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
function formulaires_editer_liens_charger_dist($a, $b, $c, $options = array()) {
|
||
|
|
||
|
// compat avec ancienne signature ou le 4eme argument est $editable
|
||
|
if (!is_array($options)) {
|
||
|
$options = array('editable' => $options);
|
||
|
} elseif (!isset($options['editable'])) {
|
||
|
$options['editable'] = true;
|
||
|
}
|
||
|
|
||
|
$editable = $options['editable'];
|
||
|
|
||
|
list($table_source, $objet, $id_objet, $objet_lien) = determine_source_lien_objet($a, $b, $c);
|
||
|
if (!$table_source or !$objet or !$objet_lien or !$id_objet) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
$objet_source = objet_type($table_source);
|
||
|
$table_sql_source = table_objet_sql($objet_source);
|
||
|
|
||
|
// verifier existence de la table xxx_liens
|
||
|
include_spip('action/editer_liens');
|
||
|
if (!objet_associable($objet_lien)) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// L'éditabilité :) est définie par un test permanent (par exemple "associermots") ET le 4ème argument
|
||
|
include_spip('inc/autoriser');
|
||
|
$editable = ($editable and autoriser('associer' . $table_source, $objet, $id_objet)
|
||
|
and autoriser('modifier', $objet, $id_objet));
|
||
|
|
||
|
if (!$editable and !count(objet_trouver_liens(
|
||
|
array($objet_lien => '*'),
|
||
|
array(($objet_lien == $objet_source ? $objet : $objet_source) => $id_objet)
|
||
|
))) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// squelettes de vue et de d'association
|
||
|
// ils sont différents si des rôles sont définis.
|
||
|
$skel_vue = $table_source . '_lies';
|
||
|
$skel_ajout = $table_source . '_associer';
|
||
|
|
||
|
// description des roles
|
||
|
include_spip('inc/roles');
|
||
|
if ($roles = roles_presents($objet_source, $objet)) {
|
||
|
// on demande de nouveaux squelettes en conséquence
|
||
|
$skel_vue = $table_source . '_roles_lies';
|
||
|
$skel_ajout = $table_source . '_roles_associer';
|
||
|
}
|
||
|
|
||
|
$oups = '';
|
||
|
if ($editable) {
|
||
|
$oups = _request('_oups');
|
||
|
$oups = $oups ? $oups : '';
|
||
|
if ($oups) {
|
||
|
if (unserialize(base64_decode($oups, true))) {
|
||
|
// on est bon, rien a faire
|
||
|
} elseif (unserialize($oups)) {
|
||
|
// il faut encoder
|
||
|
$oups = base64_encode($oups);
|
||
|
} else {
|
||
|
$oups = '';
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
$valeurs = array(
|
||
|
'id' => "$table_source-$objet-$id_objet-$objet_lien", // identifiant unique pour les id du form
|
||
|
'_vue_liee' => $skel_vue,
|
||
|
'_vue_ajout' => $skel_ajout,
|
||
|
'_objet_lien' => $objet_lien,
|
||
|
'id_lien_ajoute' => _request('id_lien_ajoute'),
|
||
|
'objet' => $objet,
|
||
|
'id_objet' => $id_objet,
|
||
|
'objet_source' => $objet_source,
|
||
|
'table_source' => $table_source,
|
||
|
'recherche' => '',
|
||
|
'visible' => 0,
|
||
|
'ajouter_lien' => '',
|
||
|
'supprimer_lien' => '',
|
||
|
'qualifier_lien' => '',
|
||
|
'_roles' => $roles, # description des roles
|
||
|
'_oups' => entites_html($oups),
|
||
|
'editable' => $editable,
|
||
|
);
|
||
|
|
||
|
// les options non definies dans $valeurs sont passees telles quelles au formulaire html
|
||
|
$valeurs = array_merge($options, $valeurs);
|
||
|
|
||
|
return $valeurs;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Traiter le post des informations d'édition de liens
|
||
|
*
|
||
|
* Les formulaires peuvent poster dans quatre variables
|
||
|
* - ajouter_lien et supprimer_lien
|
||
|
* - remplacer_lien
|
||
|
* - qualifier_lien
|
||
|
*
|
||
|
* Les deux premières peuvent être de trois formes différentes :
|
||
|
* ajouter_lien[]="objet1-id1-objet2-id2"
|
||
|
* ajouter_lien[objet1-id1-objet2-id2]="nimportequoi"
|
||
|
* ajouter_lien['clenonnumerique']="objet1-id1-objet2-id2"
|
||
|
* Dans ce dernier cas, la valeur ne sera prise en compte
|
||
|
* que si _request('clenonnumerique') est vrai (submit associé a l'input)
|
||
|
*
|
||
|
* remplacer_lien doit être de la forme
|
||
|
* remplacer_lien[objet1-id1-objet2-id2]="objet3-id3-objet2-id2"
|
||
|
* ou objet1-id1 est celui qu'on enleve et objet3-id3 celui qu'on ajoute
|
||
|
*
|
||
|
* qualifier_lien doit être de la forme, et sert en complément de ajouter_lien
|
||
|
* qualifier_lien[objet1-id1-objet2-id2][role] = array("role1", "autre_role")
|
||
|
* qualifier_lien[objet1-id1-objet2-id2][valeur] = array("truc", "chose")
|
||
|
* produira 2 liens chacun avec array("role"=>"role1","valeur"=>"truc") et array("role"=>"autre_role","valeur"=>"chose")
|
||
|
*
|
||
|
* @param string $a
|
||
|
* @param string|int $b
|
||
|
* @param int|string $c
|
||
|
* @param array|bool $options
|
||
|
* - Si array, tableau d'options
|
||
|
* - Si bool : valeur de l'option 'editable' uniquement
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
function formulaires_editer_liens_traiter_dist($a, $b, $c, $options = array()) {
|
||
|
// compat avec ancienne signature ou le 4eme argument est $editable
|
||
|
if (!is_array($options)) {
|
||
|
$options = array('editable' => $options);
|
||
|
} elseif (!isset($options['editable'])) {
|
||
|
$options['editable'] = true;
|
||
|
}
|
||
|
|
||
|
$editable = $options['editable'];
|
||
|
|
||
|
$res = array('editable' => $editable ? true : false);
|
||
|
list($table_source, $objet, $id_objet, $objet_lien) = determine_source_lien_objet($a, $b, $c);
|
||
|
if (!$table_source or !$objet or !$objet_lien) {
|
||
|
return $res;
|
||
|
}
|
||
|
|
||
|
|
||
|
if (_request('tout_voir')) {
|
||
|
set_request('recherche', '');
|
||
|
}
|
||
|
|
||
|
include_spip('inc/autoriser');
|
||
|
if (autoriser('modifier', $objet, $id_objet)) {
|
||
|
// annuler les suppressions du coup d'avant !
|
||
|
if (_request('annuler_oups')
|
||
|
and $oups = _request('_oups')
|
||
|
and $oups = base64_decode($oups)
|
||
|
and $oups = unserialize($oups)
|
||
|
) {
|
||
|
if ($oups_objets = charger_fonction("editer_liens_oups_{$table_source}_{$objet}_{$objet_lien}", 'action', true)) {
|
||
|
$oups_objets($oups);
|
||
|
} else {
|
||
|
$objet_source = objet_type($table_source);
|
||
|
include_spip('action/editer_liens');
|
||
|
foreach ($oups as $oup) {
|
||
|
// verifier le contenu de $oup
|
||
|
foreach ($oup as $champ => $valeur) {
|
||
|
if (!is_string($champ)
|
||
|
or preg_match(',\W,', $champ)
|
||
|
or !is_scalar($valeur)) {
|
||
|
unset($oup[$champ]);
|
||
|
}
|
||
|
}
|
||
|
if (!empty($oup[$objet]) and !empty($oup[$objet_source])) {
|
||
|
if ($objet_lien == $objet_source) {
|
||
|
objet_associer(array($objet_source => $oup[$objet_source]), array($objet => $oup[$objet]), $oup);
|
||
|
} else {
|
||
|
objet_associer(array($objet => $oup[$objet]), array($objet_source => $oup[$objet_source]), $oup);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
# oups ne persiste que pour la derniere action, si suppression
|
||
|
set_request('_oups');
|
||
|
}
|
||
|
|
||
|
$supprimer = _request('supprimer_lien');
|
||
|
$ajouter = _request('ajouter_lien');
|
||
|
|
||
|
// il est possible de preciser dans une seule variable un remplacement :
|
||
|
// remplacer_lien[old][new]
|
||
|
if ($remplacer = _request('remplacer_lien')) {
|
||
|
foreach ($remplacer as $k => $v) {
|
||
|
if ($old = lien_verifier_action($k, '')) {
|
||
|
foreach (is_array($v) ? $v : array($v) as $kn => $vn) {
|
||
|
if ($new = lien_verifier_action($kn, $vn)) {
|
||
|
$supprimer[$old] = 'x';
|
||
|
$ajouter[$new] = '+';
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($supprimer) {
|
||
|
if ($supprimer_objets = charger_fonction(
|
||
|
"editer_liens_supprimer_{$table_source}_{$objet}_{$objet_lien}",
|
||
|
'action',
|
||
|
true
|
||
|
)) {
|
||
|
$oups = $supprimer_objets($supprimer);
|
||
|
} else {
|
||
|
include_spip('action/editer_liens');
|
||
|
$oups = array();
|
||
|
|
||
|
foreach ($supprimer as $k => $v) {
|
||
|
if ($lien = lien_verifier_action($k, $v)) {
|
||
|
$lien = explode('-', $lien);
|
||
|
list($objet_source, $ids, $objet_lie, $idl, $role) = $lien;
|
||
|
// appliquer une condition sur le rôle si défini ('*' pour tous les roles)
|
||
|
$cond = (!is_null($role) ? array('role' => $role) : array());
|
||
|
if ($objet_lien == $objet_source) {
|
||
|
$oups = array_merge(
|
||
|
$oups,
|
||
|
objet_trouver_liens(array($objet_source => $ids), array($objet_lie => $idl), $cond)
|
||
|
);
|
||
|
objet_dissocier(array($objet_source => $ids), array($objet_lie => $idl), $cond);
|
||
|
} else {
|
||
|
$oups = array_merge(
|
||
|
$oups,
|
||
|
objet_trouver_liens(array($objet_lie => $idl), array($objet_source => $ids), $cond)
|
||
|
);
|
||
|
objet_dissocier(array($objet_lie => $idl), array($objet_source => $ids), $cond);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
set_request('_oups', $oups ? base64_encode(serialize($oups)) : null);
|
||
|
}
|
||
|
|
||
|
if ($ajouter) {
|
||
|
if ($ajouter_objets = charger_fonction("editer_liens_ajouter_{$table_source}_{$objet}_{$objet_lien}", 'action', true)
|
||
|
) {
|
||
|
$ajout_ok = $ajouter_objets($ajouter);
|
||
|
} else {
|
||
|
$ajout_ok = false;
|
||
|
include_spip('action/editer_liens');
|
||
|
foreach ($ajouter as $k => $v) {
|
||
|
if ($lien = lien_verifier_action($k, $v)) {
|
||
|
$ajout_ok = true;
|
||
|
list($objet1, $ids, $objet2, $idl) = explode('-', $lien);
|
||
|
$qualifs = lien_retrouver_qualif($objet_lien, $lien);
|
||
|
if ($objet_lien == $objet1) {
|
||
|
lien_ajouter_liaisons($objet1, $ids, $objet2, $idl, $qualifs);
|
||
|
} else {
|
||
|
lien_ajouter_liaisons($objet2, $idl, $objet1, $ids, $qualifs);
|
||
|
}
|
||
|
set_request('id_lien_ajoute', $ids);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
# oups ne persiste que pour la derniere action, si suppression
|
||
|
# une suppression suivie d'un ajout dans le meme hit est un remplacement
|
||
|
# non annulable !
|
||
|
if ($ajout_ok) {
|
||
|
set_request('_oups');
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
return $res;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Retrouver l'action de liaision demandée
|
||
|
*
|
||
|
* Les formulaires envoient une action dans un tableau ajouter_lien
|
||
|
* ou supprimer_lien
|
||
|
*
|
||
|
* L'action est de la forme : objet1-id1-objet2-id2
|
||
|
* ou de la forme : objet1-id1-objet2-id2-role
|
||
|
*
|
||
|
* L'action peut-être indiquée dans la clé ou dans la valeur.
|
||
|
* Si elle est indiquee dans la valeur et que la clé est non numérique,
|
||
|
* on ne la prend en compte que si un submit avec la clé a été envoyé
|
||
|
*
|
||
|
* @internal
|
||
|
* @param string $k Clé du tableau
|
||
|
* @param string $v Valeur du tableau
|
||
|
* @return string Action demandée si trouvée, sinon ''
|
||
|
*/
|
||
|
function lien_verifier_action($k, $v) {
|
||
|
$action = '';
|
||
|
if (preg_match(',^\w+-[\w*]+-[\w*]+-[\w*]+(-[\w*])?,', $k)) {
|
||
|
$action = $k;
|
||
|
}
|
||
|
if (preg_match(',^\w+-[\w*]+-[\w*]+-[\w*]+(-[\w*])?,', $v)) {
|
||
|
if (is_numeric($k)) {
|
||
|
$action = $v;
|
||
|
}
|
||
|
if (_request($k)) {
|
||
|
$action = $v;
|
||
|
}
|
||
|
}
|
||
|
// ajout un role null fictif (plus pratique) si pas défini
|
||
|
if ($action and count(explode('-', $action)) == 4) {
|
||
|
$action .= '-';
|
||
|
}
|
||
|
|
||
|
return $action;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Retrouve le ou les qualificatifs postés avec une liaison demandée
|
||
|
*
|
||
|
* @internal
|
||
|
* @param string $objet_lien
|
||
|
* objet qui porte le lien
|
||
|
* @param string $lien
|
||
|
* Action du lien
|
||
|
* @return array
|
||
|
* Liste des qualifs pour chaque lien. Tableau vide s'il n'y en a pas.
|
||
|
**/
|
||
|
function lien_retrouver_qualif($objet_lien, $lien) {
|
||
|
// un role est défini dans la liaison
|
||
|
$defs = explode('-', $lien);
|
||
|
list($objet1, , $objet2, , $role) = $defs;
|
||
|
if ($objet_lien == $objet1) {
|
||
|
$colonne_role = roles_colonne($objet1, $objet2);
|
||
|
} else {
|
||
|
$colonne_role = roles_colonne($objet2, $objet1);
|
||
|
}
|
||
|
|
||
|
// cas ou le role est defini en 5e argument de l'action sur le lien (suppression, ajout rapide sans autre attribut)
|
||
|
if ($role) {
|
||
|
return array(
|
||
|
// un seul lien avec ce role
|
||
|
array($colonne_role => $role)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
// retrouver les rôles postés pour cette liaison, s'il y en a.
|
||
|
$qualifier_lien = _request('qualifier_lien');
|
||
|
if (!$qualifier_lien or !is_array($qualifier_lien)) {
|
||
|
return array();
|
||
|
}
|
||
|
|
||
|
// pas avec l'action complete (incluant le role)
|
||
|
$qualif = array();
|
||
|
if ((!isset($qualifier_lien[$lien]) or !$qualif = $qualifier_lien[$lien])
|
||
|
and count($defs) == 5
|
||
|
) {
|
||
|
// on tente avec l'action sans le role
|
||
|
array_pop($defs);
|
||
|
$lien = implode('-', $defs);
|
||
|
if (!isset($qualifier_lien[$lien]) or !$qualif = $qualifier_lien[$lien]) {
|
||
|
$qualif = array();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// $qualif de la forme array(role=>array(...),valeur=>array(...),....)
|
||
|
// on le reforme en array(array(role=>..,valeur=>..,..),array(role=>..,valeur=>..,..),...)
|
||
|
$qualifs = array();
|
||
|
while (count($qualif)) {
|
||
|
$q = array();
|
||
|
foreach ($qualif as $att => $values) {
|
||
|
if (is_array($values)) {
|
||
|
$q[$att] = array_shift($qualif[$att]);
|
||
|
if (!count($qualif[$att])) {
|
||
|
unset($qualif[$att]);
|
||
|
}
|
||
|
} else {
|
||
|
$q[$att] = $values;
|
||
|
unset($qualif[$att]);
|
||
|
}
|
||
|
}
|
||
|
// pas de rôle vide
|
||
|
if (!$colonne_role or !isset($q[$colonne_role]) or $q[$colonne_role]) {
|
||
|
$qualifs[] = $q;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $qualifs;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ajoute les liens demandés en prenant éventuellement en compte le rôle
|
||
|
*
|
||
|
* Appelle la fonction objet_associer. L'appelle autant de fois qu'il y
|
||
|
* a de rôles demandés pour cette liaison.
|
||
|
*
|
||
|
* @internal
|
||
|
* @param string $objet_source Objet source de la liaison (qui a la table de liaison)
|
||
|
* @param array|string $ids Identifiants pour l'objet source
|
||
|
* @param string $objet_lien Objet à lier
|
||
|
* @param array|string $idl Identifiants pour l'objet lié
|
||
|
* @param array $qualifs
|
||
|
* @return void
|
||
|
**/
|
||
|
function lien_ajouter_liaisons($objet_source, $ids, $objet_lien, $idl, $qualifs) {
|
||
|
|
||
|
// retrouver la colonne de roles s'il y en a a lier
|
||
|
if (is_array($qualifs) and count($qualifs)) {
|
||
|
foreach ($qualifs as $qualif) {
|
||
|
objet_associer(array($objet_source => $ids), array($objet_lien => $idl), $qualif);
|
||
|
}
|
||
|
} else {
|
||
|
objet_associer(array($objet_source => $ids), array($objet_lien => $idl));
|
||
|
}
|
||
|
}
|