Server : Apache System : Linux webd348.cluster026.gra.hosting.ovh.net 5.15.148-ovh-vps-grsec-zfs-classid #1 SMP Thu Feb 8 09:41:04 UTC 2024 x86_64 User : hednacluml ( 122243) PHP Version : 8.3.9 Disable Function : _dyuweyrj4,_dyuweyrj4r,dl Directory : /home/hednacluml/univers/ecrire/base/ |
<?php /***************************************************************************\ * SPIP, Système de publication pour l'internet * * * * Copyright © avec tendresse depuis 2001 * * Arnaud Martin, Antoine Pitrou, Philippe Rivière, Emmanuel Saint-James * * * * Ce programme est un logiciel libre distribué sous licence GNU/GPL. * \***************************************************************************/ use Spip\Compilateur\Noeud\Boucle; /** * Fonctions relatives aux objets éditoriaux et SQL * * @package SPIP\Core\SQL\Tables **/ if (!defined('_ECRIRE_INC_VERSION')) { return; } /** * Merge dans un tableau une de ses clés avec une valeur * * @param array $table * Tableau dont on veut compléter une clé * @param string $index * Clé du tableau que l'on souhaite compléter * @param array $valeur * Sous tableau à merger dans la clé. * @return void **/ function array_set_merge(&$table, $index, $valeur) { if (!isset($table[$index])) { $table[$index] = $valeur; } else { $table[$index] = array_merge($table[$index], $valeur); } } /** * Lister les infos de toutes les tables sql declarées * * Si un argument est fourni, on ne renvoie que les infos de cette table. * Elle est auto-declarée si inconnue jusqu'alors. * * @api * @param string|null $table_sql * table_sql demandee explicitement * @param array $desc * description connue de la table sql demandee * @return array|string * - array : description de la table ou des tables * - string (interne) si table '::md5' retourne un hash */ function lister_tables_objets_sql(?string $table_sql = null, $desc = []) { static $deja_la = false; static $infos_tables = null; static $md5 = null; static $plugin_hash = null; // plugins hash connu ? non si _CACHE_PLUGINS_OPT est pas encore chargé. $_PLUGINS_HASH = defined('_PLUGINS_HASH') ? _PLUGINS_HASH : '!_CACHE_PLUGINS_OPT'; // prealablement recuperer les tables_principales if (is_null($infos_tables) or $plugin_hash !== $_PLUGINS_HASH) { // pas de reentrance (cas base/serial) if ($deja_la) { spip_log('Re-entrance anormale sur lister_tables_objets_sql : ' . var_export(debug_backtrace(), true), _LOG_CRITIQUE); return ($table_sql === '::md5' ? $md5 : []); } $deja_la = true; $plugin_hash = $_PLUGINS_HASH; // avant de lancer les pipelines // recuperer les declarations explicites ancienne mode // qui servent a completer declarer_tables_objets_sql base_serial($GLOBALS['tables_principales']); base_auxiliaires($GLOBALS['tables_auxiliaires']); $infos_tables = [ 'spip_articles' => [ 'page' => 'article', 'texte_retour' => 'icone_retour_article', 'texte_modifier' => 'icone_modifier_article', 'texte_creer' => 'icone_ecrire_article', 'texte_objets' => 'public:articles', 'texte_objet' => 'public:article', 'texte_signale_edition' => 'texte_travail_article', 'info_aucun_objet' => 'info_aucun_article', 'info_1_objet' => 'info_1_article', 'info_nb_objets' => 'info_nb_articles', 'texte_logo_objet' => 'logo_article', 'texte_langue_objet' => 'titre_langue_article', 'texte_definir_comme_traduction_objet' => 'trad_lier', 'titre' => 'titre, lang', 'date' => 'date', 'principale' => 'oui', 'introduction_longueur' => '500', 'champs_editables' => [ 'surtitre', 'titre', 'soustitre', 'descriptif', 'nom_site', 'url_site', 'chapo', 'texte', 'ps', 'virtuel' ], 'champs_versionnes' => [ 'id_rubrique', 'surtitre', 'titre', 'soustitre', 'jointure_auteurs', 'descriptif', 'nom_site', 'url_site', 'chapo', 'texte', 'ps' ], 'field' => [ 'id_article' => 'bigint(21) NOT NULL', 'surtitre' => "text DEFAULT '' NOT NULL", 'titre' => "text DEFAULT '' NOT NULL", 'soustitre' => "text DEFAULT '' NOT NULL", 'id_rubrique' => "bigint(21) DEFAULT '0' NOT NULL", 'descriptif' => "text DEFAULT '' NOT NULL", 'chapo' => "mediumtext DEFAULT '' NOT NULL", 'texte' => "longtext DEFAULT '' NOT NULL", 'ps' => "mediumtext DEFAULT '' NOT NULL", 'date' => "datetime DEFAULT '0000-00-00 00:00:00' NOT NULL", 'statut' => "varchar(10) DEFAULT '0' NOT NULL", 'id_secteur' => "bigint(21) DEFAULT '0' NOT NULL", 'maj' => 'TIMESTAMP', 'export' => "VARCHAR(10) DEFAULT 'oui'", 'date_redac' => "datetime DEFAULT '0000-00-00 00:00:00' NOT NULL", 'visites' => "integer DEFAULT '0' NOT NULL", 'referers' => "integer DEFAULT '0' NOT NULL", 'popularite' => "DOUBLE DEFAULT '0' NOT NULL", 'accepter_forum' => "CHAR(3) DEFAULT '' NOT NULL", 'date_modif' => "datetime DEFAULT '0000-00-00 00:00:00' NOT NULL", 'lang' => "VARCHAR(10) DEFAULT '' NOT NULL", 'langue_choisie' => "VARCHAR(3) DEFAULT 'non'", 'id_trad' => "bigint(21) DEFAULT '0' NOT NULL", 'nom_site' => "tinytext DEFAULT '' NOT NULL", 'url_site' => "text DEFAULT '' NOT NULL", 'virtuel' => "text DEFAULT '' NOT NULL", ], 'key' => [ 'PRIMARY KEY' => 'id_article', 'KEY id_rubrique' => 'id_rubrique', 'KEY id_secteur' => 'id_secteur', 'KEY id_trad' => 'id_trad', 'KEY lang' => 'lang', 'KEY statut' => 'statut, date', ], 'join' => [ 'id_article' => 'id_article', 'id_rubrique' => 'id_rubrique' ], 'parent' => [ ['type' => 'rubrique', 'champ' => 'id_rubrique'] ], 'rechercher_champs' => [ 'surtitre' => 5, 'titre' => 8, 'soustitre' => 5, 'chapo' => 3, 'texte' => 1, 'ps' => 1, 'nom_site' => 1, 'url_site' => 1, 'descriptif' => 4 ], 'rechercher_jointures' => [ 'auteur' => ['nom' => 10], ], 'statut' => [ [ 'champ' => 'statut', 'publie' => 'publie', 'previsu' => 'publie,prop,prepa/auteur', 'post_date' => 'date', 'exception' => ['statut', 'tout'] ] ], 'statut_titres' => [ 'prepa' => 'info_article_redaction', 'prop' => 'info_article_propose', 'publie' => 'info_article_publie', 'refuse' => 'info_article_refuse', 'poubelle' => 'info_article_supprime' ], 'statut_textes_instituer' => [ 'prepa' => 'texte_statut_en_cours_redaction', 'prop' => 'texte_statut_propose_evaluation', 'publie' => 'texte_statut_publie', 'refuse' => 'texte_statut_refuse', 'poubelle' => 'texte_statut_poubelle', ], 'texte_changer_statut' => 'texte_article_statut', 'aide_changer_statut' => 'artstatut', 'tables_jointures' => [ 'profondeur' => 'rubriques', #'id_auteur' => 'auteurs_liens' // declaration generique plus bas ], ], 'spip_auteurs' => [ 'page' => 'auteur', 'texte_retour' => 'icone_retour', 'texte_ajouter' => 'titre_ajouter_un_auteur', 'texte_modifier' => 'admin_modifier_auteur', 'texte_objets' => 'icone_auteurs', 'texte_objet' => 'public:auteur', 'info_aucun_objet' => 'info_aucun_auteur', 'info_1_objet' => 'info_1_auteur', 'info_nb_objets' => 'info_nb_auteurs', 'texte_logo_objet' => 'logo_auteur', 'texte_creer_associer' => 'creer_et_associer_un_auteur', 'titre' => "nom AS titre, '' AS lang", 'date' => 'date', 'principale' => 'oui', 'champs_editables' => ['nom', 'email', 'lang', 'bio', 'nom_site', 'url_site', 'imessage', 'pgp'], 'champs_versionnes' => ['nom', 'bio', 'lang', 'email', 'nom_site', 'url_site', 'login'], 'field' => [ 'id_auteur' => 'bigint(21) NOT NULL', 'nom' => "text DEFAULT '' NOT NULL", 'bio' => "text DEFAULT '' NOT NULL", 'email' => "tinytext DEFAULT '' NOT NULL", 'nom_site' => "tinytext DEFAULT '' NOT NULL", 'url_site' => "text DEFAULT '' NOT NULL", 'login' => 'VARCHAR(255) BINARY', 'pass' => "tinytext DEFAULT '' NOT NULL", 'low_sec' => "tinytext DEFAULT '' NOT NULL", 'statut' => "varchar(255) DEFAULT '0' NOT NULL", 'webmestre' => "varchar(3) DEFAULT 'non' NOT NULL", 'maj' => 'TIMESTAMP', 'pgp' => "TEXT DEFAULT '' NOT NULL", 'htpass' => "tinytext DEFAULT '' NOT NULL", 'en_ligne' => "datetime DEFAULT '0000-00-00 00:00:00' NOT NULL", 'alea_actuel' => 'tinytext', 'alea_futur' => 'tinytext', 'prefs' => 'text', 'cookie_oubli' => 'tinytext', 'source' => "VARCHAR(10) DEFAULT 'spip' NOT NULL", 'lang' => "VARCHAR(10) DEFAULT '' NOT NULL", 'imessage' => "VARCHAR(3) DEFAULT '' NOT NULL", 'backup_cles' => "mediumtext DEFAULT '' NOT NULL", ], 'key' => [ 'PRIMARY KEY' => 'id_auteur', 'KEY login' => 'login', 'KEY statut' => 'statut', 'KEY en_ligne' => 'en_ligne', ], 'join' => [ 'id_auteur' => 'id_auteur', 'login' => 'login' ], 'rechercher_champs' => [ 'nom' => 5, 'bio' => 1, 'email' => 1, 'nom_site' => 1, 'url_site' => 1, 'login' => 1 ], // 2 conditions pour les auteurs : statut!=poubelle, // et avoir des articles publies 'statut' => [ [ 'champ' => 'statut', 'publie' => '!5poubelle', 'previsu' => '!5poubelle', 'exception' => 'statut' ], [ 'champ' => [ ['spip_auteurs_liens', 'id_auteur'], [ 'spip_articles', ['id_objet', 'id_article', 'objet', 'article'] ], 'statut' ], 'publie' => 'publie', 'previsu' => '!', 'post_date' => 'date', 'exception' => ['statut', 'lien', 'tout'] ], ], 'statut_images' => [ 'auteur-6forum-16.png', '0minirezo' => 'auteur-0minirezo-16.png', '1comite' => 'auteur-1comite-16.png', '6forum' => 'auteur-6forum-16.png', '5poubelle' => 'auteur-5poubelle-16.png', 'nouveau' => '' ], 'statut_titres' => [ 'titre_image_visiteur', '0minirezo' => 'titre_image_administrateur', '1comite' => 'titre_image_redacteur_02', '6forum' => 'titre_image_visiteur', '5poubelle' => 'titre_image_auteur_supprime', ], 'tables_jointures' => [#'auteurs_liens' // declaration generique plus bas ], ], 'spip_rubriques' => [ 'page' => 'rubrique', 'url_voir' => 'rubrique', 'url_edit' => 'rubrique_edit', 'texte_retour' => 'icone_retour', 'texte_objets' => 'public:rubriques', 'texte_objet' => 'public:rubrique', 'texte_modifier' => 'icone_modifier_rubrique', 'texte_creer' => 'icone_creer_rubrique', 'texte_ajouter' => 'titre_ajouter_une_rubrique', 'texte_creer_associer' => 'creer_et_associer_une_rubrique', 'info_aucun_objet' => 'info_aucun_rubrique', 'info_1_objet' => 'info_1_rubrique', 'info_nb_objets' => 'info_nb_rubriques', 'texte_logo_objet' => 'logo_rubrique', 'texte_langue_objet' => 'titre_langue_rubrique', 'texte_definir_comme_traduction_objet' => 'texte_definir_comme_traduction_rubrique', 'titre' => 'titre, lang', 'date' => 'date', 'principale' => 'oui', 'introduction_longueur' => '600', 'champs_editables' => ['titre', 'texte', 'descriptif', 'extra'], 'champs_versionnes' => ['titre', 'descriptif', 'texte'], 'field' => [ 'id_rubrique' => 'bigint(21) NOT NULL', 'id_parent' => "bigint(21) DEFAULT '0' NOT NULL", 'titre' => "text DEFAULT '' NOT NULL", 'descriptif' => "text DEFAULT '' NOT NULL", 'texte' => "longtext DEFAULT '' NOT NULL", 'id_secteur' => "bigint(21) DEFAULT '0' NOT NULL", 'maj' => 'TIMESTAMP', 'statut' => "varchar(10) DEFAULT '0' NOT NULL", 'date' => "datetime DEFAULT '0000-00-00 00:00:00' NOT NULL", 'lang' => "VARCHAR(10) DEFAULT '' NOT NULL", 'langue_choisie' => "VARCHAR(3) DEFAULT 'non'", 'statut_tmp' => "varchar(10) DEFAULT '0' NOT NULL", 'date_tmp' => "datetime DEFAULT '0000-00-00 00:00:00' NOT NULL", 'profondeur' => "smallint(5) DEFAULT '0' NOT NULL" ], 'key' => [ 'PRIMARY KEY' => 'id_rubrique', 'KEY lang' => 'lang', 'KEY id_parent' => 'id_parent', ], 'parent' => [ ['type' => 'rubrique', 'champ' => 'id_parent'] ], 'rechercher_champs' => [ 'titre' => 8, 'descriptif' => 5, 'texte' => 1 ], 'statut' => [ [ 'champ' => 'statut', 'publie' => 'publie', 'previsu' => '!', 'exception' => ['statut', 'tout'] ], ], 'tables_jointures' => [#'id_auteur' => 'auteurs_liens' // declaration generique plus bas ], ], // toutes les tables ont le droit a une jointure sur les auteurs ['tables_jointures' => ['id_auteur' => 'auteurs_liens']] ]; // avant d'appeller les pipeline qui peuvent generer une reentrance a l'install // initialiser la signature $md5 = md5(serialize($infos_tables)); $GLOBALS['tables_principales'] = pipeline('declarer_tables_principales', $GLOBALS['tables_principales']); $GLOBALS['tables_auxiliaires'] = pipeline('declarer_tables_auxiliaires', $GLOBALS['tables_auxiliaires']); $infos_tables = pipeline('declarer_tables_objets_sql', $infos_tables); // completer les informations manquantes ou implicites $all = []; foreach (array_keys($infos_tables) as $t) { // les cles numeriques servent a declarer // les proprietes applicables a tous les objets // on les mets de cote if (is_numeric($t)) { $all = array_merge_recursive($all, $infos_tables[$t]); unset($infos_tables[$t]); } else { $infos_tables[$t] = renseigner_table_objet_sql($t, $infos_tables[$t]); } } // repercuter les proprietes generales communes a tous les objets foreach (array_keys($infos_tables) as $t) { foreach ($all as $i => $v) { if (in_array($i, ['tables_jointures', 'champs_versionnes'])) { $add = $all[$i]; // eviter les doublons de declaration de table jointure (ex des mots sur auteurs) // pour les declarations generiques avec cles numeriques if ($i == 'tables_jointures' and isset($infos_tables[$t][$i]) and is_countable($infos_tables[$t][$i]) ? count($infos_tables[$t][$i]) : 0) { $doublons = array_intersect($infos_tables[$t][$i], $add); foreach ($doublons as $d) { if ( is_numeric(array_search($d, $infos_tables[$t][$i])) and is_numeric($k = array_search($d, $add)) ) { unset($add[$k]); } } } $infos_tables[$t][$i] = array_merge($infos_tables[$t][$i] ?? [], $add); } else { $infos_tables[$t][$i] = array_merge_recursive( $infos_tables[$t][$i] ?? [], $all[$i] ); } } } // completer les tables principales et auxiliaires // avec celles declarees uniquement dans declarer_table_objets_sql // pour assurer la compat en transition foreach ($infos_tables as $table => $infos) { $principale_ou_auxiliaire = ($infos['principale'] ? 'tables_principales' : 'tables_auxiliaires'); // memoriser des champs eventuels declares par des plugins dans le pipeline tables_xxx // qui a ete appelle avant $mem = ($GLOBALS[$principale_ou_auxiliaire][$table] ?? []); // l'ajouter au tableau $GLOBALS[$principale_ou_auxiliaire][$table] = []; if (isset($infos['field']) and isset($infos['key'])) { foreach (['field', 'key', 'join'] as $k) { if (isset($infos_tables[$table][$k])) { $GLOBALS[$principale_ou_auxiliaire][$table][$k] = &$infos_tables[$table][$k]; } } } else { // ici on ne renvoie que les declarations, donc RIEN // pour avoir la vrai description en base, il faut passer par trouver_table $GLOBALS[$principale_ou_auxiliaire][$table] = []; } if (is_countable($mem) ? count($mem) : 0) { foreach (array_keys($mem) as $k) { if (isset($GLOBALS[$principale_ou_auxiliaire][$table][$k])) { $GLOBALS[$principale_ou_auxiliaire][$table][$k] = array_merge( $GLOBALS[$principale_ou_auxiliaire][$table][$k], $mem[$k] ); } else { $GLOBALS[$principale_ou_auxiliaire][$table][$k] = $mem[$k]; } } } } // recuperer les interfaces (table_titre, table_date) // on ne le fait que dans un second temps pour que table_objet soit fonctionnel // dans le pipeline de declarer_tables_interfaces include_spip('public/interfaces'); foreach (array_keys($infos_tables) as $t) { $infos_tables[$t] = renseigner_table_objet_interfaces($t, $infos_tables[$t]); } $deja_la = false; // signature $md5 = md5(serialize($infos_tables)); } if ($table_sql === '::md5') { return $md5; } if ($table_sql and !isset($infos_tables[$table_sql])) { #$desc = renseigner_table_objet_sql($table_sql,$desc); $desc = renseigner_table_objet_interfaces($table_sql, $desc); return $desc; } if ($table_sql) { return $infos_tables[$table_sql] ?? []; } return $infos_tables; } /** * Déclare les tables principales du Core * * Tables principales, hors objets éditoriaux. * * @param array $tables_principales * Description des tables principales déjà déclarées * @return void **/ function base_serial(&$tables_principales) { $spip_jobs = [ 'id_job' => 'bigint(21) NOT NULL', 'descriptif' => "text DEFAULT '' NOT NULL", 'fonction' => 'varchar(255) NOT NULL', //nom de la fonction 'args' => "longblob DEFAULT '' NOT NULL", // arguments 'md5args' => "char(32) NOT NULL default ''", // signature des arguments 'inclure' => 'varchar(255) NOT NULL', // fichier a inclure ou path/ pour charger_fonction 'priorite' => 'smallint(6) NOT NULL default 0', 'date' => "datetime DEFAULT '0000-00-00 00:00:00' NOT NULL", // date au plus tot 'status' => 'tinyint NOT NULL default 1', ]; $spip_jobs_key = [ 'PRIMARY KEY' => 'id_job', 'KEY date' => 'date', 'KEY status' => 'status', ]; /// Attention: mes_fonctions peut avoir deja defini cette variable /// il faut donc rajouter, mais pas reinitialiser $tables_principales['spip_jobs'] = ['field' => &$spip_jobs, 'key' => &$spip_jobs_key]; } /** * Déclare les tables auxiliaires du Core * * @param array $tables_auxiliaires * Description des tables auxiliaires déjà déclarées * @return void **/ function base_auxiliaires(&$tables_auxiliaires) { $spip_resultats = [ 'recherche' => "char(16) DEFAULT '' NOT NULL", 'id' => 'INT UNSIGNED NOT NULL', 'points' => "INT UNSIGNED DEFAULT '0' NOT NULL", 'table_objet' => "varchar(30) DEFAULT '' NOT NULL", 'serveur' => "char(16) DEFAULT '' NOT NULL", // hash md5 partiel du serveur de base ('' pour le serveur principal) 'maj' => 'TIMESTAMP' ]; $spip_resultats_key = [// pas de cle ni index, ca fait des insertions plus rapides et les requetes jointes utilisees en recheche ne sont pas plus lentes ... ]; $spip_auteurs_liens = [ 'id_auteur' => "bigint(21) DEFAULT '0' NOT NULL", 'id_objet' => "bigint(21) DEFAULT '0' NOT NULL", 'objet' => "VARCHAR (25) DEFAULT '' NOT NULL", 'vu' => "VARCHAR(6) DEFAULT 'non' NOT NULL" ]; $spip_auteurs_liens_key = [ 'PRIMARY KEY' => 'id_auteur,id_objet,objet', 'KEY id_auteur' => 'id_auteur', 'KEY id_objet' => 'id_objet', 'KEY objet' => 'objet', ]; $spip_meta = [ 'nom' => 'VARCHAR (255) NOT NULL', 'valeur' => "text DEFAULT ''", 'impt' => "ENUM('non', 'oui') DEFAULT 'oui' NOT NULL", 'maj' => 'TIMESTAMP' ]; $spip_meta_key = [ 'PRIMARY KEY' => 'nom' ]; $spip_jobs_liens = [ 'id_job' => "bigint(21) DEFAULT '0' NOT NULL", 'id_objet' => "bigint(21) DEFAULT '0' NOT NULL", 'objet' => "VARCHAR (25) DEFAULT '' NOT NULL", ]; $spip_jobs_liens_key = [ 'PRIMARY KEY' => 'id_job,id_objet,objet', 'KEY id_job' => 'id_job' ]; $tables_auxiliaires['spip_auteurs_liens'] = [ 'field' => &$spip_auteurs_liens, 'key' => &$spip_auteurs_liens_key ]; $tables_auxiliaires['spip_meta'] = [ 'field' => &$spip_meta, 'key' => &$spip_meta_key ]; $tables_auxiliaires['spip_resultats'] = [ 'field' => &$spip_resultats, 'key' => &$spip_resultats_key ]; $tables_auxiliaires['spip_jobs_liens'] = [ 'field' => &$spip_jobs_liens, 'key' => &$spip_jobs_liens_key ]; } /** * Auto remplissage des informations non explicites * sur un objet d'une table sql * * - table_objet * - table_objet_surnoms * - type * - type_surnoms * - url_voir * - url_edit * - icone_objet * * - texte_retour * - texte_modifier * - texte_creer * - texte_creer_associer * - texte_ajouter * - texte_objets * - texte_objet * * - info_aucun_objet * - info_1_objet * - info_nb_objets * * - texte_logo_objet * - texte_langue_objet * - texte_definir_comme_traduction_objet * * - principale * - champs_contenu : utlisé pour générer l'affichage par défaut du contenu * - editable * - champs_editables : utilisé pour prendre en compte le post lors de l'édition * * - champs_versionnes * * L'objet doit définir de lui même ces champs pour gérer des statuts : * - statut * - statut_images * - statut_titres * - statut_textes_instituer * - texte_changer_statut * - aide_changer_statut * * - modeles : permet de declarer les modeles associes a cet objet * * Les infos non renseignées sont auto-déduites par conventions * ou laissées vides * * @param string $table_sql * @param array $infos * @return array */ function renseigner_table_objet_sql($table_sql, &$infos) { if (!isset($infos['type'])) { // si on arrive de base/trouver_table, on a la cle primaire : // s'en servir pour extrapoler le type if (isset($infos['key']['PRIMARY KEY'])) { $primary = $infos['key']['PRIMARY KEY']; $primary = explode(',', $primary); $primary = reset($primary); $infos['type'] = preg_replace(',^spip_|^id_|s$,', '', $primary); } else { $infos['type'] = preg_replace(',^spip_|s$,', '', $table_sql); } } if (!isset($infos['type_surnoms'])) { $infos['type_surnoms'] = []; } if (!isset($infos['table_objet'])) { $infos['table_objet'] = preg_replace(',^spip_,', '', $table_sql); } if (!isset($infos['table_objet_surnoms'])) { $infos['table_objet_surnoms'] = []; } if (!isset($infos['principale'])) { $infos['principale'] = (isset($GLOBALS['tables_principales'][$table_sql]) ? 'oui' : false); } // normaliser pour pouvoir tester en php $infos['principale']? // et dans une boucle {principale=oui} $infos['principale'] = (($infos['principale'] and $infos['principale'] != 'non') ? 'oui' : false); // declarer et normaliser pour pouvoir tester en php $infos['editable']? // et dans une boucle {editable=oui} if (!isset($infos['editable'])) { $infos['editable'] = 'oui'; } $infos['editable'] = (($infos['editable'] and $infos['editable'] != 'non') ? 'oui' : false); // les urls publiques sont par defaut page=type pour les tables principales, et rien pour les autres // seules les exceptions sont donc a declarer if (!isset($infos['page'])) { $infos['page'] = ($infos['principale'] ? $infos['type'] : ''); } if (!isset($infos['url_voir'])) { $infos['url_voir'] = $infos['type']; } if (!isset($infos['url_edit'])) { $infos['url_edit'] = $infos['url_voir'] . ($infos['editable'] ? '_edit' : ''); } if (!isset($infos['icone_objet'])) { $infos['icone_objet'] = $infos['type']; } // chaines de langue // par defaut : objet:icone_xxx_objet if (!isset($infos['texte_retour'])) { $infos['texte_retour'] = 'icone_retour'; } if (!isset($infos['texte_modifier'])) { $infos['texte_modifier'] = $infos['type'] . ':' . 'icone_modifier_' . $infos['type']; } if (!isset($infos['texte_creer'])) { $infos['texte_creer'] = $infos['type'] . ':' . 'icone_creer_' . $infos['type']; } if (!isset($infos['texte_creer_associer'])) { $infos['texte_creer_associer'] = $infos['type'] . ':' . 'texte_creer_associer_' . $infos['type']; } if (!isset($infos['texte_ajouter'])) { // Ajouter un X $infos['texte_ajouter'] = $infos['type'] . ':' . 'texte_ajouter_' . $infos['type']; } if (!isset($infos['texte_objets'])) { $infos['texte_objets'] = $infos['type'] . ':' . 'titre_' . $infos['table_objet']; } if (!isset($infos['texte_objet'])) { $infos['texte_objet'] = $infos['type'] . ':' . 'titre_' . $infos['type']; } if (!isset($infos['texte_logo_objet'])) { // objet:titre_logo_objet "Logo de ce X" $infos['texte_logo_objet'] = $infos['type'] . ':' . 'titre_logo_' . $infos['type']; } if (!isset($infos['texte_langue_objet'])) { // objet:texte_langue_objet "Langue de ce X" $infos['texte_langue_objet'] = $infos['type'] . ':' . 'titre_langue_' . $infos['type']; } if (!isset($infos['texte_definir_comme_traduction_objet'])) { // "Ce X est une traduction du X numéro :" $infos['texte_definir_comme_traduction_objet'] = $infos['type'] . ':' . 'texte_definir_comme_traduction_' . $infos['type']; } // objet:info_aucun_objet if (!isset($infos['info_aucun_objet'])) { $infos['info_aucun_objet'] = $infos['type'] . ':' . 'info_aucun_' . $infos['type']; } // objet:info_1_objet if (!isset($infos['info_1_objet'])) { $infos['info_1_objet'] = $infos['type'] . ':' . 'info_1_' . $infos['type']; } // objet:info_nb_objets if (!isset($infos['info_nb_objets'])) { $infos['info_nb_objets'] = $infos['type'] . ':' . 'info_nb_' . $infos['table_objet']; } if (!isset($infos['champs_editables'])) { $infos['champs_editables'] = []; } if (!isset($infos['champs_versionnes'])) { $infos['champs_versionnes'] = []; } if (!isset($infos['rechercher_champs'])) { $infos['rechercher_champs'] = []; } if (!isset($infos['rechercher_jointures'])) { $infos['rechercher_jointures'] = []; } if (!isset($infos['modeles'])) { $infos['modeles'] = [$infos['type']]; } return $infos; } /** * Renseigner les infos d'interface compilateur pour les tables objets * complete la declaration precedente * * titre * date * statut * tables_jointures * * @param $table_sql * @param $infos * @return array */ function renseigner_table_objet_interfaces($table_sql, &$infos) { if (!isset($infos['titre'])) { if (isset($infos['table_objet']) and isset($GLOBALS['table_titre'][$infos['table_objet']])) { $infos['titre'] = $GLOBALS['table_titre'][$infos['table_objet']]; } else { $infos['titre'] = ((isset($infos['field']['titre'])) ? 'titre,' : "'' as titre,"); $infos['titre'] .= ((isset($infos['field']['lang'])) ? 'lang' : "'' as lang"); } } if (!isset($infos['date'])) { if (isset($infos['table_objet']) and isset($GLOBALS['table_date'][$infos['table_objet']])) { $infos['date'] = $GLOBALS['table_date'][$infos['table_objet']]; } else { $infos['date'] = ((isset($infos['field']['date'])) ? 'date' : ''); } } $infos['statut'] ??= $GLOBALS['table_statut'][$table_sql] ?? ''; $infos['tables_jointures'] ??= []; if (isset($GLOBALS['tables_jointures'][$table_sql])) { $infos['tables_jointures'] = array_merge($infos['tables_jointures'], $GLOBALS['tables_jointures'][$table_sql]); } return $infos; } /** * Retourne la liste des tables principales et leurs descriptions * * @api * @return array * Liste et descriptions des tables principales **/ function lister_tables_principales() { static $done = false; if (!$done or !(is_countable($GLOBALS['tables_principales']) ? count($GLOBALS['tables_principales']) : 0)) { lister_tables_objets_sql(); $done = true; } return $GLOBALS['tables_principales']; } /** * Retourne la liste des tables auxiliaires et leurs descriptions * * @api * @return array * Liste et descriptions des tables auxiliaires **/ function lister_tables_auxiliaires() { static $done = false; if (!$done or !(is_countable($GLOBALS['tables_auxiliaires']) ? count($GLOBALS['tables_auxiliaires']) : 0)) { lister_tables_objets_sql(); $done = true; } return $GLOBALS['tables_auxiliaires']; } /** * Recenser les surnoms de table_objet * * @return array */ function lister_tables_objets_surnoms() { static $surnoms = null; static $md5 = null; if ( !$surnoms or $md5 != lister_tables_objets_sql('::md5') ) { // passer dans un pipeline qui permet aux plugins de declarer leurs exceptions // pour compatibilite, car il faut dorenavent utiliser // declarer_table_objets_sql $surnoms = pipeline( 'declarer_tables_objets_surnoms', [ # pour les modeles # a enlever ? 'doc' => 'documents', 'img' => 'documents', 'emb' => 'documents', ] ); $infos_tables = lister_tables_objets_sql(); foreach ($infos_tables as $t => $infos) { // cas de base type=>table // et preg_replace(',^spip_|^id_|s$,',table)=>table if ($infos['table_objet']) { // securite, si la fonction est appelee trop tot, c'est vide // optimisations pour table_objet //$surnoms[$infos['type']] = $infos['table_objet']; $surnoms[preg_replace(',^spip_|^id_|s$,', '', $infos['table_objet'])] = $infos['table_objet']; $surnoms[preg_replace(',^spip_|^id_|s$,', '', $infos['type'])] = $infos['table_objet']; if (is_array($infos['table_objet_surnoms']) and count($infos['table_objet_surnoms'])) { foreach ($infos['table_objet_surnoms'] as $surnom) { $surnoms[$surnom] = $infos['table_objet']; } } } } $md5 = lister_tables_objets_sql('::md5'); } return $surnoms; } /** * Recenser les surnoms de table_objet * * @return array */ function lister_types_surnoms() { static $surnoms = null; static $md5 = null; if ( !$surnoms or $md5 != lister_tables_objets_sql('::md5') ) { // passer dans un pipeline qui permet aux plugins de declarer leurs exceptions // pour compatibilite, car il faut dorenavent utiliser // declarer_table_objets_sql $surnoms = pipeline('declarer_type_surnoms', ['racine-site' => 'site']); $infos_tables = lister_tables_objets_sql(); foreach ($infos_tables as $t => $infos) { if ($infos['type']) { // securite, si la fonction est appelee trop tot, c'est vide // optimisations pour objet_type //$surnoms[$infos['type']] = $infos['type']; $surnoms[preg_replace(',^spip_|^id_|s$,', '', $infos['table_objet'])] = $infos['type']; $surnoms[preg_replace(',^spip_|^id_|s$,', '', $infos['type'])] = $infos['type']; // surnoms declares if (is_array($infos['type_surnoms']) and count($infos['type_surnoms'])) { foreach ($infos['type_surnoms'] as $surnom) { $surnoms[$surnom] = $infos['type']; } } } } $md5 = lister_tables_objets_sql('::md5'); } return $surnoms; } /** * Retourne la liste des tables SQL qui concernent SPIP * * Cette liste n'est calculée qu'une fois par serveur pour l'ensemble du hit * * @param string $serveur * Nom du fichier de connexion à la base de données * @return array * Couples (nom de la table SQL => même nom, sans 'spip_' devant) **/ function lister_tables_spip($serveur = '') { static $tables = []; if (!isset($tables[$serveur])) { $tables[$serveur] = []; if (!function_exists('sql_alltable')) { include_spip('base/abstract_sql'); } $ts = sql_alltable(null, $serveur); // toutes les tables "spip_" (ou prefixe perso) $connexion = $GLOBALS['connexions'][$serveur ?: 0]; $spip = $connexion['prefixe'] . '_'; foreach ($ts as $t) { $t = substr($t, strlen($spip)); $tables[$serveur]["spip_$t"] = $t; } } return $tables[$serveur]; } /** * Retourne la liste des tables SQL, Spip ou autres * * Cette liste n'est calculée qu'une fois par serveur pour l'ensemble du hit * * @param string $serveur * Nom du fichier de connexion à la base de données * @return array * Couples (nom de la table SQL => même nom) **/ function lister_toutes_tables($serveur) { static $tables = []; if (!isset($tables[$serveur])) { $tables[$serveur] = []; if (!function_exists('sql_alltable')) { include_spip('base/abstract_sql'); } $ts = sql_alltable('%', $serveur); // toutes les tables foreach ($ts as $t) { $tables[$serveur][$t] = $t; } } return $tables[$serveur]; } /** * Retrouve le nom d'objet à partir de la table * * - spip_articles -> articles * - id_article -> articles * - article -> articles * * @api * @param string $type * Nom de la table SQL (le plus souvent) * Tolère un nom de clé primaire. * @param string|false $serveur * - string: Nom du connecteur * - false: Pas de recherche en bdd * @return string * Nom de l'objet **/ function table_objet(string $type, $serveur = ''): string { if ($type) { $type = preg_replace(',^spip_|^id_|s$,', '', $type); } if (!strlen($type)) { return ''; } $surnoms = lister_tables_objets_surnoms(); if (isset($surnoms[$type])) { return $surnoms[$type]; } if ($serveur !== false) { $serveur = (string) $serveur; $t = lister_tables_spip($serveur); $trouver_table = charger_fonction('trouver_table', 'base'); $typetrim = rtrim($type, 's') . 's'; if ( (isset($t[$typetrim]) or in_array($typetrim, $t)) and ($desc = $trouver_table(rtrim($type, 's') . 's', $serveur)) ) { return $desc['id_table']; } elseif ( (isset($t[$type]) or in_array($type, $t)) and ($desc = $trouver_table($type, $serveur)) ) { return $desc['id_table']; } spip_log('table_objet(' . $type . ') calculee sans verification'); #spip_log(debug_backtrace(),'db'); } return rtrim($type, 's') . 's'; # cas historique ne devant plus servir, sauf si $serveur=false } /** * Retrouve la table sql à partir de l'objet ou du type * * - articles -> spip_articles * - article -> spip_articles * - id_article -> spip_articles * * @api * @param string $type * Nom ou type de l'objet * Tolère un nom de clé primaire. * @param string|false $serveur * - string: Nom du connecteur * - false: Pas de recherche en bdd * @return string * Nom de la table SQL **/ function table_objet_sql(string $type, $serveur = ''): string { $nom = table_objet($type, $serveur); if (!strlen($nom)) { return ''; } if (!isset($GLOBALS['table_des_tables']['articles'])) { // eviter de multiples inclusions include_spip('public/interfaces'); } if (isset($GLOBALS['table_des_tables'][$nom])) { $nom = $GLOBALS['table_des_tables'][$nom]; $nom = "spip_$nom"; } else { $infos_tables = lister_tables_objets_sql(); if (isset($infos_tables["spip_$nom"])) { $nom = "spip_$nom"; } elseif ($serveur !== false) { $serveur = (string) $serveur; $t = lister_tables_spip($serveur); if (isset($t[$nom]) or in_array($nom, $t)) { $trouver_table = charger_fonction('trouver_table', 'base'); if ($desc = $trouver_table($nom, $serveur)) { return $desc['table_sql']; } } } } return $nom; } /** * Retrouve la clé primaire à partir du nom d'objet ou de table * * - articles -> id_article * - article -> id_article * - spip_articles -> id_article * * @api * @param string $type * Nom de la table SQL ou de l'objet * @param string $serveur * Nom du connecteur * @return string|null * Nom de la clé primaire **/ function id_table_objet(string $type, string $serveur = ''): ?string { static $trouver_table = null; $type = objet_type($type, $serveur); if (!$type) { return null; } $t = table_objet($type); if (!$trouver_table) { $trouver_table = charger_fonction('trouver_table', 'base'); } $ts = lister_tables_spip($serveur); if ( in_array($t, $ts) or in_array($t, lister_toutes_tables($serveur)) ) { $desc = $trouver_table($t, $serveur); if (isset($desc['key']['PRIMARY KEY'])) { return $desc['key']['PRIMARY KEY']; } if (!$desc or isset($desc['field']["id_$type"])) { return "id_$type"; } // sinon renvoyer le premier champ de la table... $keys = array_keys($desc['field']); return array_shift($keys); } return "id_$type"; } /** * Retrouve le type d'objet à partir du nom d'objet ou de table * * - articles -> article * - spip_articles -> article * - id_article -> article * * @api * @param string $table_objet * Nom de l'objet ou de la table SQL * @param string|false $serveur * - string: Nom du connecteur * - false: Pas de recherche en bdd * @return string|null * Type de l'objet **/ function objet_type(string $table_objet, $serveur = ''): ?string { if (!$table_objet) { return null; } $surnoms = lister_types_surnoms(); // scenario de base // le type est decline a partir du nom de la table en enlevant le prefixe eventuel // et la marque du pluriel // on accepte id_xx en entree aussi $type = preg_replace(',^spip_|^id_|s$,', '', $table_objet); if (isset($surnoms[$type])) { return $surnoms[$type]; } // securite : eliminer les caracteres non \w $type = preg_replace(',[^\w-],', '', $type); // si le type redonne bien la table c'est bon // oui si table_objet ressemblait deja a un type if ( $type == $table_objet or (table_objet($type, $serveur) == $table_objet) or (table_objet_sql($type, $serveur) == $table_objet) ) { return $type; } // si on ne veut pas chercher en base if ($serveur === false) { return $type; } $serveur = (string) $serveur; // sinon on passe par la cle primaire id_xx pour trouver le type // car le s a la fin est incertain // notamment en cas de pluriel derogatoire // id_jeu/spip_jeux id_journal/spip_journaux qui necessitent tout deux // une declaration jeu => jeux, journal => journaux // dans le pipeline declarer_tables_objets_surnoms $trouver_table = charger_fonction('trouver_table', 'base'); $ts = lister_tables_spip($serveur); $desc = false; if (in_array($table_objet, $ts)) { $desc = $trouver_table($table_objet); } if (!$desc and in_array($table_objet = table_objet($type, $serveur), $ts)) { $desc = $trouver_table($table_objet, $serveur); } // si le type est declare : bingo ! if ($desc and isset($desc['type'])) { return $desc['type']; } // on a fait ce qu'on a pu return $type; } /** * Determiner si un objet est publie ou non * * On se base pour cela sur sa declaration de statut * pour des cas particuliers non declarables, on permet de fournir une fonction * base_xxxx_test_si_publie qui sera appele par la fonction * * @param string $objet * @param int $id_objet * @param string $serveur * @return bool */ function objet_test_si_publie($objet, $id_objet, $serveur = '') { // voir si une fonction est definie pour faire le boulot // elle a la priorite dans ce cas if ($f = charger_fonction($objet . '_test_si_publie', 'base', true)) { return $f($objet, $id_objet, $serveur); } // sinon on se fie a la declaration de l'objet si presente $id_table = $table_objet = table_objet($objet); $id_table_objet = id_table_objet($objet, $serveur); $trouver_table = charger_fonction('trouver_table', 'base'); if ( $desc = $trouver_table($table_objet, $serveur) and isset($desc['statut']) and $desc['statut'] ) { $boucle = new Boucle(); $boucle->show = $desc; $boucle->nom = 'objet_test_si_publie'; $boucle->id_boucle = $id_table; $boucle->id_table = $id_table; $boucle->primary = $desc['key']['PRIMARY KEY'] ?? ''; $boucle->sql_serveur = $serveur; $boucle->select[] = $id_table_objet; $boucle->from[$table_objet] = table_objet_sql($objet, $serveur); $boucle->where[] = $id_table . '.' . $id_table_objet . '=' . intval($id_objet); $boucle->descr['nom'] = 'objet_test_si_publie'; // eviter notice php $boucle->descr['sourcefile'] = 'internal'; $boucle->descr['gram'] = 'html'; include_spip('public/compiler'); include_spip('public/composer'); instituer_boucle($boucle, false, true); $res = calculer_select( $boucle->select, $boucle->from, $boucle->from_type, $boucle->where, $boucle->join, $boucle->group, $boucle->order, $boucle->limit, $boucle->having, $table_objet, $id_table, $serveur ); if (sql_fetch($res)) { return true; } return false; } // si pas d'info statut ni de fonction : l'objet est publie return true; } /** * Cherche les contenus parent d'un contenu précis. * * comme : * ``` * $tables['spip_auteurs']['parent'] = array( * 'type' => 'organisation', * 'champ' => 'id_organisation', * 'table' => 'spip_organisations_liens', * 'table_condition' => 'role="parent"', * 'source_champ' => 'id_objet', * 'champ_type' => 'objet' * ); * ``` * * La fonction retourne un tableau de parents, chacun de la forme * ['objet' => '...', 'id_objet' => X, 'table' => '...', 'champ' => '...'] * Si la table utilisée pour trouver le parent est une table de liens (finissant par _liens), * le tableau contient en plus en entrée 'lien' qui contient les informations complètes du lien (rang, role...) * * @api * @param string $objet * Type de l'objet dont on cherche les parent * @param int $id_objet * Identifiant de l'objet dont on cherche les parent * @param bool $parent_direct_seulement * ne considerer que les relations directes et non via table de liens * @return array * Retourne un tableau décrivant les parents trouvés */ function objet_lister_parents($objet, $id_objet, $parent_direct_seulement = false) { $parents = []; // Si on trouve une ou des méthodes de parent if ($parent_methodes = objet_type_decrire_infos_parents($objet)) { // On identifie les informations sur l'objet source dont on cherche le parent. include_spip('base/abstract_sql'); $table_objet = table_objet_sql($objet); $cle_objet = id_table_objet($objet); $id_objet = intval($id_objet); // On teste chacun méthode dans l'ordre, et dès qu'on a trouvé un parent on s'arrête foreach ($parent_methodes as $parent_methode) { // Champ identifiant le parent (id et éventuellement le type) // -- cette identification ne dépend pas du fait que le parent soit stocké dans une table de différente // de celle de l'objet source $select = []; if (isset($parent_methode['champ'])) { $select[] = $parent_methode['champ']; } if (isset($parent_methode['champ_type'])) { $select[] = $parent_methode['champ_type']; } // Détermination de la table du parent et des conditions sur l'objet source et le parent. $condition_objet_invalide = false; $where = []; if (!isset($parent_methode['table'])) { // Le parent est stocké dans la même table que l'objet source : // -- toutes les conditions s'appliquent à la table source. $table = $table_objet; $where = ["$cle_objet = $id_objet"]; // -- Condition supplémentaire sur la détection du parent if (isset($parent_methode['condition'])) { $where[] = $parent_methode['condition']; } } elseif (!$parent_direct_seulement) { // Le parent est stocké dans une table différente de l'objet source. // -- on vérifie d'emblée si il y a une condition sur l'objet source et si celle-ci est vérifiée // Si non, on peut arrêter le traitement. if (isset($parent_methode['condition'])) { $where = [ "$cle_objet = $id_objet", $parent_methode['condition'] ]; if (!sql_countsel($table_objet, $where)) { $condition_objet_invalide = true; } } // Si pas de condition sur l'objet source ou que la condition est vérifiée, on peut construire // la requête sur la table qui accueille le parent. if (!$condition_objet_invalide) { $table = $parent_methode['table']; // On construit les conditions en fonction de l'identification de l'objet source $where = []; // -- si le champ_source de l'id n'est pas précisé c'est qu'il est déjà connu et donc que c'est // le même que celui de l'objet source. $where[] = isset($parent_methode['source_champ']) ? "{$parent_methode['source_champ']} = $id_objet" : "$cle_objet = $id_objet"; if (isset($parent_methode['source_champ_type'])) { $where[] = "{$parent_methode['source_champ_type']} = " . sql_quote($objet); } // -- Condition supplémentaire sur la détection du parent if (isset($parent_methode['table_condition'])) { $where[] = $parent_methode['table_condition']; } } } // On lance la requête de récupération du parent $is_table_lien = (strpos($table, '_liens') !== false and substr($table, -6) === '_liens'); if ( !$condition_objet_invalide and $where and ($lignes = sql_allfetsel($is_table_lien ? '*' : $select, $table, $where)) ) { foreach ($lignes as $ligne) { // Si le type est fixe if (isset($parent_methode['type'])) { $parent = [ 'objet' => $parent_methode['type'], 'id_objet' => intval($ligne[$parent_methode['champ']]), 'champ' => $parent_methode['champ'], 'table' => $table, ]; } elseif (isset($parent_methode['champ_type'])) { $parent = [ 'objet' => $ligne[$parent_methode['champ_type']], 'id_objet' => intval($ligne[$parent_methode['champ']]), 'champ' => $parent_methode['champ'], 'champ_type' => $parent_methode['champ_type'], 'table' => $table, ]; } if ($is_table_lien) { $parent['lien'] = $ligne; } $parents[] = $parent; } } } } // On passe par un pipeline avant de retourner $parents = pipeline( 'objet_lister_parents', [ 'args' => [ 'objet' => $objet, 'id_objet' => $id_objet, ], 'data' => $parents, ] ); return $parents; } /** * Fonction helper qui permet de récupérer une liste simplifiée des parents, regroupés par objet * [ 'objet1' => [ids...], 'objet2' => [ids...] ] * * @param $objet * @param $id_objet * @return array */ function objet_lister_parents_par_type($objet, $id_objet) { $parents = objet_lister_parents($objet, $id_objet); $parents_par_type = []; foreach ($parents as $parent) { if (!isset($parents_par_type[$parent['objet']])) { $parents_par_type[$parent['objet']] = []; } $parents_par_type[$parent['objet']][] = $parent['id_objet']; } return $parents_par_type; } /** * Cherche tous les contenus enfants d'un contenu précis * * comme : * ``` * $tables['spip_auteurs']['parent'] = array( * 'type' => 'organisation', * 'champ' => 'id_organisation', * 'table' => 'spip_organisations_liens', * 'table_condition' => 'role="parent"', * 'source_champ' => 'id_objet', * 'champ_type' => 'objet' * ); * ``` * * La fonction retourne un tableau des enfants, chacun de la forme * ['objet' => '...', 'id_objet' => X, 'table' => '...', 'champ' => '...'] * Si la table utilisée pour trouver l'eenfant est une table de liens (finissant par _liens), * le tableau contient en plus en entrée 'lien' qui contient les informations complètes du lien (rang, role...) * * @api * @param $objet * Type de l'objet dont on cherche les enfants * @param $id_objet * Identifiant de l'objet dont on cherche les enfants * @return array * Retourne un tableau de tableaux, avec comme clés les types des objets, et dans chacun un tableau des identifiants trouvés */ function objet_lister_enfants($objet, $id_objet) { $enfants = []; // Si on trouve des types d'enfants et leurs méthodes if ($enfants_methodes = objet_type_decrire_infos_enfants($objet)) { include_spip('base/abstract_sql'); $id_objet = intval($id_objet); // On parcourt tous les types d'enfants trouvés foreach ($enfants_methodes as $objet_enfant => $_methode_parent) { // On construit les conditions d'identification du parent $where = []; // -- L'identifiant du parent if (isset($_methode_parent['champ'])) { $where[] = $_methode_parent['champ'] . ' = ' . $id_objet; } // -- Si le parent est variable if (isset($_methode_parent['champ_type'])) { $where[] = $_methode_parent['champ_type'] . ' = ' . sql_quote($objet); } // On détermine la table, le champ id des enfants et on complète éventuellement les conditions if (!isset($_methode_parent['table'])) { // Les enfants sont stockés dans la même table que l'objet parent : $table_enfant = table_objet_sql($objet_enfant); $cle_objet_enfant = id_table_objet($objet_enfant); // S'il y a une condition supplémentaire if (isset($_methode_parent['condition'])) { $where[] = $_methode_parent['condition']; } } else { // Les enfants sont stockés dans une table différente de l'objet parent. $table_enfant = $_methode_parent['table']; $cle_objet_enfant = $_methode_parent['source_champ'] ?? id_table_objet($objet_enfant); // S'il y a une condition supplémentaire if (isset($_methode_parent['table_condition'])) { $where[] = $_methode_parent['table_condition']; } } // On lance la requête $is_table_lien = (strpos($table_enfant, '_liens') !== false and substr($table_enfant, -6) === '_liens'); if ($rows = sql_allfetsel($is_table_lien ? '*' : $cle_objet_enfant, $table_enfant, $where)) { $enfant = [ 'objet' => $objet_enfant, 'id_objet' => 0, 'table' => $table_enfant ]; if (isset($_methode_parent['champ'])) { $enfant['champ'] = $_methode_parent['champ']; } if (isset($_methode_parent['champ_type'])) { $enfant['champ_type'] = $_methode_parent['champ_type']; } foreach ($rows as $row) { $enfant['id_objet'] = intval($row[$cle_objet_enfant]); if ($is_table_lien) { $enfant['lien'] = $row; } $enfants[] = $enfant; } } } } // On passe par un pipeline avant de retourner $enfants = pipeline( 'objet_lister_enfants', [ 'args' => [ 'objet' => $objet, 'id_objet' => $id_objet, ], 'data' => $enfants, ] ); return $enfants; } /** * Fonction helper qui permet de récupérer une liste simplifiée des enfants, regroupés par objet * [ 'objet1' => [ids...], 'objet2' => [ids...] ] * * @param $objet * @param $id_objet * @return array */ function objet_lister_enfants_par_type($objet, $id_objet) { $enfants = objet_lister_enfants($objet, $id_objet); $enfants_par_type = []; foreach ($enfants as $enfant) { if (!isset($enfants_par_type[$enfant['objet']])) { $enfants_par_type[$enfant['objet']] = []; } $enfants_par_type[$enfant['objet']][] = $enfant['id_objet']; } return $enfants_par_type; } /** * Donne les informations de parenté directe d'un type d'objet si on en trouve * * @param $objet * Type de l'objet dont on cherche les informations de parent * @return array|false * Retourne un tableau de tableau contenant les informations de type et de champ pour trouver le parent ou false sinon */ function objet_type_decrire_infos_parents($objet) { static $parents = []; // Si on ne l'a pas encore cherché pour cet objet if (!isset($parents[$objet])) { $parents[$objet] = false; $table = table_objet_sql($objet); // Si on trouve bien la description de cet objet if ($infos = lister_tables_objets_sql($table)) { if (isset($infos['parent']) and is_array($infos['parent'])) { // S'il y a une description explicite de parent, c'est prioritaire // -- on traite les cas où il y a une ou plusieurs description mais on renvoie toujours un tableau // de description if (!isset($infos['parent'][0])) { $parents[$objet] = [$infos['parent']]; } else { $parents[$objet] = $infos['parent']; } } elseif (isset($infos['field']['id_rubrique'])) { // Sinon on cherche des cas courants connus magiquement, à commencer par id_rubrique $parents[$objet] = [['type' => 'rubrique', 'champ' => 'id_rubrique']]; } elseif (isset($infos['field']['id_parent'])) { // Sinon on cherche un champ id_parent, ce qui signifie que l'objet est parent de lui-même $parents[$objet] = [['type' => $objet, 'champ' => 'id_parent']]; } } } return $parents[$objet]; } /** * Donne les informations des enfants directs d'un type d'objet si on en trouve * * @param $objet * Type de l'objet dont on cherche les informations des enfants * @return array * Retourne un tableau de tableaux contenant chacun les informations d'un type d'enfant */ function objet_type_decrire_infos_enfants($objet) { static $enfants = []; // Si on a déjà fait la recherche pour ce type d'objet if (!isset($enfants[$objet])) { $enfants[$objet] = []; $tables = lister_tables_objets_sql(); // On parcourt toutes les tables d'objet, et on cherche si chacune peut être enfant foreach ($tables as $table => $infos) { $objet_enfant = objet_type($table); // On ne va pas refaire les tests des différents cas, on réutilise if ($parent_methodes = objet_type_decrire_infos_parents($objet_enfant)) { // On parcourt les différents cas possible, si certains peuvent concerner l'objet demandé foreach ($parent_methodes as $parent_methode) { // Si la méthode qu'on teste n'exclut pas le parent demandé if (!isset($parent_methode['exclus']) or !in_array($objet, $parent_methode['exclus'])) { // Si le type du parent est fixe et directement l'objet demandé if (isset($parent_methode['type']) and isset($parent_methode['champ']) and $parent_methode['type'] == $objet) { $enfants[$objet][$objet_enfant] = $parent_methode; } // Si le type est variable, alors l'objet demandé peut forcément être parent elseif (isset($parent_methode['champ_type']) and isset($parent_methode['champ'])) { $enfants[$objet][$objet_enfant] = $parent_methode; } } } } } } return $enfants[$objet]; }