uawdijnntqw1x1x1
IP : 216.73.217.34
Hostname : webm019.cluster129.gra.hosting.ovh.net
Kernel : Linux webm019.cluster129.gra.hosting.ovh.net 6.18.42-ovh-vps-grsec-zfs+ #1 SMP PREEMPT_DYNAMIC Wed Aug 5 15:59:48 CEST 2026 x86_64
Disable Function : _dyuweyrj4,_dyuweyrj4r,dl
OS : Linux
PATH:
/
home
/
checkyd
/
www
/
ecrire
/
..
/
plugins
/
..
/
7b5d0
/
inc.tar
/
/
date_gestion.php000064400000002465152525113000007723 0ustar00<?php /** * Plugin Spip-Bonux * Le plugin qui lave plus SPIP que SPIP * (c) 2008 Mathieu Marcillaud, Cedric Morin, Tetue * Licence GPL */ if (!defined('_ECRIRE_INC_VERSION')) { return; } /** * Recuperer les champs date_xx et heure_xx, verifier leur coherence et les reformater * * @param string $suffixe * @param bool $horaire * @param array $erreurs * @return int */ function verifier_corriger_date_saisie($suffixe, $horaire, &$erreurs) { include_spip('inc/filtres'); $date = _request("date_$suffixe").($horaire?' '.trim(_request("heure_$suffixe")).':00':''); $date = recup_date($date); if (!$date) { return ''; } $ret = null; if (!$ret=mktime(0, 0, 0, $date[1], $date[2], $date[0])) { $erreurs["date_$suffixe"] = _T('spip_bonux:erreur_date'); } elseif (!$ret=mktime($date[3], $date[4], $date[5], $date[1], $date[2], $date[0])) { $erreurs["date_$suffixe"] = _T('spip_bonux:erreur_heure'); } if ($ret) { if (trim(_request("date_$suffixe") !== ($d=date('d/m/Y', $ret)))) { $erreurs["date_$suffixe"] = _T('spip_bonux:erreur_date_corrigee'); set_request("date_$suffixe", $d); } if ($horaire and trim(_request("heure_$suffixe") !== ($h = date('H:i', $ret)))) { $erreurs["heure_$suffixe"] = _T('spip_bonux:erreur_heure_corrigee'); set_request("heure_$suffixe", $h); } } return $ret; } importer_csv.php000064400000010661152525113000007767 0ustar00<?php /** * Plugin Spip-Bonux * Le plugin qui lave plus SPIP que SPIP * (c) 2008 Mathieu Marcillaud, Cedric Morin, Tetue * Licence GPL * * Fonctions de lecture d'un fichier CSV pour transformation en array() * */ if (!defined('_ECRIRE_INC_VERSION')) { return; } include_spip('inc/charsets'); /** * Based on an example by ramdac at ramdac dot org * Returns a multi-dimensional array from a CSV file optionally using the * first row as a header to create the underlying data as associative arrays. * * @param string $file Filepath including filename * @param bool $head Use first row as header. * @param string $delim Specify a delimiter other than a comma. * @param int $len Line length to be passed to fgetcsv * @return array or false on failure to retrieve any rows. */ /** * Importer le charset d'une ligne * * Importe un texte de CSV dans un charset et le passe dans le charset du site (utf8 probablement) * Les CSV peuvent sous ms@@@ avoir le charset 'iso-8859-1', mais pasfois aussi 'windows-1252' :/ * * @param mixed $texte * @param bool|string $definir_charset_source * false : ne fait rien * string : utilisera pour les prochains imports le charset indiqué * true : remet le charset d'import par défaut de la fonction * @return array */ function importer_csv_importcharset($texte, $definir_charset_source = false) { // le plus frequent, en particulier avec les trucs de ms@@@ static $charset_source = 'iso-8859-1'; if ($definir_charset_source) { if ($definir_charset_source === true) { $charset_source = 'iso-8859-1'; } else { $charset_source = $definir_charset_source; } } // mais open-office sait faire mieux, donc mefiance ! if (is_utf8($texte)) { $charset = 'utf-8'; } else { $charset = $charset_source; } return importer_charset($texte, $charset); } /** * enlever les accents des cles presentes dans le head, * sinon ca pose des problemes ... * * @param string $key * @return string */ function importer_csv_nettoie_key($key) { return translitteration($key); } /** * Lit un fichier csv et retourne un tableau * si $head est true, la premiere ligne est utilisee en header * pour generer un tableau associatif * * @param string $file * @param array $options * bool $head * string $delim * string $enclos * int $len * string $charset_source : Permet de définir un charset source du CSV, si différent de utf-8 ou iso-8859-1 * @return false|array */ function inc_importer_csv_dist($file, $options = []) { // support ancienne syntaxe // inc_importer_csv_dist($file, $head = false, $delim = ',', $enclos = '"', $len = 10000, $charset_source = '') if (!is_array($options)) { $args = func_get_args(); $options = []; foreach ([1 => 'head', 2 => 'delim', 3 => 'enclos', 4 => 'len', 5 => 'charset_source'] as $k => $option) { if (!empty($args[$k])) { $options[$option] = $args[$k]; } } } $default_options = [ 'head' => false, 'delim' => ',', 'enclos' => '"', 'len' => 10000, 'charset_source' => '', ]; $options = array_merge($default_options, $options); $return = false; if (@file_exists($file) and $handle = fopen($file, 'r')) { if ($options['charset_source']) { importer_csv_importcharset('', $options['charset_source']); } if ($options['head']) { $header = fgetcsv($handle, $options['len'], $options['delim'], $options['enclos']); if ($header) { $header = array_map('importer_csv_importcharset', $header); $header = array_map('importer_csv_nettoie_key', $header); $header_type = array(); foreach ($header as $heading) { if (!isset($header_type[$heading])) { $header_type[$heading] = "scalar"; } else { $header_type[$heading] = "array"; } } } } while (($data = fgetcsv($handle, $options['len'], $options['delim'], $options['enclos'])) !== false) { $data = array_map('importer_csv_importcharset', $data); if ($options['head'] and isset($header)) { $row = array(); foreach ($header as $key => $heading) { if ($header_type[$heading] == "array") { if (!isset($row[$heading])) { $row[$heading] = array(); } if (isset($data[$key]) and strlen($data[$key])) { $row[$heading][] = $data[$key]; } } else { $row[$heading] = (isset($data[$key])) ? $data[$key] : ''; } } $return[] = $row; } else { $return[] = $data; } } if ($options['charset_source']) { importer_csv_importcharset('', true); } } return $return; } exporter_csv.php000064400000015101152525113000007770 0ustar00<?php /** * Plugin Spip-Bonux * Le plugin qui lave plus SPIP que SPIP * (c) 2008 Mathieu Marcillaud, Cedric Morin, Tetue * Licence GPL * * Fonctions d'export d'une requete sql ou d'un tableau * au format CSV * Merge du plugin csv_import et spip-surcharges * */ if (!defined('_ECRIRE_INC_VERSION')) { return; } include_spip('inc/charsets'); include_spip('inc/filtres'); include_spip('inc/texte'); /** * Exporter un champ pour un export CSV : pas de retour a la ligne, * et echapper les guillements par des doubles guillemets * * NB : on supprime les retours lignes qui sont pourtant supportes par le standard CSV et LibreOffice, mais pas par Excel * * @param string $champ * @return string */ function exporter_csv_champ($champ) { $champ = str_replace("\r\n", "\n", $champ); $champ = str_replace("\r", "\n", $champ); $champ = preg_replace(",\n\s+,ms", "\n", $champ); $champ = preg_replace(',\s[\s]+,ms', ' ', $champ); $champ = str_replace('"', '""', $champ); return '"' . $champ . '"'; } /** * Exporter un champ pour un export CSV sans retour a la ligne, * (qui sont supportees par le standard CSV et LibreOffice, mais pas par Excel) * * et echapper les guillements par des doubles guillemets * * @param string $champ * @return string */ function exporter_csv_champ_no_lf($champ) { $champ = preg_replace(',[\s]+,ms', ' ', $champ); $champ = str_replace('"', '""', $champ); return '"' . $champ . '"'; } /** * Exporter une ligne complete au format CSV, avec delimiteur fourni * * @uses exporter_csv_champ() * @uses exporter_csv_champ_no_lf() * * @param int $nb * @param array $ligne * @param string $delim * @param string|null $importer_charset * Si défini exporte dans le charset indiqué * @param callable $callback * @return string */ function exporter_csv_ligne_numerotee($nb, $ligne, $delim = ',', $importer_charset = null, $callback=null, $fonction_exporter_champ=null) { if ($callback) { $ligne = call_user_func($callback, $nb, $ligne, $importer_charset); } if (!$fonction_exporter_champ or !function_exists($fonction_exporter_champ)) { $fonction_exporter_champ = 'exporter_csv_champ'; } $output = join($delim, array_map($fonction_exporter_champ, $ligne)) . "\r\n"; if ($importer_charset) { $output = str_replace('’', '\'', $output); $output = unicode2charset(html2unicode(charset2unicode($output)), $importer_charset); } return $output; } /** * @deprecated * * @param $ligne * @param string $delim * @param null $importer_charset * @return string */ function exporter_csv_ligne($ligne, $delim = ',', $importer_charset = null){ return exporter_csv_ligne_numerotee(null, $ligne, $delim, $importer_charset); } /** * Exporte une ressource sous forme de fichier CSV * * La ressource peut etre un tableau ou une resource SQL issue d'une requete * L'extension est choisie en fonction du delimiteur : * - si on utilise ',' c'est un vrai csv avec extension csv * - si on utilise ';' ou tabulation c'est pour E*cel, et on exporte en iso-truc, avec une extension .xls * * @uses exporter_csv_ligne() * * @param string $titre * titre utilise pour nommer le fichier * @param array|resource $resource * @param array $options * string $delim : delimiteur * array $entetes : tableau d'en-tetes pour nommer les colonnes (genere la premiere ligne) * bool $envoyer : pour envoyer le fichier exporte (permet le telechargement) * string $charset : charset de l'export si different de celui du site * callable callback : fonction callback a appeler sur chaque ligne pour mettre en forme/completer les donnees * @return string */ function inc_exporter_csv_dist($titre, $resource, $options = []) { // support ancienne syntaxe // inc_exporter_csv_dist($titre, $resource, $delim = ', ', $entetes = null, $envoyer = true) if (!is_array($options)) { $args = func_get_args(); $options = []; foreach (array(2 => 'delim', 3 => 'entetes', 4 => 'envoyer') as $k => $option) { if (!empty($args[$k])) { $options[$option] = $args[$k]; } } } $default_options = array( 'delim' => ', ', 'entetes' => null, 'envoyer' => true, 'charset' => null, 'callback' => null, ); $options = array_merge($default_options, $options); $filename = preg_replace(',[^-_\w]+,', '_', translitteration(textebrut(typo($titre)))); if ($options['delim'] == 'TAB') { $options['delim'] = "\t"; } if (!in_array($options['delim'], array(',', ';', "\t"))) { $options['delim'] = ','; } $charset = $GLOBALS['meta']['charset']; $importer_charset = null; $fonction_exporter_champ = null; if ($options['delim'] == ',') { $extension = 'csv'; } else { $extension = 'xls'; # Excel n'accepte pas l'utf-8 ni les entites html... on transcode tout ce qu'on peut $charset = 'iso-8859-1'; # Excel n'accepte pas les retours ligne dans les CSV $fonction_exporter_champ = 'exporter_csv_champ_no_lf'; } // mais si une option charset est explicite, elle a la priorite if (!empty($options['charset'])) { $charset = $options['charset']; } $importer_charset = (($charset === $GLOBALS['meta']['charset']) ? null : $charset); $filename = "$filename.$extension"; $output = ''; $nb = 0; if (!empty($options['entetes']) and is_array($options['entetes'])) { $output = exporter_csv_ligne_numerotee($nb, $options['entetes'], $options['delim'], $importer_charset, $options['callback'], $fonction_exporter_champ); } // les donnees commencent toujours a la ligne 1, qu'il y ait ou non des entetes $nb++; if ($options['envoyer']) { $disposition = ($options['envoyer'] === 'attachment' ? 'attachment' : 'inline'); header("Content-Type: text/comma-separated-values; charset=$charset"); header("Content-Disposition: $disposition; filename=$filename"); // Vider tous les tampons $level = @ob_get_level(); while ($level--) { @ob_end_flush(); } } // si envoyer=='attachment' on passe par un fichier temporaire // sinon on ecrit directement sur stdout if ($options['envoyer'] and $options['envoyer'] !== 'attachment') { $fichier = "php://output"; } else { $fichier = sous_repertoire(_DIR_CACHE, 'export') . $filename; } $fp = fopen($fichier, 'w'); $length = fwrite($fp, $output); while ($row = is_array($resource) ? array_shift($resource) : sql_fetch($resource)) { $output = exporter_csv_ligne_numerotee($nb, $row, $options['delim'], $importer_charset, $options['callback'], $fonction_exporter_champ); $length += fwrite($fp, $output); $nb++; } fclose($fp); if ($options['envoyer']) { if ($options['envoyer'] === 'attachment') { header("Content-Length: $length"); readfile($fichier); } // si on a envoye inline, c'est deja tout bon exit; } return $fichier; } exporter_xls.php000064400000007545152525113000010020 0ustar00<?php /** * Plugin Spip-Bonux * Le plugin qui lave plus SPIP que SPIP * (c) 2008 Mathieu Marcillaud, Cedric Morin, Tetue * Licence GPL * * Fonctions d'export d'une requete sql ou d'un tableau * au format CSV * Merge du plugin csv_import et spip-surcharges * */ use Export\ExportDataExcel; if (!defined('_ECRIRE_INC_VERSION')) { return; } include_spip('inc/charsets'); include_spip('inc/filtres'); include_spip('inc/texte'); /** * Preparer une ligne avant export XLS : charset si besoin + callback * * @param int $nb * @param array $ligne * @param string|null $importer_charset * Si défini exporte dans le charset indiqué * @param callable $callback * @return string */ function exporter_xls_preparer_ligne_numerotee($nb, $ligne, $importer_charset = null, $callback=null) { if ($callback) { $ligne = call_user_func($callback, $nb, $ligne, $importer_charset); } foreach ($ligne as $k => $v) { $ligne[$k] = str_replace('’', '\'', $ligne[$k]); $ligne[$k] = rtrim($ligne[$k]); if ($importer_charset and !is_numeric($v)) { $ligne[$k] = unicode2charset(html2unicode(charset2unicode($ligne[$k])), $importer_charset); } } return $ligne; } /** * Exporte une ressource sous forme de fichier CSV * * La ressource peut etre un tableau ou une resource SQL issue d'une requete * L'extension est choisie en fonction du delimiteur : * - si on utilise ',' c'est un vrai csv avec extension csv * - si on utilise ';' ou tabulation c'est pour E*cel, et on exporte en iso-truc, avec une extension .xls * * @uses exporter_xls_ligne() * * @param string $titre * titre utilise pour nommer le fichier * @param array|resource $resource * @param array $options * array $entetes : tableau d'en-tetes pour nommer les colonnes (genere la premiere ligne) * bool $envoyer : pour envoyer le fichier exporte (permet le telechargement) * string $charset : charset de l'export si different de celui du site * callable callback : fonction callback a appeler sur chaque ligne pour mettre en forme/completer les donnees * @return string */ function inc_exporter_xls_dist($titre, $resource, $options = []) { include_spip('lib/php-export-data/src/ExportData'); include_spip('lib/php-export-data/src/ExportDataExcel'); $default_options = [ 'entetes' => null, 'envoyer' => true, 'charset' => null, 'callback' => null, ]; $options = array_merge($default_options, $options); $filename = preg_replace(',[^-_\w]+,', '_', translitteration(textebrut(typo($titre)))); $extension = 'xls'; $charset = $GLOBALS['meta']['charset']; // mais si une option charset est explicite, elle a la priorite if (!empty($options['charset'])) { $charset = $options['charset']; } $importer_charset = (($charset === $GLOBALS['meta']['charset']) ? null : $charset); $filename = "$filename.$extension"; $fichier = sous_repertoire(_DIR_CACHE, 'export') . $filename; if ($options['envoyer']) { // Vider tous les tampons $level = @ob_get_level(); while ($level--) { @ob_end_flush(); } } $exporter = new ExportDataExcel($options['envoyer'] ? 'browser' : 'file', $fichier); $exporter->encoding = $charset; $exporter->initialize(); // start export $nb = 0; if (!empty($options['entetes']) and is_array($options['entetes'])) { $ligne = exporter_xls_preparer_ligne_numerotee($nb, $options['entetes'], $importer_charset, $options['callback']); $exporter->addRow($ligne); } // les donnees commencent toujours a la ligne 1, qu'il y ait ou non des entetes $nb++; while ($row = is_array($resource) ? array_shift($resource) : sql_fetch($resource)) { $ligne = exporter_xls_preparer_ligne_numerotee($nb, $row, $importer_charset, $options['callback']); $exporter->addRow($ligne); $nb++; } $exporter->finalize(); // writes the footer, flushes remaining data to browser. if ($options['envoyer']) { // si on a envoye inline, c'est deja tout bon exit; } return $fichier; } getdocument.php000064400000003220152525410300007565 0ustar00<?php /***************************************************************************\ * SPIP, Systeme de publication pour l'internet * * * * Copyright (c) 2001-2020 * * 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. * \***************************************************************************/ /** * Gère un cas d'upload trop gros * * Fichier obsolète, à supprimer. * Mais fonction utilisée encore dans medias_detecter_fond_par_defaut() * * @package SPIP\Medias\Upload **/ # # Fichier obsolete, a supprimer # if (!defined('_ECRIRE_INC_VERSION')) { return; } // inclure les fonctions bases du core include_once _DIR_RESTREINT . 'inc/documents.php'; include_spip('inc/minipres'); /** * Traite l'erreur d'un upload trop gros * * L'erreur est appelée depuis public.php et medias_detecter_fond_par_defaut * et affiche un minipres avec la taille limite de documents possibles * * @see minipres() **/ function erreur_upload_trop_gros() { include_spip('inc/filtres'); $msg = '<p>' . taille_en_octets($_SERVER['CONTENT_LENGTH']) . '<br />' . _T( 'medias:upload_limit', array('max' => ini_get('upload_max_filesize')) ) . '</p>'; echo minipres(_T('pass_erreur'), "<div class='upload_answer upload_error'>" . $msg . '</div>'); exit; } choisir_mode_document.php000064400000004135152525410300011617 0ustar00<?php /***************************************************************************\ * SPIP, Systeme de publication pour l'internet * * * * Copyright (c) 2001-2020 * * 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 des modes de documents * * @package SPIP\Medias\Modes */ if (!defined('_ECRIRE_INC_VERSION')) { return; } /** * Choisir le mode du document : image/document * * @param array $infos * Informations sur le document (en base + prises sur le fichier) * @param bool $type_inclus_image * Type d'inclusion demandée : true si image * @param string $objet * Objet sur lequel est placé le document * @return string * Mode du document */ function inc_choisir_mode_document($infos, $type_inclus_image, $objet) { // si ce n'est pas une image, c'est forcement un document if (!$infos['type_image'] or !$type_inclus_image) { return 'document'; } // si on a pas le droit d'ajouter de document a l'objet, c'est donc un mode image if ($objet and isset($GLOBALS['meta']["documents_$objet"]) and ($GLOBALS['meta']["documents_$objet"] == 'non')) { return 'image'; } // _INTERFACE_DOCUMENTS // en fonction de la taille de l'image // par defaut l'affectation en fonction de la largeur de l'image // est desactivee car pas comprehensible par le novice // il suffit de faire dans mes_options // define('_LARGEUR_MODE_IMAGE', 450); // pour beneficier de cette detection auto @define('_LARGEUR_MODE_IMAGE', 0); if (!_LARGEUR_MODE_IMAGE) { return 'image'; } if ($infos['largeur'] > 0 and $infos['largeur'] < _LARGEUR_MODE_IMAGE ) { return 'image'; } else { return 'document'; } } documenter_objet.php000064400000003661152525410300010610 0ustar00<?php /***************************************************************************\ * SPIP, Systeme de publication pour l'internet * * * * Copyright (c) 2001-2020 * * 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. * \***************************************************************************/ /** * Calcul du portfolio / liste de documents de l'espace privé * * @package SPIP\Medias\Fonctions **/ if (!defined('_ECRIRE_INC_VERSION')) { return; } /** * Retourne le code HTML du portfolio, liste des documents et formulaire d'ajout de documents * * @note * Marque les documents vus dans les textes de l'objet avant le calcul ! * * @uses inc_marquer_doublons_doc_dist() * * @param int $id Identifiant de l'objet * @param string $type Type d'objet * @return string code HTML **/ function inc_documenter_objet_dist($id, $type) { $serveur = ''; // avant de documenter un objet, on verifie que ses documents vus sont bien lies ! $spip_table_objet = table_objet_sql($type); $table_objet = table_objet($type); $id_table_objet = id_table_objet($type, $serveur); $champs = sql_fetsel('*', $spip_table_objet, addslashes($id_table_objet) . '=' . intval($id)); $marquer_doublons_doc = charger_fonction('marquer_doublons_doc', 'inc'); $marquer_doublons_doc($champs, $id, $type, $id_table_objet, $table_objet, $spip_table_objet, '', $serveur); $contexte = array('objet' => $type, 'id_objet' => $id, 'espace_prive' => test_espace_prive()); return recuperer_fond('prive/objets/contenu/portfolio_document', array_merge($_GET, $contexte)); } determiner_statut_document.php000064400000005305152525410300012715 0ustar00<?php /***************************************************************************\ * SPIP, Systeme de publication pour l'internet * * * * Copyright (c) 2001-2020 * * 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 des modes de documents * * @package SPIP\Medias\Modes */ if (!defined('_ECRIRE_INC_VERSION')) { return; } /** * Determiner le statut automatique d'un document * @param int $id_document * @param string $statut_ancien * @param string $date_publication_ancienne * @return array|false */ function inc_determiner_statut_document($id_document, $statut_ancien, $date_publication_ancienne) { $statut = 'prepa'; $trouver_table = charger_fonction('trouver_table', 'base'); $res = sql_select( 'id_objet,objet', 'spip_documents_liens', "objet!='document' AND id_document=" . intval($id_document) ); // On aura 19 jours 3h14 et 7 secondes pour corriger en 2038 (limitation de la représentation POSIX du temps sur les 32 bits) $date_publication = strtotime('2038-01-01 00:00:00'); include_spip('base/objets'); while ($row = sql_fetch($res)) { if ( // cas particulier des rubriques qui sont publiees des qu'elles contiennent un document ! $row['objet'] == 'rubrique' // ou si objet publie selon sa declaration or objet_test_si_publie($row['objet'], $row['id_objet']) ) { $statut = 'publie'; $date_publication = 0; continue; } // si pas publie, et article, il faut checker la date de post-publi eventuelle elseif ($row['objet'] == 'article' and $row2 = sql_fetsel( 'date', 'spip_articles', 'id_article=' . intval($row['id_objet']) . " AND statut='publie'" ) ) { $statut = 'publie'; $date_publication = min($date_publication, strtotime($row2['date'])); } } $date_publication = date('Y-m-d H:i:s', $date_publication); if ($statut == 'publie' and $statut_ancien == 'publie' and $date_publication == $date_publication_ancienne) { return false; } if ($statut != 'publie' and $statut_ancien != 'publie' and $statut_ancien != '0') { return false; } $champs = []; if ($statut !== $statut_ancien) { $champs['statut'] = $statut; } if ($date_publication !== $date_publication_ancienne) { $champs['date_publication'] = $date_publication; } return $champs; } renseigner_document.php000064400000012211152525410300011306 0ustar00<?php /***************************************************************************\ * SPIP, Systeme de publication pour l'internet * * * * Copyright (c) 2001-2020 * * 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. * \***************************************************************************/ /** * Fonctions pour compléter les informations connues d'un document * * @package SPIP\Medias\Renseigner **/ if (!defined('_ECRIRE_INC_VERSION')) { return; } /** * Récuperer les infos distantes d'une URL, * et renseigner pour une insertion en base * * Utilise une variable static car appellée plusieurs fois au cours du même hit * (vérification puis traitement) * * Un plugin peut avec le pipeline renseigner_document_distant renseigner * les informations fichier et mode avant l'analyse et traitement par ce plugin, * qui dans ce cas ne les fera pas. Exemple : OEmbed * * @pipeline_appel renseigner_document_distant * * @param string $source * URL du document * @return array|string * Informations sur le fichier distant, sinon message d'erreur. * Sans erreur, retourne un tableau : * * - source : URL du fichier * - distant : Est-ce un fichier distant ? * - mode : Mode d'inclusion * - fichier : Chemin local du fichier s'il a été recopié */ function renseigner_source_distante($source) { static $infos = array(); if (isset($infos[$source])) { return $infos[$source]; } include_spip('inc/distant'); // on passe la source dans le pipeline, le premier plugin // qui est capable de renseigner complete // fichier et mode + tous les autres champs a son gout // ex : oembed $a = pipeline('renseigner_document_distant', array('source' => $source)); // si la source est encore la, en revenir a la // methode traditionnelle : chargement de l'url puis analyse if (!isset($a['fichier']) or !isset($a['mode'])) { if (!$a = recuperer_infos_distantes($a['source'])) { return _T('medias:erreur_chemin_distant', array('nom' => $source)); } # NB: dans les bonnes conditions (fichier autorise et pas trop gros) # $a['fichier'] est une copie locale du fichier unset($a['body']); $a['distant'] = 'oui'; $a['mode'] = 'document'; $a['fichier'] = set_spip_doc($source); } // stocker pour la seconde demande return $infos[$source] = $a; } /** * Renseigner les informations de taille et dimension d'un document * * Récupère les informations de taille (largeur / hauteur / type_image / taille) d'un document * Utilise pour cela les fonctions du répertoire metadatas/* * * Ces fonctions de récupérations peuvent retourner d'autres champs si ces champs sont définis * comme editable dans la déclaration de la table spip_documents * * @todo * Renommer cette fonction sans "_image" * * @param string $fichier * Le fichier à examiner * @param string $ext * L'extension du fichier à examiner * @param bool $distant * Indique que le fichier peut etre distant, on essaiera alors d'en recuperer un bout pour en lire les meta infos * @return array|string $infos * * - Si c'est une chaîne, c'est une erreur * - Si c'est un tableau, l'ensemble des informations récupérées du fichier */ function renseigner_taille_dimension_image($fichier, $ext, $distant = false) { $infos = array( 'largeur' => 0, 'hauteur' => 0, 'type_image' => '', 'taille' => 0 ); // Quelques infos sur le fichier if ( !$fichier or !@file_exists($fichier) or !$infos['taille'] = @intval(filesize($fichier)) ) { if ($distant) { // on ne saura pas la taille unset($infos['taille']); // recuperer un debut de fichier 512ko semblent suffire $tmp = _DIR_TMP . md5($fichier); $res = recuperer_url($fichier, array('file' => $tmp, 'taille_max' => 512 * 1024)); if (!$res) { spip_log("Echec copie du fichier $fichier", 'medias'); return _T('medias:erreur_copie_fichier', array('nom' => $fichier)); } $fichier = $tmp; } else { spip_log("Echec copie du fichier $fichier", 'medias'); return _T('medias:erreur_copie_fichier', array('nom' => $fichier)); } } // chercher une fonction de description $meta = array(); if ($metadata = charger_fonction($ext, 'metadata', true)) { $meta = $metadata($fichier); } else { $media = sql_getfetsel('media_defaut', 'spip_types_documents', 'extension=' . sql_quote($ext)); if ($metadata = charger_fonction($media, 'metadata', true)) { $meta = $metadata($fichier); } } $meta = pipeline( 'renseigner_document', array('args' => array('extension' => $ext, 'fichier' => $fichier), 'data' => $meta) ); include_spip('inc/filtres'); # pour objet_info() $editables = objet_info('document', 'champs_editables'); foreach ($meta as $m => $v) { if (isset($infos[$m]) or in_array($m, $editables)) { $infos[$m] = $v; } } return $infos; } verifier_document_mode_vignette.php000064400000002323152525410300013674 0ustar00<?php /***************************************************************************\ * SPIP, Systeme de publication pour l'internet * * * * Copyright (c) 2001-2020 * * 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 inc_verifier_document_mode_vignette_dist($infos) { // Si on veut uploader une vignette, il faut qu'elle ait ete bien lue if ($infos['inclus'] != 'image') { return _T('medias:erreur_format_fichier_image', array('nom' => $infos['fichier'])); } #SVG if (isset($infos['largeur']) and isset($infos['hauteur'])) { if (!($infos['largeur'] or $infos['hauteur'])) { return _T('medias:erreur_upload_vignette', array('nom' => $infos['fichier'])); } } return true; } documents.php000064400000016746152525410300007271 0ustar00<?php /***************************************************************************\ * SPIP, Systeme de publication pour l'internet * * * * Copyright (c) 2001-2020 * * 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; } // inclure les fonctions bases du core include_once _DIR_RESTREINT . 'inc/documents.php'; include_spip('inc/actions'); // *action_auteur et determine_upload // Constante indiquant le charset probable des documents non utf-8 joints if (!defined('CHARSET_JOINT')) { define('CHARSET_JOINT', 'iso-8859-1'); } // Filtre pour #FICHIER permettant d'incruster le contenu d'un document // Si 2e arg fourni, conversion dans le charset du site si possible // https://code.spip.net/@contenu_document function contenu_document($arg, $charset = '') { include_spip('inc/distant'); if (is_numeric($arg)) { $r = sql_fetsel('fichier,distant', 'spip_documents', 'id_document=' . intval($arg)); if (!$r) { return ''; } $f = $r['fichier']; $f = ($r['distant'] == 'oui') ? _DIR_RACINE . copie_locale($f) : get_spip_doc($f); } else { if (!@file_exists($f = $arg)) { if (!$f = copie_locale($f)) { return ''; } $f = _DIR_RACINE . $f; } } $r = spip_file_get_contents($f); if ($charset) { include_spip('inc/charsets'); if ($charset !== 'auto') { $r = importer_charset($r, $charset); } elseif ($GLOBALS['meta']['charset'] == 'utf-8' and !is_utf8($r)) { $r = importer_charset($r, CHARSET_JOINT); } } return $r; } // https://code.spip.net/@generer_url_document_dist function generer_url_document_dist($id_document, $args = '', $ancre = '') { include_spip('inc/autoriser'); if (!autoriser('voir', 'document', $id_document)) { return ''; } $r = sql_fetsel('fichier,distant', 'spip_documents', 'id_document=' . intval($id_document)); if (!$r) { return ''; } $f = $r['fichier']; if ($r['distant'] == 'oui') { return $f; } // Si droit de voir tous les docs, pas seulement celui-ci // il est inutilement couteux de rajouter une protection $r = (autoriser('voir', 'document')); if (($r and $r !== 'htaccess')) { return get_spip_doc($f); } include_spip('inc/securiser_action'); // cette action doit etre publique ! return generer_url_action( 'acceder_document', $args . ($args ? '&' : '') . 'arg=' . $id_document . ($ancre ? "&ancre=$ancre" : '') . '&cle=' . calculer_cle_action($id_document . ',' . $f) . '&file=' . rawurlencode($f), true, true ); } // // Affiche le document avec sa vignette par defaut // // Attention : en mode 'doc', si c'est un fichier graphique on prefere // afficher une vue reduite, quand c'est possible (presque toujours, donc) // En mode 'image', l'image conserve sa taille // // A noter : dans le portfolio prive on pousse le vice jusqu'a reduire la taille // de la vignette -> c'est a ca que sert la variable $portfolio function vignette_automatique($img, $doc, $lien, $x = 0, $y = 0, $align = '', $class = null, $connect = null) { include_spip('inc/distant'); include_spip('inc/texte'); include_spip('inc/filtres_images_mini'); if (is_null($class)) { $class = 'spip_logo spip_logos'; } $e = $doc['extension']; if (!$img) { if ($img = image_du_document($doc, $connect)) { if (!$x and !$y) { // eviter une double reduction $img = image_reduire($img); } } else { $f = charger_fonction('vignette', 'inc'); $img = $f($e, false); $size = @getimagesize($img); $img = "<img src='$img' " . $size[3] . ' />'; } } else { $size = @getimagesize($img); $img = "<img src='$img' " . $size[3] . ' />'; } // on appelle image_reduire independamment de la presence ou non // des librairies graphiques // la fonction sait se debrouiller et faire de son mieux dans tous les cas if ($x or $y) { $img = image_reduire($img, $x, $y); } $img = inserer_attribut($img, 'alt', ''); $img = inserer_attribut($img, 'class', $class); if ($align) { $img = inserer_attribut($img, 'align', $align); } if (!$lien) { return $img; } $titre = supprimer_tags(typo($doc['titre'])); $titre = ' - ' . taille_en_octets($doc['taille']) . ($titre ? " - $titre" : ''); $type = sql_fetsel('titre, mime_type', 'spip_types_documents', 'extension = ' . sql_quote($e)); $mime = $type['mime_type']; $titre = attribut_html(couper($type['titre'] . $titre, 80)); return "<a href='$lien' type='$mime' title='$titre'>$img</a>"; } /** * Trouve une image caractéristique d'un document. * * Si celui-ci est une image et que les outils graphiques sont dispos, * retourner le document (en exploitant sa copie locale s'il est distant). * * Si on a un connecteur externe, on utilise l’URL externe. * * Autrement retourner la vignette fournie par SPIP pour ce type MIME * * @param array $document * @param null|string $connect * @return string Chemin de l’image */ function image_du_document($document, $connect = null) { if ($e = $document['extension'] and isset($GLOBALS['meta']['formats_graphiques']) and (strpos($GLOBALS['meta']['formats_graphiques'], $e) !== false) and (!test_espace_prive() or $GLOBALS['meta']['creer_preview'] == 'oui') and $document['fichier'] ) { include_spip('inc/quete'); if ($document['distant'] == 'oui') { $image = _DIR_RACINE . copie_locale($document['fichier']); } elseif ($image = document_spip_externe($document['fichier'], $connect)) { return $image; } else { $image = get_spip_doc($document['fichier']); } if (@file_exists($image)) { return $image; } } return ''; } /** * Afficher un document dans la colonne de gauche * * @deprecated * Utiliser l'inclusion prévue ou une véritable * déclaration d'objet éditorial (la colonne document * est alors affichée automatiquement sur la page d'édition de l'objet) * * @param int $id * Identifiant de l'objet, ou id_auteur négatif pour un nouvel objet * @param string $type * Type d'objet * @param null $script * ?? * @return string * Code HTML permettant de gérer des documents */ function afficher_documents_colonne($id, $type = 'article', $script = null) { return recuperer_fond('prive/objets/editer/colonne_document', array('objet' => $type, 'id_objet' => $id)); } /** * Affiche le code d'un raccourcis de document, tel que <doc123|left> * * Affiche un code de raccourcis de document, et l'insère * dans le textarea principal de l'objet (champ 'texte') sur un double-clic * * @param string $doc * Type de raccourcis : doc,img,emb... * @param int $id * Identifiant du document * @param string $align * Alignement du document : left,center,right * * @return string * Texte du raccourcis **/ function affiche_raccourci_doc($doc, $id, $align) { static $num = 0; $pipe = $onclick = ''; if ($align) { $pipe = "|$align"; $onclick = "\nondblclick=\"barre_inserer('\\x3C$doc$id$pipe>', $('textarea[name=texte]')[0]);\"\ntitle=\"" . str_replace( '&', '&', entites_html(_T('medias:double_clic_inserer_doc')) ) . '"'; } else { $align = 'center'; } return "\n<div style='text-align: $align'$onclick><$doc$id$pipe></div>\n"; } verifier_document_mode_image.php000064400000002315152525410300013132 0ustar00<?php /***************************************************************************\ * SPIP, Systeme de publication pour l'internet * * * * Copyright (c) 2001-2020 * * 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 inc_verifier_document_mode_image_dist($infos) { // Si on veut uploader une image, il faut qu'elle ait ete bien lue if ($infos['inclus'] != 'image') { return _T('medias:erreur_format_fichier_image', array('nom' => $infos['fichier'])); } #SVG if (isset($infos['largeur']) and isset($infos['hauteur'])) { if (!($infos['largeur'] or $infos['hauteur'])) { return _T('medias:erreur_upload_vignette', array('nom' => $infos['fichier'])); } } return true; } joindre_document.php000064400000026016152525410300010607 0ustar00<?php /***************************************************************************\ * SPIP, Systeme de publication pour l'internet * * * * Copyright (c) 2001-2020 * * 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; } /** * Recuperer le nom du fichier selon le mode d'upload choisi * et mettre cela au format $_FILES * * Renvoie une liste de fichier ou un message en cas d'erreur * * @return string/array */ function joindre_trouver_fichier_envoye() { static $files = array(); // on est appele deux fois dans un hit, resservir ce qu'on a trouve a la verif // lorsqu'on est appelle au traitement if (count($files)) { return $files; } if (_request('joindre_upload')) { $post = isset($_FILES) ? $_FILES : $GLOBALS['HTTP_POST_FILES']; $files = array(); if (is_array($post)) { include_spip('action/ajouter_documents'); foreach ($post as $file) { if (is_array($file['name'])) { while (count($file['name'])) { $test = array( 'error' => array_shift($file['error']), 'name' => array_shift($file['name']), 'tmp_name' => array_shift($file['tmp_name']), 'type' => array_shift($file['type']), ); if (!($test['error'] == 4)) { if (is_string($err = joindre_upload_error($test['error']))) { return $err; } // un erreur upload if (!is_array(verifier_upload_autorise($test['name']))) { return _T('medias:erreur_upload_type_interdit', array('nom' => $test['name'])); } $files[] = $test; } } } else { //UPLOAD_ERR_NO_FILE if (!($file['error'] == 4)) { if (is_string($err = joindre_upload_error($file['error']))) { return $err; } // un erreur upload if (!is_array(verifier_upload_autorise($file['name']))) { return _T('medias:erreur_upload_type_interdit', array('nom' => $file['name'])); } $files[] = $file; } } } if (!count($files)) { return _T('medias:erreur_indiquez_un_fichier'); } } return $files; } elseif (_request('joindre_distant')) { $path = _request('url'); if (!strlen($path) or $path == 'http://') { return _T('medias:erreur_indiquez_un_fichier'); } include_spip('inc/distant'); if (!valider_url_distante($path)) { return _T('medias:erreur_upload_type_interdit', array('nom' => $path)); } include_spip('action/ajouter_documents'); $infos = renseigner_source_distante($path); if (!is_array($infos)) { return $infos; } // message d'erreur else { return array( array( 'name' => basename($path), 'tmp_name' => $path, 'distant' => true, ) ); } } elseif (_request('joindre_ftp')) { $path = _request('cheminftp'); if (!$path || strstr($path, '..')) { return _T('medias:erreur_indiquez_un_fichier'); } include_spip('inc/documents'); include_spip('inc/actions'); $upload = determine_upload(); if ($path != '/' and $path != './') { $upload .= $path; } if (!is_dir($upload)) { // seul un fichier est demande return array( array( 'name' => basename($upload), 'tmp_name' => $upload ) ); } else { // on upload tout un repertoire $files = array(); foreach (preg_files($upload) as $fichier) { $files[] = array( 'name' => basename($fichier), 'tmp_name' => $fichier ); } return $files; } } elseif (_request('joindre_zip') and $token_zip = _request('chemin_zip')) { $zip_to_clean = (isset($GLOBALS['visiteur_session']['zip_to_clean']) ? unserialize($GLOBALS['visiteur_session']['zip_to_clean']) : array()); if (!$zip_to_clean or !isset($zip_to_clean[$token_zip]) or !$path = $zip_to_clean[$token_zip]) { return _T('avis_operation_impossible'); } include_spip('inc/documents'); //pour creer_repertoire_documents define('_TMP_ZIP', $path); define('_TMP_DIR', creer_repertoire_documents(md5($path . $GLOBALS['visiteur_session']['id_auteur']))); if (_TMP_DIR == _DIR_IMG) { return _T('avis_operation_impossible'); } $files = array(); if (_request('options_upload_zip') == 'deballe') { $files = joindre_deballer_lister_zip($path, _TMP_DIR); } // si le zip doit aussi etre conserve, l'ajouter if (_request('options_upload_zip') == 'upload' or _request('options_deballe_zip_conserver')) { $files[] = array( 'name' => basename($path), 'tmp_name' => $path, ); } return $files; } return array(); } // Erreurs d'upload // renvoie false si pas d'erreur // et true si erreur = pas de fichier // pour les autres erreurs renvoie le message d'erreur function joindre_upload_error($error) { if (!$error) { return false; } spip_log("Erreur upload $error -- cf. http://php.net/manual/fr/features.file-upload.errors.php"); switch ($error) { case 4: /* UPLOAD_ERR_NO_FILE */ return true; # on peut affiner les differents messages d'erreur case 1: /* UPLOAD_ERR_INI_SIZE */ $msg = _T( 'medias:upload_limit', array('max' => ini_get('upload_max_filesize')) ); break; case 2: /* UPLOAD_ERR_FORM_SIZE */ $msg = _T( 'medias:upload_limit', array('max' => ini_get('upload_max_filesize')) ); break; case 3: /* UPLOAD_ERR_PARTIAL */ $msg = _T( 'medias:upload_limit', array('max' => ini_get('upload_max_filesize')) ); break; case 6: /* UPLOAD_ERR_NO_TMP_DIR */ $msg = _T('medias:erreur_dossier_tmp_manquant'); break; case 7: /* UPLOAD_ERR_CANT_WRITE */ $msg = _T('medias:erreur_ecriture_fichier'); break; default: /* autre */ if (!$msg) { $msg = _T('pass_erreur') . ' ' . $error . '<br />' . propre('[->http://php.net/manual/fr/features.file-upload.errors.php]'); } break; } spip_log("erreur upload $error"); return $msg; } /** * Verifier si le fichier poste est un zip * Si on sait le deballer, proposer les options necessaires * * @param array $files * @return string */ function joindre_verifier_zip($files) { if (function_exists('gzopen') and (count($files) == 1) and !isset($files[0]['distant']) and (preg_match('/\.zip$/i', $files[0]['name']) or (isset($files[0]['type']) and $files[0]['type'] == 'application/zip')) ) { // on pose le fichier dans le repertoire zip // (nota : copier_document n'ecrase pas un fichier avec lui-meme // ca autorise a boucler) include_spip('inc/getdocument'); $desc = $files[0]; $zip = copier_document( 'zip', $desc['name'], $desc['tmp_name'] ); // Est-ce qu'on sait le lire ? include_spip('inc/pclzip'); if ($zip and $archive = new PclZip($zip) and $contenu = joindre_decrire_contenu_zip($archive) and $tmp = sous_repertoire(_DIR_TMP, 'zip') and rename($zip, $tmp = $tmp . basename($zip)) ) { $zip_to_clean = (isset($GLOBALS['visiteur_session']['zip_to_clean']) ? unserialize($GLOBALS['visiteur_session']['zip_to_clean']) : array()); $zip_to_clean[md5($tmp)] = $tmp; session_set('zip_to_clean', serialize($zip_to_clean)); $contenu[] = $tmp; return $contenu; } } // ce n'est pas un zip sur lequel il faut demander plus de precisions return false; } /** * Verifier et decrire les fichiers de l'archive, en deux listes : * - une liste des noms de fichiers ajoutables * - une liste des erreurs (fichiers refuses) * * @param object $zip * @return array */ function joindre_decrire_contenu_zip($zip) { include_spip('action/ajouter_documents'); // si pas possible de decompacter: installer comme fichier zip joint if (!$list = $zip->listContent()) { return false; } // Verifier si le contenu peut etre uploade (verif extension) $fichiers = array(); $erreurs = array(); foreach ($list as $file) { if (accepte_fichier_upload($f = $file['stored_filename'])) { $fichiers[$f] = $file; } else // pas de message pour les dossiers et fichiers caches { if (substr($f, -1) !== '/' and substr(basename($f), 0, 1) !== '.') { $erreurs[] = _T('medias:erreur_upload_type_interdit', array('nom' => $f)); } } } // si aucun fichier uploadable : installer comme fichier zip joint if (!count($fichiers)) { return false; } ksort($fichiers); return array($fichiers, $erreurs); } // https://code.spip.net/@joindre_deballes function joindre_deballer_lister_zip($path, $tmp_dir) { include_spip('inc/pclzip'); $archive = new PclZip($path); $archive->extract( PCLZIP_OPT_PATH, $tmp_dir, PCLZIP_CB_PRE_EXTRACT, 'callback_deballe_fichier' ); if ($contenu = joindre_decrire_contenu_zip($archive)) { $files = array(); $fichiers = reset($contenu); foreach ($fichiers as $fichier) { $f = basename($fichier['filename']); $files[] = array( 'tmp_name' => $tmp_dir . $f, 'name' => $f, 'titrer' => _request('options_deballe_zip_titrer'), 'mode' => _request('options_deballe_zip_mode_document') ? 'document' : null ); } return $files; } return _T('avis_operation_impossible'); } if (!function_exists('fixer_extension_document')) { /** * Cherche dans la base le type-mime du tableau representant le document * et corrige le nom du fichier ; retourne array(extension, nom corrige) * s'il ne trouve pas, retourne '' et le nom inchange * * @param unknown_type $doc * @return unknown */ // https://code.spip.net/@fixer_extension_document function fixer_extension_document($doc) { $extension = ''; $name = $doc['name']; if (preg_match(',\.([^.]+)$,', $name, $r) and $t = sql_fetsel( 'extension', 'spip_types_documents', 'extension=' . sql_quote(corriger_extension($r[1])) ) ) { $extension = $t['extension']; $name = preg_replace(',\.[^.]*$,', '', $doc['name']) . '.' . $extension; } else { if ($t = sql_fetsel('extension', 'spip_types_documents', 'mime_type=' . sql_quote($doc['type']))) { $extension = $t['extension']; $name = preg_replace(',\.[^.]*$,', '', $doc['name']) . '.' . $extension; } } return array($extension, $name); } } // // Gestion des fichiers ZIP // // https://code.spip.net/@accepte_fichier_upload function accepte_fichier_upload($f) { if (!preg_match(',.*__MACOSX/,', $f) and !preg_match(',^\.,', basename($f)) ) { include_spip('action/ajouter_documents'); $ext = corriger_extension((strtolower(substr(strrchr($f, '.'), 1)))); return sql_countsel( 'spip_types_documents', 'extension=' . sql_quote($ext) . " AND upload='oui'" ); } } # callback pour le deballage d'un zip telecharge # http://www.phpconcept.net/pclzip/man/en/?options-pclzip_cb_pre_extractfunction // https://code.spip.net/@callback_deballe_fichier function callback_deballe_fichier($p_event, &$p_header) { if (accepte_fichier_upload($p_header['filename'])) { $p_header['filename'] = _TMP_DIR . basename($p_header['filename']); return 1; } else { return 0; } } vignette.php000064400000004373152525410300007106 0ustar00<?php /***************************************************************************\ * SPIP, Systeme de publication pour l'internet * * * * Copyright (c) 2001-2020 * * 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 des vignettes de types de fichier * * @package SPIP\Medias\Vignette **/ if (!defined('_ECRIRE_INC_VERSION')) { return; } /** * Vignette pour une extension de document * * Recherche les fichiers d'icones au format png pour l'extension demandée. * On cherche prive/vignettes/ext.png dans le path. * * @param string $ext * Extension du fichier. Exemple : png * @param bool $size * true pour retourner un tableau avec les tailles de la vignette * false pour retourner uniquement le chemin du fichier * @param bool $loop * Autoriser la fonction à s'appeler sur elle-même * (paramètre interne). * @return array|bool|string * False si l'image n'est pas trouvée * Chaîne (chemin vers l'image) si on ne demande pas de taille * Tableau (chemin, largeur, hauteur) si on demande avec la taille. */ function inc_vignette_dist($ext, $size = true, $loop = true) { if (!$ext) { $ext = 'txt'; } // Chercher la vignette correspondant a ce type de document // dans les vignettes persos, ou dans les vignettes standard if ( # installation dans un dossier /vignettes personnel, par exemple /squelettes/vignettes !$v = find_in_path('prive/vignettes/' . $ext . '.png') ) { if ($loop) { $f = charger_fonction('vignette', 'inc'); $v = $f('defaut', false, $loop = false); } else { $v = false; } } # pas trouve l'icone de base if (!$size) { return $v; } $largeur = $hauteur = 0; if ($v and $size = @getimagesize($v)) { $largeur = $size[0]; $hauteur = $size[1]; } return array($v, $largeur, $hauteur); } marquer_doublons_doc.php000064400000013117152525410300011463 0ustar00<?php /***************************************************************************\ * SPIP, Systeme de publication pour l'internet * * * * Copyright (c) 2001-2020 * * 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. * \***************************************************************************/ /** * Analyse des textes pour trouver et marquer comme vu les documents utilisés dedans * * @package SPIP\Medias\Fonctions **/ if (!defined('_ECRIRE_INC_VERSION')) { return; } // la dist ne regarde que chapo et texte, on laisse comme ca, // mais ca permet d etendre a descriptif ou toto depuis d autres plugins $GLOBALS['medias_liste_champs'][] = 'texte'; $GLOBALS['medias_liste_champs'][] = 'chapo'; /** * Trouver les documents utilisés dans le texte d'un objet et enregistrer cette liaison comme vue. * * La liste des champs susceptibles de contenir des documents ou images est indiquée * par la globale `medias_liste_champs` (un tableau). * * Le contenu de ces champs (du moins ceux qui existent pour l'objet demandé) est récupéré et analysé. * La présence d'un modèle de document dans ces contenus, tel que imgXX, docXX ou embXX * indique que le document est utilisé et doit être lié à l'objet, avec le champ `vu=oui` * * S'il y avait des anciens liens avec vu=oui qui n'ont plus lieu d'être, ils passent à non. * * @note * La fonction pourrait avoir bien moins d'arguments : seuls $champs, $id, $type ou $objet, $desc, $serveur * sont nécessaires. On calcule $desc s'il est absent, et il contient toutes les infos… * * @param array $champs * Couples [champ => valeur] connus de l'objet * @param int $id * Identifiant de l'objet * @param string $type * Type d'objet éditorial (ex: article) * @param string $id_table_objet * Nom de la clé primaire sur la table sql de l'objet * @param string $table_objet * Nom de l'objet éditorial (ex: articles) * @param string $spip_table_objet * Nom de la table sql de l'objet * @param array $desc * Description de l'objet, si déjà calculé * @param string $serveur * Serveur sql utilisé. * @return void|null **/ function inc_marquer_doublons_doc_dist( $champs, $id, $type, $id_table_objet, $table_objet, $spip_table_objet, $desc = array(), $serveur = '' ) { // On conserve uniquement les champs qui modifient le calcul des doublons de documents // S'il n'y en a aucun, les doublons ne sont pas impactés, donc rien à faire d'autre.. if (!$champs = array_intersect_key($champs, array_flip($GLOBALS['medias_liste_champs']))) { return; } if (!$desc) { $trouver_table = charger_fonction('trouver_table', 'base'); $desc = $trouver_table($table_objet, $serveur); } // Il faut récupérer toutes les données qui impactent les liens de documents vus // afin de savoir lesquels sont présents dans les textes, et pouvoir actualiser avec // les liens actuellement enregistrés. $absents = array(); // Récupérer chaque champ impactant qui existe dans la table de l'objet et qui nous manque foreach ($GLOBALS['medias_liste_champs'] as $champ) { if (isset($desc['field'][$champ]) and !isset($champs[$champ])) { $absents[] = $champ; } } // Retrouver les textes des champs manquants if ($absents) { $row = sql_fetsel($absents, $spip_table_objet, "$id_table_objet=" . sql_quote($id)); if ($row) { $champs = array_merge($row, $champs); } } include_spip('inc/texte'); include_spip('base/abstract_sql'); include_spip('action/editer_liens'); include_spip('base/objets'); // récupérer la liste des modèles qui considèrent un document comme vu s'ils sont utilisés dans un texte $modeles = lister_tables_objets_sql('spip_documents'); $modeles = $modeles['modeles']; // liste d'id_documents trouvés dans les textes $GLOBALS['doublons_documents_inclus'] = array(); // detecter les doublons dans ces textes traiter_modeles(implode(' ', $champs), array('documents' => $modeles), '', '', null, array( 'objet' => $type, 'id_objet' => $id, $id_table_objet => $id )); $texte_documents_vus = $GLOBALS['doublons_documents_inclus']; // on ne modifie les liaisons que si c'est nécessaire $bdd_documents_vus = array( 'oui' => array(), 'non' => array() ); $liaisons = objet_trouver_liens(array('document' => '*'), array($type => $id)); foreach ($liaisons as $l) { $bdd_documents_vus[$l['vu']][] = $l['id_document']; } // il y a des nouveaux documents vus dans le texte $nouveaux = array_diff($texte_documents_vus, $bdd_documents_vus['oui']); // il y a des anciens documents vus dans la bdd $anciens = array_diff($bdd_documents_vus['oui'], $texte_documents_vus); if ($nouveaux) { // on vérifie que les documents indiqués vus existent réellement tout de même (en cas d'erreur de saisie) $ids = sql_allfetsel('id_document', 'spip_documents', sql_in('id_document', $nouveaux)); $ids = array_map('reset', $ids); if ($ids) { // Creer le lien s'il n'existe pas déjà objet_associer(array('document' => $ids), array($type => $id), array('vu' => 'oui')); objet_qualifier_liens(array('document' => $ids), array($type => $id), array('vu' => 'oui')); } } if ($anciens) { objet_qualifier_liens(array('document' => $anciens), array($type => $id), array('vu' => 'non')); } } revisions_autoriser.php000064400000005145152526156350011412 0ustar00<?php /***************************************************************************\ * SPIP, Systeme de publication pour l'internet * * * * Copyright (c) 2001-2020 * * 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. * \***************************************************************************/ /** * Autorisations du plugin révisions * * @package SPIP\Revisions\Autorisations **/ if (!defined('_ECRIRE_INC_VERSION')) { return; } /** * Fonction du pipeline autoriser. N'a rien à faire. * * @pipeline autoriser */ function revisions_autoriser() { } /** * Autorisation de voir les revisions ? * * Il faut : * - des revisions définies pour cet objet * - que l'objet existe * - que l'on soit autorisé à voir l'objet * * @param string $faire Action demandée * @param string $type Type d'objet sur lequel appliquer l'action * @param int $id Identifiant de l'objet * @param array $qui Description de l'auteur demandant l'autorisation * @param array $opt Options de cette autorisation * @return bool true s'il a le droit, false sinon */ function autoriser_voirrevisions_dist($faire, $type, $id, $qui, $opt) { $table = table_objet_sql($type); $id_table_objet = id_table_objet($type); include_spip('inc/revisions'); if (!liste_champs_versionnes($table)) { return false; } if (!$row = sql_fetsel("*", $table, "$id_table_objet=" . intval($id))) { return false; } return autoriser('voir', $type, $id, $qui, $opt); } /** * Autorisation de voir le menu révisions * * Il faut des révisions activées et présentes. * * @param string $faire Action demandée * @param string $type Type d'objet sur lequel appliquer l'action * @param int $id Identifiant de l'objet * @param array $qui Description de l'auteur demandant l'autorisation * @param array $opt Options de cette autorisation * @return bool true s'il a le droit, false sinon */ function autoriser_revisions_menu_dist($faire, $type = '', $id = 0, $qui = null, $opt = null) { // SI pas de revisions sur un objet quelconque. // ET pas de version... pas de bouton, c'est inutile... include_spip('inc/config'); if (!lire_config('objets_versions/') and !sql_countsel('spip_versions')) { return false; } return true; } suivi_versions.php000064400000014575152526156350010372 0ustar00<?php /***************************************************************************\ * SPIP, Systeme de publication pour l'internet * * * * Copyright (c) 2001-2020 * * 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. * \***************************************************************************/ /** * Fonctions de suivi de versions * * @package SPIP\Revisions\Versions **/ if (!defined("_ECRIRE_INC_VERSION")) { return; } include_spip('inc/revisions'); include_spip('inc/diff'); /** * Afficher un diff correspondant à une révision d'un objet * * @param int $id_objet Identifiant de l'objet * @param string $objet Objet * @param int $id_version Identifiant de la version * @param bool $court * - false : affiche le diff complet * - true : indique juste la taille en octets des changements * @return string * Texte HTML du diff. */ function revisions_diff($id_objet, $objet, $id_version, $court = false) { $textes = revision_comparee($id_objet, $objet, $id_version, 'diff'); if (!is_array($textes)) { return $textes; } $rev = ''; $nb = 0; foreach ($textes as $var => $t) { if ($n = strlen($t)) { if ($court) { $nb += $n; } else { $aff = propre_diff($t); if ($GLOBALS['les_notes']) { $aff .= '<p>' . $GLOBALS['les_notes'] . '</p>'; $GLOBALS['les_notes'] = ''; } $rev .= "<blockquote>$aff</blockquote>"; } } } return $court ? _T('taille_octets', array('taille' => $nb)) : $rev; } /** * Retrouver le champ d'un objet, pour une version demandée * * Si le champ n'est pas déjà présent dans la liste des champs ($champs), * on remonte les versions à partir du id_version donné, jusqu'à * récupérer une version qui contient ce champ. On complète alors la liste * des champs avec la version du champ trouvée. * * @param string $objet Objet * @param int $id_objet Identifiant de l'objet * @param int $id_version Identifiant de la version * @param string $champ Le nom du champ à retrouver * @param array $champs Liste des champs déjà connus * @return void */ function retrouver_champ_version_objet($objet, $id_objet, $id_version, $champ, &$champs) { if (isset($champs[$champ])) { return; } // Remonter dans le temps pour trouver le champ en question // pour la version demandee $id_ref = $id_version - 1; $prev = array(); while (!isset($prev[$champ]) and $id_ref > 0) { $prev = recuperer_version($id_objet, $objet, $id_ref--); } if (isset($prev[$champ])) { $champs[$champ] = $prev[$champ]; } else { // le champ n'a jamais ete versionne : // il etait initialement vide $champs[$champ] = ''; } } /** * Liste les champs modifiés par une version de révision donnée * * Pour un couple objet/id_objet et id_version donné, calcule les champs * qui ont été modifiés depuis une version précédente et la version * d'id_version, et les retourne. * * La version précédente est par défaut la version juste * avant id_version, mais peut être définie via le paramètre id_diff. * * Le retour est plus ou moins locace en fonction du paramètre format. * * @param int $id_objet Identifiant de l'objet * @param string $objet Objet * @param int $id_version Identifiant de la version * @param string $format * Type de retour * - diff => seulement les modifs (page revisions) * - apercu => idem, mais en plus tres cout s'il y en a bcp * - complet => tout, avec surlignage des modifications (page revision) * @param null $id_diff * Identifiant de la version de base du diff, par défaut l'id_version * juste précédent * @return array * Couples (champ => texte) */ function revision_comparee($id_objet, $objet, $id_version, $format = 'diff', $id_diff = null) { include_spip('inc/diff'); // chercher le numero de la version precedente if (!$id_diff) { $id_diff = sql_getfetsel("id_version", "spip_versions", "id_objet=" . intval($id_objet) . " AND id_version < " . intval($id_version) . " AND objet=" . sql_quote($objet), "", "id_version DESC", "1"); } if ($id_version && $id_diff) { // si l'ordre est inverse, on remet a l'endroit if ($id_diff > $id_version) { $t = $id_version; $id_version = $id_diff; $id_diff = $t; } $old = recuperer_version($id_objet, $objet, $id_diff); $new = recuperer_version($id_objet, $objet, $id_version); $textes = array(); // Mode "diff": on ne s'interesse qu'aux champs presents dans $new // Mode "complet": on veut afficher tous les champs switch ($format) { case 'complet': $champs = liste_champs_versionnes(table_objet_sql($objet)); break; case 'diff': case 'apercu': default: $champs = array_keys($new); break; } // memoriser les cas les plus courant $afficher_diff_champ = charger_fonction('champ', 'afficher_diff'); $afficher_diff_jointure = charger_fonction('jointure', 'afficher_diff'); foreach ($champs as $champ) { // Remonter dans le temps pour trouver le champ en question // pour chaque version retrouver_champ_version_objet($objet, $id_objet, $id_version, $champ, $new); retrouver_champ_version_objet($objet, $id_objet, $id_diff, $champ, $old); if (!strlen($new[$champ]) && !strlen($old[$champ])) { continue; } // si on n'a que le vieux, ou que le nouveau, on ne // l'affiche qu'en mode "complet" if ($format == 'complet') { $textes[$champ] = strlen($new[$champ]) ? $new[$champ] : $old[$champ]; } // si on a les deux, le diff nous interesse, plus ou moins court if (isset($new[$champ]) and isset($old[$champ])) { if (!$afficher_diff = charger_fonction($objet . "_" . $champ, 'afficher_diff', true) and !$afficher_diff = charger_fonction($champ, 'afficher_diff', true) ) { $afficher_diff = (strncmp($champ, 'jointure_', 9) == 0 ? $afficher_diff_jointure : $afficher_diff_champ); } $textes[$champ] = $afficher_diff($champ, $old[$champ], $new[$champ], $format); } } } // que donner par defaut ? (par exemple si id_version=1) if (!$textes) { $textes = recuperer_version($id_objet, $objet, $id_version); } return $textes; } revisions_pipeline.php000064400000031215152526156350011177 0ustar00<?php /***************************************************************************\ * SPIP, Systeme de publication pour l'internet * * * * Copyright (c) 2001-2020 * * 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. * \***************************************************************************/ /** * Pipelines utilisés du plugin révisions * * @package SPIP\Revisions\Pipelines **/ if (!defined('_ECRIRE_INC_VERSION')) { return; } /** * Ajoute dans le bloc d'info d'un objet un bouton permettant d'aller voir * l'historique de ses révisions * * @param array $flux Données du pipeline * @return array $flux Données du pipeline */ function revisions_boite_infos($flux) { $type = $flux['args']['type']; if ($id = intval($flux['args']['id']) and $tables = unserialize($GLOBALS['meta']['objets_versions']) and in_array(table_objet_sql($type), $tables) and autoriser('voirrevisions', $type, $id) // regarder le numero de revision le plus eleve, et afficher le bouton // si c'est interessant (id_version>1) and sql_countsel('spip_versions', 'id_objet=' . intval($id) . ' AND objet = ' . sql_quote($type)) > 1 ) { include_spip('inc/presentation'); $flux['data'] .= icone_horizontale(_T('revisions:info_historique_lien'), generer_url_ecrire('revision', "id_objet=$id&objet=$type"), "revision-24.png"); } return $flux; } /** * Afficher les dernières révisions sur l'accueil et le suivi * * Liste les révisions en bas de la page d'accueil de ecrire/ * et sur la page de suivi de l'activité du site * * @param array $flux Données du pipeline * @return array $flux Données du pipeline */ function revisions_affiche_milieu($flux) { // la bonne page et des objets révisables cochées ! if (in_array($flux['args']['exec'], array('accueil', 'suivi_edito')) and unserialize($GLOBALS['meta']['objets_versions']) ) { $contexte = array(); if ($GLOBALS['visiteur_session']['statut'] !== '0minirezo') { $contexte['id_auteur'] = $GLOBALS['visiteur_session']['id_auteur']; } $flux['data'] .= recuperer_fond('prive/objets/liste/versions', $contexte, array('ajax' => true)); } return $flux; } /** * Définir les metas de configuration liées aux révisions * * Utilisé par inc/config * * @param array $metas Liste des métas et leurs valeurs par défaut * @return array Liste des métas et leurs valeurs par défaut */ function revisions_configurer_liste_metas($metas) { // Dorénavant dans les metas on utilisera un array serialisé de types d'objets // qui correspondront aux objets versionnés $metas['objets_versions'] = ''; return $metas; } /** * Charge les données d'une révision donnée dans le formulaire d'édition d'un objet * * @param array $flux Données du pipeline * @return array $flux Données du pipeline */ function revisions_formulaire_charger($flux) { if (strncmp($flux['args']['form'], 'editer_', 7) == 0 and $id_version = _request('id_version') and $objet = substr($flux['args']['form'], 7) and $id_table_objet = id_table_objet($objet) and isset($flux['data'][$id_table_objet]) and $id = intval($flux['data'][$id_table_objet]) and !$flux['args']['je_suis_poste'] ) { // ajouter un message convival pour indiquer qu'on a restaure la version $flux['data']['message_ok'] = _T('revisions:icone_restaurer_version', array('version' => $id_version)); $flux['data']['message_ok'] .= "<br />" . _T('revisions:message_valider_recuperer_version'); // recuperer la version include_spip('inc/revisions'); $champs = recuperer_version_complete($id, $objet, $id_version); foreach ($champs as $champ => $valeur) { if (!strncmp($champ, 'jointure_', 9) == 0) { if ($champ == 'id_rubrique') { $flux['data']['id_parent'] = $valeur; } else { $flux['data'][$champ] = $valeur; } } } } return $flux; } /** * Sur une insertion en base, lever un flag pour ne pas creer une premiere révision vide * * @param array $x Données du pipeline * @return array $x Données du pipeline */ function revisions_pre_insertion($x) { $table = $x['args']['table']; include_spip('inc/revisions'); if ($champs = liste_champs_versionnes($table)) { // on ne connait pas encore l'ID, mais on leve un flag pour ne pas creer de premiere revision pour ce type d'objet // sur une simple jointure (auteur) $GLOBALS['premiere_revision']["$table:0"] = true; } return $x; } /** * Sur une insertion en base, lever un flag pour ne pas creer une premiere révision vide * dans pre_edition mais attendre la post_edition pour cela * * @param array $x Données du pipeline * @return array $x Données du pipeline */ function revisions_post_insertion($x) { $table = $x['args']['table']; include_spip('inc/revisions'); if ($champs = liste_champs_versionnes($table)) { if (isset($GLOBALS['premiere_revision']["$table:0"])) { unset($GLOBALS['premiere_revision']["$table:0"]); } $GLOBALS['premiere_revision']["$table:" . $x['args']['id_objet']] = true; } return $x; } /** * Avant toute modification en base * vérifier qu'une version initiale existe bien pour cet objet * et la creer sinon avec l'etat actuel de l'objet * * @param array $x Données du pipeline * @return array $x Données du pipeline */ function revisions_pre_edition($x) { // ne rien faire quand on passe ici en controle md5 if (!isset($x['args']['action']) or $x['args']['action'] !== 'controler' ) { $table = $x['args']['table']; include_spip('inc/revisions'); // si flag leve passer son chemin, post_edition le fera (mais baisser le flag en le gardant en memoire tout de meme) if (isset($GLOBALS['premiere_revision']["$table:" . $x['args']['id_objet']])) { $GLOBALS['premiere_revision']["$table:" . $x['args']['id_objet']] = 0; } // sinon creer une premiere revision qui date et dont on ne connait pas l'auteur elseif ($versionnes = liste_champs_versionnes($table)) { $objet = isset($x['args']['type']) ? $x['args']['type'] : objet_type($table); verifier_premiere_revision($table, $objet, $x['args']['id_objet'], $versionnes, -1); } } return $x; } /** * Avant modification en base d'un lien, * enregistrer une première révision de l'objet si nécessaire * * @param array $x Données du pipeline * @return array $x Données du pipeline */ function revisions_pre_edition_lien($x) { if (intval($x['args']['id_objet_source']) > 0 and intval($x['args']['id_objet']) > 0 ) { $table = table_objet_sql($x['args']['objet']); $id_objet = intval($x['args']['id_objet']); include_spip('inc/revisions'); // si c'est une creation de lien qui arrive aussitot apres l'insertion on ne cree pas de revision if (isset($GLOBALS['premiere_revision']["$table:0"])) { unset($GLOBALS['premiere_revision']["$table:0"]); $GLOBALS['premiere_revision']["$table:" . $id_objet] = true; } if (isset($GLOBALS['premiere_revision']["$table:" . $id_objet])) { $GLOBALS['premiere_revision']["$table:" . $id_objet] = 0; } // ex : si le champ jointure_mots est versionnable sur les articles elseif ($versionnes = liste_champs_versionnes($table) and in_array($j = 'jointure_' . table_objet($x['args']['objet_source']), $versionnes) ) { verifier_premiere_revision($table, $x['args']['objet'], $id_objet, $versionnes, -1); } $table = table_objet_sql($x['args']['objet_source']); $id_objet = $x['args']['id_objet_source']; // si c'est une creation de lien qui arrive aussitot apres l'insertion on ne cree pas de revision if (isset($GLOBALS['premiere_revision']["$table:0"])) { unset($GLOBALS['premiere_revision']["$table:0"]); $GLOBALS['premiere_revision']["$table:" . $id_objet] = true; } if (isset($GLOBALS['premiere_revision']["$table:" . $id_objet])) { $GLOBALS['premiere_revision']["$table:" . $id_objet] = 0; } // ex : si le champ jointure_articles est versionnable sur les mots elseif ($versionnes = liste_champs_versionnes($table) and in_array($j = 'jointure_' . table_objet($x['args']['objet']), $versionnes) ) { verifier_premiere_revision($table, $x['args']['objet_source'], $id_objet, $versionnes, -1); } } return $x; } /** * Après modification en base, versionner l'objet * * @param array $x Données du pipeline * @return array $x Données du pipeline */ function revisions_post_edition($x) { include_spip('inc/revisions'); if (isset($x['args']['table']) and $versionnes = liste_champs_versionnes($x['args']['table'])) { // Regarder si au moins une des modifs est versionnable $champs = array(); $table = $x['args']['table']; $objet = isset($x['args']['type']) ? $x['args']['type'] : objet_type($table); include_spip('inc/session'); if (isset($GLOBALS['premiere_revision']["$table:" . $x['args']['id_objet']])) { unset($GLOBALS['premiere_revision']["$table:" . $x['args']['id_objet']]); // verifier la premiere version : sur une version initiale on attend ici pour la creer // plutot que de creer une version vide+un diff verifier_premiere_revision($table, $objet, $x['args']['id_objet'], $versionnes, session_get('id_auteur')); } else { // on versionne les differences foreach ($versionnes as $key) { if (isset($x['data'][$key])) { $champs[$key] = $x['data'][$key]; } } if (count($champs)) { ajouter_version($x['args']['id_objet'], $objet, $champs, '', session_get('id_auteur')); } } } return $x; } /** * Après modification en base d'un lien, versionner l'objet si nécessaire * * @param array $x Données du pipeline * @return array $x Données du pipeline */ function revisions_post_edition_lien($x) { /*pipeline('post_edition_lien', array( 'args' => array( 'table_lien' => $table_lien, 'objet_source' => $objet_source, 'id_objet_source' => $l[$primary], 'objet' => $l['objet'], 'id_objet' => $id_o, 'action'=>'delete', ), 'data' => $couples ) */ if (intval($x['args']['id_objet_source']) > 0 and intval($x['args']['id_objet']) > 0 ) { $table = table_objet_sql($x['args']['objet']); $id_objet = $x['args']['id_objet']; include_spip('inc/revisions'); include_spip('inc/session'); // si c'est une creation de lien qui arrive aussitot apres l'insertion on ne cree pas de revision // (au cas ou pre_edition_lien n'aurait pas ete appele ?) if (isset($GLOBALS['premiere_revision']["$table:0"])) { unset($GLOBALS['premiere_revision']["$table:0"]); $GLOBALS['premiere_revision']["$table:" . $id_objet] = true; } if (isset($GLOBALS['premiere_revision']["$table:" . $id_objet])) { $GLOBALS['premiere_revision']["$table:" . $id_objet] = 0; } // ex : si le champ jointure_mots est versionnable sur les articles elseif ($versionnes = liste_champs_versionnes($table) and in_array($j = 'jointure_' . table_objet($x['args']['objet_source']), $versionnes) ) { $champs = array( $j => recuperer_valeur_champ_jointure($x['args']['objet'], $id_objet, $x['args']['objet_source']) ); ajouter_version($id_objet, $x['args']['objet'], $champs, '', session_get('id_auteur')); } $table = table_objet_sql($x['args']['objet_source']); $id_objet = $x['args']['id_objet_source']; // si c'est une creation de lien qui arrive aussitot apres l'insertion on ne cree pas de revision // (au cas ou pre_edition_lien n'aurait pas ete appele ?) if (isset($GLOBALS['premiere_revision']["$table:0"])) { unset($GLOBALS['premiere_revision']["$table:0"]); $GLOBALS['premiere_revision']["$table:" . $id_objet] = true; } if (isset($GLOBALS['premiere_revision']["$table:" . $id_objet])) { $GLOBALS['premiere_revision']["$table:" . $id_objet] = 0; } // ex : si le champ jointure_articles est versionnable sur les mots elseif ($versionnes = liste_champs_versionnes($table) and in_array($j = 'jointure_' . table_objet($x['args']['objet']), $versionnes) ) { $champs = array( $j => recuperer_valeur_champ_jointure($x['args']['objet_source'], $id_objet, $x['args']['objet']) ); ajouter_version($id_objet, $x['args']['objet_source'], $champs, '', session_get('id_auteur')); } } return $x; } /** * Ajoute la tâche d'optimisation des tables dans la liste des tâches périodiques * * @pipeline taches_generales_cron * * @param array $taches_generales * Tableau des tâches et leur périodicité en seconde * @return array * Tableau des tâches et leur périodicité en seconde */ function revisions_taches_generales_cron($taches_generales) { $taches_generales['optimiser_revisions'] = 86400; return $taches_generales; } revisions.php000064400000071776152526156350007332 0ustar00<?php /***************************************************************************\ * SPIP, Systeme de publication pour l'internet * * * * Copyright (c) 2001-2020 * * 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. * \***************************************************************************/ /** * Fonctions utilitaires du plugin révisions * * @package SPIP\Revisions\Fonctions **/ if (!defined("_ECRIRE_INC_VERSION")) { return; } $GLOBALS['agregation_versions'] = 10; /** Intervalle de temps (en seconde) separant deux révisions par un même auteur */ define('_INTERVALLE_REVISIONS', 600); /** * Découper les paragraphes d'un texte en fragments * * @param string $texte Texte à fragmenter * @param array $paras Tableau de fragments déjà là * @return string[] Tableau de fragments (paragraphes) **/ function separer_paras($texte, $paras = array()) { if (!$paras) { $paras = array(); } while (preg_match("/(\r\n?){2,}|\n{2,}/", $texte, $regs)) { $p = strpos($texte, $regs[0]) + strlen($regs[0]); $paras[] = substr($texte, 0, $p); $texte = substr($texte, $p); } if ($texte) { $paras[] = $texte; } return $paras; } // https://code.spip.net/@replace_fragment function replace_fragment($id_objet, $objet, $version_min, $version_max, $id_fragment, $fragment) { $fragment = serialize($fragment); $compress = 0; /* On ne compresse plus les fragments car probleme de portabilite de base, corruptions de donnees en backup SQLite ou meme en mysqldump */ // pour le portage en PG il faut l'equivalente au mysql_escape_string // et deporter son appel dans les fonctions d'abstraction. /* if (function_exists('gzcompress') AND $GLOBALS['connexions'][0]['type'] == 'mysql') { $s = gzcompress($fragment); if (strlen($s) < strlen($fragment)) { # spip_log("gain gz: ".intval(100 - 100 * strlen($s) / strlen($fragment)),'revisions'); $compress = 1; $fragment = $s; } } */ // Attention a echapper $fragment, binaire potentiellement gz return array( 'id_objet' => intval($id_objet), 'objet' => $objet, 'id_fragment' => intval($id_fragment), 'version_min' => intval($version_min), 'version_max' => intval($version_max), 'compress' => $compress, 'fragment' => $fragment ); } // https://code.spip.net/@envoi_replace_fragments function envoi_replace_fragments($replaces) { $desc = $GLOBALS['tables_auxiliaires']['spip_versions_fragments']; foreach ($replaces as $r) { sql_replace('spip_versions_fragments', $r, $desc); } } // https://code.spip.net/@envoi_delete_fragments function envoi_delete_fragments($id_objet, $objet, $deletes) { if (count($deletes)) { sql_delete("spip_versions_fragments", "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet) . " AND ((" . join(") OR (", $deletes) . "))"); } } // // Ajouter les fragments de la derniere version (tableau associatif id_fragment => texte) // // https://code.spip.net/@ajouter_fragments function ajouter_fragments($id_objet, $objet, $id_version, $fragments) { global $agregation_versions; $replaces = array(); foreach ($fragments as $id_fragment => $texte) { $nouveau = true; // Recuperer la version la plus recente $row = sql_fetsel("compress, fragment, version_min, version_max", "spip_versions_fragments", "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet) . " AND id_fragment=$id_fragment AND version_min<=$id_version", "", "version_min DESC", "1"); if ($row) { $fragment = $row['fragment']; $version_min = $row['version_min']; if ($row['compress'] > 0) { $fragment = @gzuncompress($fragment); } $fragment = unserialize($fragment); if (is_array($fragment)) { unset($fragment[$id_version]); // Si le fragment n'est pas trop gros, prolonger celui-ci $nouveau = count($fragment) >= $agregation_versions && strlen($row['fragment']) > 1000; } } if ($nouveau) { $fragment = array($id_version => $texte); $version_min = $id_version; } else { // Ne pas dupliquer les fragments non modifies $modif = true; for ($i = $id_version - 1; $i >= $version_min; $i--) { if (isset($fragment[$i])) { $modif = ($fragment[$i] != $texte); break; } } if ($modif) { $fragment[$id_version] = $texte; } } // Preparer l'enregistrement du fragment $replaces[] = replace_fragment($id_objet, $objet, $version_min, $id_version, $id_fragment, $fragment); } envoi_replace_fragments($replaces); } // // Supprimer tous les fragments d'un objet lies a un intervalle de versions // (essaie d'eviter une trop grande fragmentation) // // https://code.spip.net/@supprimer_fragments function supprimer_fragments($id_objet, $objet, $version_debut, $version_fin) { global $agregation_versions; $replaces = array(); $deletes = array(); // D'abord, vider les fragments inutiles sql_delete("spip_versions_fragments", "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet) . " AND version_min>=$version_debut AND version_max<=$version_fin"); // Fragments chevauchant l'ensemble de l'intervalle, s'ils existent $result = sql_select("id_fragment, compress, fragment, version_min, version_max", "spip_versions_fragments", "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet) . " AND version_min<$version_debut AND version_max>$version_fin"); while ($row = sql_fetch($result)) { $id_fragment = $row['id_fragment']; $fragment = $row['fragment']; if ($row['compress'] > 0) { $fragment = gzuncompress($fragment); } $fragment = unserialize($fragment); for ($i = $version_fin; $i >= $version_debut; $i--) { if (isset($fragment[$i])) { // Recopier le dernier fragment si implicite if (!isset($fragment[$version_fin + 1])) { $fragment[$version_fin + 1] = $fragment[$i]; } unset($fragment[$i]); } } $replaces[] = replace_fragment($id_objet, $objet, $row['version_min'], $row['version_max'], $id_fragment, $fragment); } // Fragments chevauchant le debut de l'intervalle, s'ils existent $result = sql_select("id_fragment, compress, fragment, version_min, version_max", "spip_versions_fragments", "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet) . " AND version_min<$version_debut AND version_max>=$version_debut AND version_max<=$version_fin"); $deb_fragment = array(); while ($row = sql_fetch($result)) { $id_fragment = $row['id_fragment']; $fragment = $row['fragment']; $version_min = $row['version_min']; $version_max = $row['version_max']; if ($row['compress'] > 0) { $fragment = gzuncompress($fragment); } $fragment = unserialize($fragment); for ($i = $version_debut; $i <= $version_max; $i++) { if (isset($fragment[$i])) { unset($fragment[$i]); } } // Stocker temporairement le fragment pour agregation $deb_fragment[$id_fragment] = $fragment; // Ajuster l'intervalle des versions $deb_version_min[$id_fragment] = $version_min; $deb_version_max[$id_fragment] = $version_debut - 1; } // Fragments chevauchant la fin de l'intervalle, s'ils existent $result = sql_select("id_fragment, compress, fragment, version_min, version_max", "spip_versions_fragments", "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet) . " AND version_max>$version_fin AND version_min>=$version_debut AND version_min<=$version_fin"); while ($row = sql_fetch($result)) { $id_fragment = $row['id_fragment']; $fragment = $row['fragment']; $version_min = $row['version_min']; $version_max = $row['version_max']; if ($row['compress'] > 0) { $fragment = gzuncompress($fragment); } $fragment = unserialize($fragment); for ($i = $version_fin; $i >= $version_min; $i--) { if (isset($fragment[$i])) { // Recopier le dernier fragment si implicite if (!isset($fragment[$version_fin + 1])) { $fragment[$version_fin + 1] = $fragment[$i]; } unset($fragment[$i]); } } // Virer l'ancien enregistrement (la cle primaire va changer) $deletes[] = "id_fragment=$id_fragment AND version_min=$version_min"; // Essayer l'agregation $agreger = false; if (isset($deb_fragment[$id_fragment])) { $agreger = (count($deb_fragment[$id_fragment]) + count($fragment) <= $agregation_versions); if ($agreger) { $fragment = $deb_fragment[$id_fragment] + $fragment; $version_min = $deb_version_min[$id_fragment]; } else { $replaces[] = replace_fragment($id_objet, $objet, $deb_version_min[$id_fragment], $deb_version_max[$id_fragment], $id_fragment, $deb_fragment[$id_fragment]); } unset($deb_fragment[$id_fragment]); } if (!$agreger) { // Ajuster l'intervalle des versions $version_min = $version_fin + 1; } $replaces[] = replace_fragment($id_objet, $objet, $version_min, $version_max, $id_fragment, $fragment); } // Ajouter fragments restants if (is_array($deb_fragment) && count($deb_fragment) > 0) { foreach ($deb_fragment as $id_fragment => $fragment) { $replaces[] = replace_fragment($id_objet, $objet, $deb_version_min[$id_fragment], $deb_version_max[$id_fragment], $id_fragment, $deb_fragment[$id_fragment]); } } envoi_replace_fragments($replaces); envoi_delete_fragments($id_objet, $objet, $deletes); } /** * Récupérer les fragments d'un objet pour une version demandée * * @param int $id_objet Identifiant de l'objet * @param string $objet Objet * @param int $id_version Identifiant de la version * @return array Couples id_fragment => texte */ function recuperer_fragments($id_objet, $objet, $id_version) { $fragments = array(); if ($id_version == 0) { return array(); } $result = sql_select( "id_fragment, version_min, version_max, compress, fragment", "spip_versions_fragments", "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet) . " AND version_min<=$id_version AND version_max>=$id_version"); while ($row = sql_fetch($result)) { $id_fragment = $row['id_fragment']; $version_min = $row['version_min']; $fragment = $row['fragment']; // si le fragment est compressé, tenter de le décompresser, sinon écrire une erreur if ($row['compress'] > 0) { $fragment_ = @gzuncompress($fragment); if (strlen($fragment) && $fragment_ === false) { $fragment = serialize(array($row['version_max'] => "[" . _T('forum_titre_erreur') . $id_fragment . "]")); } else { $fragment = $fragment_; } } // tenter dedésérialiser le fragment, sinon écrire une erreur $fragment_ = unserialize($fragment); if (strlen($fragment) && $fragment_ === false) { $fragment = array($row['version_max'] => "[" . _T('forum_titre_erreur') . $id_fragment . "]"); } else { $fragment = $fragment_; } // on retrouve le fragment le plus près de notre version for ($i = $id_version; $i >= $version_min; $i--) { if (isset($fragment[$i])) { ## hack destine a sauver les archives des sites iso-8859-1 ## convertis en utf-8 (les archives ne sont pas converties ## mais ce code va les nettoyer ; pour les autres charsets ## la situation n'est pas meilleure ni pire qu'avant) if ($GLOBALS['meta']['charset'] == 'utf-8' and include_spip('inc/charsets') and !is_utf8($fragment[$i]) ) { $fragment[$i] = importer_charset($fragment[$i], 'iso-8859-1'); } $fragments[$id_fragment] = $fragment[$i]; // quitter la boucle dès le premier touvé. break; } } } return $fragments; } // // Apparier des paragraphes deux a deux entre une version originale // et une version modifiee // // https://code.spip.net/@apparier_paras function apparier_paras($src, $dest, $flou = true) { $src_dest = array(); $dest_src = array(); $t1 = $t2 = array(); $md1 = $md2 = array(); $gz_min1 = $gz_min2 = array(); $gz_trans1 = $gz_trans2 = array(); $l1 = $l2 = array(); // Nettoyage de la ponctuation pour faciliter l'appariement foreach ($src as $key => $val) { $t1[$key] = strval(preg_replace("/[[:punct:][:space:]]+/", " ", $val)); } foreach ($dest as $key => $val) { $t2[$key] = strval(preg_replace("/[[:punct:][:space:]]+/", " ", $val)); } // Premiere passe : chercher les correspondance exactes foreach ($t1 as $key => $val) { $md1[$key] = md5($val); } foreach ($t2 as $key => $val) { $md2[md5($val)][$key] = $key; } foreach ($md1 as $key1 => $h) { if (isset($md2[$h])) { $key2 = reset($md2[$h]); if (isset($t1[$key1]) and isset($t2[$key2]) and $t1[$key1] == $t2[$key2]) { $src_dest[$key1] = $key2; $dest_src[$key2] = $key1; unset($t1[$key1]); unset($t2[$key2]); unset($md2[$h][$key2]); } } } if ($flou) { // Deuxieme passe : recherche de correlation par test de compressibilite foreach ($t1 as $key => $val) { $l1[$key] = strlen(gzcompress($val)); } foreach ($t2 as $key => $val) { $l2[$key] = strlen(gzcompress($val)); } foreach ($t1 as $key1 => $s1) { foreach ($t2 as $key2 => $s2) { $r = strlen(gzcompress($s1 . $s2)); $taux = 1.0 * $r / ($l1[$key1] + $l2[$key2]); if (!isset($gz_min1[$key1]) || !$gz_min1[$key1] || $gz_min1[$key1] > $taux) { $gz_min1[$key1] = $taux; $gz_trans1[$key1] = $key2; } if (!isset($gz_min2[$key2]) || !$gz_min2[$key2] || $gz_min2[$key2] > $taux) { $gz_min2[$key2] = $taux; $gz_trans2[$key2] = $key1; } } } // Depouiller les resultats de la deuxieme passe : // ne retenir que les correlations reciproques foreach ($gz_trans1 as $key1 => $key2) { if ($gz_trans2[$key2] == $key1 && $gz_min1[$key1] < 0.9) { $src_dest[$key1] = $key2; $dest_src[$key2] = $key1; } } } // Retourner les mappings return array($src_dest, $dest_src); } /** * Retrouve les champs d'un objet et leurs contenus à une version donnée * * @uses recuperer_version() * @uses liste_champs_versionnes() * @uses retrouver_champ_version_objet() * * @param int $id_objet Identifiant de l'objet * @param string $objet Objet * @param int $id_version Identifiant de la version * @return array Couples champs => textes **/ function recuperer_version_complete($id_objet, $objet, $id_version) { if (!$id_version or !$id_objet or !$objet) { return array(); } include_spip('inc/suivi_versions'); // champs modifiés à la version voulue $textes = recuperer_version($id_objet, $objet, $id_version); // tous les champs possibles versionnés pour l'objet $champs = liste_champs_versionnes(table_objet_sql($objet)); foreach ($champs as $champ) { // Remonter dans le temps pour trouver le champ en question pour chaque version retrouver_champ_version_objet($objet, $id_objet, $id_version, $champ, $textes); } return $textes; } /** * Récupérer les champs d'un objet, pour une version demandée * * @param int $id_objet Identifiant de l'objet * @param string $objet Objet * @param int $id_version Identifiant de la version * @return array Couples champs => textes */ function recuperer_version($id_objet, $objet, $id_version) { $champs = sql_getfetsel("champs", "spip_versions", "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet) . " AND id_version=" . intval($id_version)); if (!$champs or !is_array($champs = unserialize($champs))) { return array(); } else { return reconstuire_version($champs, recuperer_fragments($id_objet, $objet, $id_version)); } } /** * Reconstruire une version donnée * * À partir de la liste des champs et de fragments, * retourne le texte de chaque champ. * * @param array $champs Couples (champ => liste d'id_fragment). * La liste est de la forme "5 32 7 16 8 2" * @param array $fragments Couples (id_fragment => texte) * @param array $res Couples (champ => texte) déjà connus * @return array Couples (champ => texte) */ function reconstuire_version($champs, $fragments, $res = array()) { static $msg; if (!$msg) { $msg = _T('forum_titre_erreur'); } foreach ($champs as $nom => $code) { if (!isset($res[$nom])) { $t = ''; foreach (array_filter(explode(' ', $code)) as $id) { $t .= isset($fragments[$id]) ? $fragments[$id] : "[$msg$id]"; } $res[$nom] = $t; } } return $res; } // https://code.spip.net/@supprimer_versions function supprimer_versions($id_objet, $objet, $version_min, $version_max) { sql_delete("spip_versions", "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet) . " AND id_version>=$version_min AND id_version<=$version_max"); supprimer_fragments($id_objet, $objet, $version_min, $version_max); } /** * Ajouter une version à un objet éditorial * * @param int $id_objet * @param string $objet * @param array $champs * @param string $titre_version * Titre donné aux modifications apportées * @param int|null $id_auteur * Auteur apportant les modifications. En absence (session anonyme), utilisera l'IP pour garder une trace. * @return int * id_version : identifiant de la version **/ function ajouter_version($id_objet, $objet, $champs, $titre_version = "", $id_auteur = null) { $paras = $paras_old = $paras_champ = $fragments = array(); // Attention a une edition anonyme (type wiki): id_auteur n'est pas // definie, on enregistre alors le numero IP $str_auteur = intval($id_auteur) ? intval($id_auteur) : $GLOBALS['ip']; // si pas de titre dans cette version, la marquer 'non' permanente, // et elle pourra etre fusionnee avec une revision ulterieure dans un delai < _INTERVALLE_REVISIONS // permet de fusionner plusieurs editions consecutives champs par champs avec les crayons $permanent = empty($titre_version) ? 'non' : ''; // Detruire les tentatives d'archivages non abouties en 1 heure sql_delete('spip_versions', array( "id_objet=" . intval($id_objet), "objet=" . sql_quote($objet), "id_version <= 0", "date < DATE_SUB(" . sql_quote(date('Y-m-d H:i:s')) . ", INTERVAL " . _INTERVALLE_REVISIONS . " SECOND)" ) ); // Signaler qu'on opere en mettant un numero de version negatif // distinctif (pour eviter la violation d'unicite) // et un titre contenant en fait le moment de l'insertion list($ms, $sec) = explode(' ', microtime()); // SQL ne ramene que 4 chiffres significatifs apres la virgule pour 0.0+titre_version $date = ($sec . substr($ms, 1, 4)) - 20; $datediff = ($sec - mktime(0, 0, 0, 9, 1, 2007)) * 1000000 + substr($ms, 2, strlen($ms) - 4); $valeurs = array( 'id_objet' => $id_objet, 'objet' => $objet, 'id_version' => (0 - $datediff), 'date' => date('Y-m-d H:i:s'), 'id_auteur' => $str_auteur, // varchar ici! 'titre_version' => $date ); sql_insertq('spip_versions', $valeurs); // Eviter les validations entremelees en s'endormant s'il existe // une version <0 plus recente mais pas plus vieille que 10s // Une <0 encore plus vieille est une operation avortee, // on passe outre (vaut mieux archiver mal que pas du tout). // Pour tester: // 0. mettre le delai a 30 // 1. decommenter le premier sleep(15) // 2. enregistrer une modif // 3. recommenter le premier sleep(15), decommenter le second. // 4. enregistrer une autre modif dans les 15 secondes # sleep(15); $delai = $sec - 10; while (sql_countsel('spip_versions', "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet) . " AND id_version < 0 AND 0.0+titre_version < $date AND titre_version<>" . sql_quote($date, '', 'text') . " AND 0.0+titre_version > $delai")) { spip_log("version $objet $id_objet :insertion en cours avant $date ($delai)", 'revisions'); sleep(1); $delai++; } # sleep(15); spip_log("sortie $sec $delai"); // Determiner le numero du prochain fragment $next = sql_fetsel("id_fragment", "spip_versions_fragments", "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet), "", "id_fragment DESC", "1"); $onlylock = ''; // Examiner la derniere version $row = sql_fetsel("id_version, champs, id_auteur, date, permanent", "spip_versions", "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet) . " AND id_version > 0", '', "id_version DESC", "1"); // le champ id_auteur est un varchar dans cette table if ($row) { $id_version = $row['id_version']; $paras_old = recuperer_fragments($id_objet, $objet, $id_version); $champs_old = $row['champs']; if ($row['id_auteur'] != $str_auteur or $row['permanent'] != 'non' or strtotime($row['date']) < (time() - _INTERVALLE_REVISIONS) ) { spip_log(strtotime($row['date']), 'revisions'); spip_log(time(), 'revisions'); spip_log(_INTERVALLE_REVISIONS, 'revisions'); $id_version++; } // version precedente recente, on va la mettre a jour // avec les nouveaux arrivants si presents else { $champs = reconstuire_version(unserialize($champs_old), $paras_old, $champs); $onlylock = 're'; } } else { $id_version = 1; } $next = !$next ? 1 : ($next['id_fragment'] + 1); // Generer les nouveaux fragments $codes = array(); foreach ($champs as $nom => $texte) { $codes[$nom] = array(); $paras = separer_paras($texte, $paras); $paras_champ[$nom] = count($paras); } // Apparier les fragments de maniere optimale $n = count($paras); if ($n) { // Tables d'appariement dans les deux sens list(, $trans) = apparier_paras($paras_old, $paras); reset($champs); $nom = ''; // eviter une notice PHP au tout debut de la boucle // on ajoute ''=>0 en debut de tableau. $paras_champ = array($nom => 0) + $paras_champ; for ($i = 0; $i < $n; $i++) { while ($i >= $paras_champ[$nom]) { $nom = key($champs); next($champs); } // Lier au fragment existant si possible, sinon creer un nouveau fragment $id_fragment = isset($trans[$i]) ? $trans[$i] : $next++; $codes[$nom][] = $id_fragment; $fragments[$id_fragment] = $paras[$i]; } } foreach ($champs as $nom => $t) { $codes[$nom] = join(' ', $codes[$nom]); # avec la ligne qui suit, un champ qu'on vide ne s'enregistre pas # if (!strlen($codes[$nom])) unset($codes[$nom]); } // Enregistrer les modifications ajouter_fragments($id_objet, $objet, $id_version, $fragments); // Si l'insertion ne servait que de verrou, // la detruire apres mise a jour de l'ancienne entree, // sinon la mise a jour efface en fait le verrou. if (!$onlylock) { sql_updateq('spip_versions', array( 'id_version' => $id_version, 'date' => date('Y-m-d H:i:s'), 'champs' => serialize($codes), 'permanent' => $permanent, 'titre_version' => $titre_version ), "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet) . " AND id_version < 0 AND titre_version='$date'"); } else { sql_updateq('spip_versions', array( 'date' => date('Y-m-d H:i:s'), 'champs' => serialize($codes), 'permanent' => $permanent, 'titre_version' => $titre_version ), "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet) . " AND id_version=$id_version"); sql_delete("spip_versions", "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet) . " AND id_version < 0 AND titre_version ='$date'"); } spip_log($onlylock . "memorise la version $id_version de l'objet $objet $id_objet $titre_version", 'revisions'); return $id_version; } // les textes "diff" ne peuvent pas passer dans propre directement, // car ils contiennent des <span> et <div> parfois mal places // https://code.spip.net/@propre_diff function propre_diff($texte) { $span_diff = array(); if (preg_match_all(',<(/)?(span|div) (class|rem)="diff-[^>]*>,', $texte, $regs, PREG_SET_ORDER)) { $regs = array_slice($regs, 0, 500); #limiter la casse s'il y en a trop foreach ($regs as $c => $reg) { $texte = str_replace($reg[0], '@@@SPIP_DIFF' . $c . '@@@', $texte); } } // [ ...<span diff> -> lien ] // < tag <span diff> > $texte = preg_replace(',<([^>]*?@@@SPIP_DIFF[0-9]+@@@),', '<\1', $texte); # attention ici astuce seulement deux @@ finals car on doit eviter # deux patterns a suivre, afin de pouvoir prendre [ mais eviter [[ $texte = preg_replace(',(^|[^[])[[]([^[\]]*@@@SPIP_DIFF[0-9]+@@),', '\1[\2', $texte); // desactiver TeX & toujours-paragrapher $tex = $GLOBALS['traiter_math']; $GLOBALS['traiter_math'] = ''; $mem = $GLOBALS['toujours_paragrapher']; $GLOBALS['toujours_paragrapher'] = false; $texte = propre($texte); // retablir $GLOBALS['traiter_math'] = $tex; $GLOBALS['toujours_paragrapher'] = $mem; // un blockquote mal ferme peut gener l'affichage, et title plante safari $texte = preg_replace(',<(/?(blockquote|title)[^>]*)>,i', '<\1>', $texte); // Dans les <cadre> c'est un peu plus complique if (preg_match_all(',<textarea (.*)</textarea>,Uims', $texte, $area, PREG_SET_ORDER)) { foreach ($area as $reg) { $remplace = preg_replace(',@@@SPIP_DIFF[0-9]+@@@,', '**', $reg[0]); if ($remplace <> $reg[0]) { $texte = str_replace($reg[0], $remplace, $texte); } } } // replacer les valeurs des <span> et <div> diff- if (is_array($regs)) { foreach ($regs as $c => $reg) { $bal = (!$reg[1]) ? $reg[0] : "</$reg[2]>"; $texte = str_replace('@@@SPIP_DIFF' . $c . '@@@', $bal, $texte); $GLOBALS['les_notes'] = str_replace('@@@SPIP_DIFF' . $c . '@@@', $bal, $GLOBALS['les_notes']); } } // quand le dernier tag est ouvrant le refermer ... $reg = end($regs); if (!$reg[1] and $reg[2]) { $texte .= "</$reg[2]>"; } // et interdire_scripts ! $texte = interdire_scripts($texte); return $texte; } /** * Liste les champs versionnés d'une table objet. * * @param string $table * Nom complet de sa table sql. Exemple 'spip_articles' * @return array * Liste des champs versionnés */ function liste_champs_versionnes($table) { $liste_objets_versionnees = is_array(unserialize($GLOBALS['meta']['objets_versions'])) ? unserialize($GLOBALS['meta']['objets_versions']) : array(); if (!in_array($table, $liste_objets_versionnees)) { return array(); } include_spip('base/objets'); if ($infos = lister_tables_objets_sql($table) and isset($infos['champs_versionnes']) ) { return $infos['champs_versionnes']; } return array(); } /** * Lorsqu'un champ versionée est une jointure, récuperer tous les liens * et les mettre sous forme de liste énumérée * * @param string $objet * @param string $id_objet * @param string $jointure * @return string */ function recuperer_valeur_champ_jointure($objet, $id_objet, $jointure) { $objet_joint = objet_type($jointure); include_spip('action/editer_liens'); $v = array(); if (objet_associable($objet_joint)) { $liens = objet_trouver_liens(array($objet_joint => '*'), array($objet => $id_objet)); foreach ($liens as $l) { $v[] = $l[$objet_joint]; } } elseif (objet_associable($objet)) { $liens = objet_trouver_liens(array($objet => $id_objet), array($objet_joint => '*')); foreach ($liens as $l) { $v[] = $l[$objet]; } } sort($v); return implode(",", $v); } /** * Créer la première révision d'un objet si nécessaire * * À faire notamment si on vient d'activer l'extension et qu'on fait une modif * sur un objet qui était déjà en base, mais non versionné * * La fonction renvoie le numéro de la dernière version de l'objet, * et 0 si pas de version pour cet objet * * @param string $table * @param string $objet * @param int $id_objet * @param array $champs * @param int $id_auteur * @return int */ function verifier_premiere_revision($table, $objet, $id_objet, $champs = null, $id_auteur = 0) { $id_table_objet = id_table_objet($objet); if (!$champs) { $champs = liste_champs_versionnes($table); } if (!$champs) { return false; } if (!$id_version = sql_getfetsel('id_version', 'spip_versions', "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet), '', 'id_version DESC', '0,1') ) { // recuperer toutes les valeurs actuelles des champs // pour l'objet $originaux = sql_fetsel("*", $table, "$id_table_objet=" . intval($id_objet)); $premiere = false; $champs_originaux = array(); foreach ($champs as $v) { if (isset($originaux[$v])) { $champs_originaux[$v] = $originaux[$v]; } elseif (strncmp($v, 'jointure_', 9) == 0) { $champs_originaux[$v] = recuperer_valeur_champ_jointure($objet, $id_objet, substr($v, 9)); } if (isset($champs_originaux[$v]) and isset($originaux[$v]) and strlen($originaux[$v])) { $premiere = true; } } // Si un champ est non vide, // il faut creer une premiere revision if ($premiere) { $trouver_table = charger_fonction('trouver_table', 'base'); $desc = $trouver_table($table); // "trouver" une date raisonnable pour la version initiale $date_modif = ""; foreach (array('date_modif', 'maj') as $d) { if (!$date_modif and isset($originaux[$d]) and $t = strtotime($originaux[$d])) { $date_modif = date("Y-m-d H:i:s", $t - 20); } } if (!$date_modif and isset($desc['date']) and isset($originaux[$desc['date']]) ) { $date_modif = $originaux[$desc['date']]; } elseif (!$date_modif) { $date_modif = date("Y-m-d H:i:s", time() - 20); } if ($id_version = ajouter_version($id_objet, $objet, $champs_originaux, _T('revisions:version_initiale'), $id_auteur) ) { sql_updateq('spip_versions', array('date' => $date_modif), "id_objet=" . intval($id_objet) . " AND objet=" . sql_quote($objet) . " AND id_version=$id_version"); } } } return $id_version; } diff.php000064400000030275152526156350006206 0ustar00<?php /***************************************************************************\ * SPIP, Systeme de publication pour l'internet * * * * Copyright (c) 2001-2020 * * 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. * \***************************************************************************/ /** * Fonctions utilitaires du plugin révisions * * @package SPIP\Revisions\Diff **/ if (!defined("_ECRIRE_INC_VERSION")) { return; } // LCS (Longest Common Subsequence) en deux versions // (ref: http://web.archive.org/web/20071206224029/http://www2.toki.or.id/book/AlgDesignManual/BOOK/BOOK5/NODE208.HTM#SECTION03178000000000000000) /** * Calcule un LCS (Longest Common Subsequence) simplifié * * Chaque chaîne est une permutation de l'autre et on passe en paramètre * un des deux tableaux de correspondances * * @see lcs() * @param array $s * @return array **/ function lcs_opt($s) { $n = count($s); if (!$n) { return array(); } $paths = array(); $paths_ymin = array(); $max_len = 0; // Insertion des points asort($s); $max = 400; foreach ($s as $y => $c) { if ($max-- < 0) { break; } # eviter l'explosion memoire des tres gros diff for ($len = $max_len; $len > 0; $len--) { if ($paths_ymin[$len] < $y) { $paths_ymin[$len + 1] = $y; $paths[$len + 1] = $paths[$len]; $paths[$len + 1][$y] = $c; break; } } if ($len == 0) { $paths_ymin[1] = $y; $paths[1] = array($y => $c); } if ($len + 1 > $max_len) { $max_len = $len + 1; } } return $paths[$max_len]; } /** * Calcule un LCS (Longest Common Subsequence) * * Les deux chaînes n'ont pas été traitées au préalable par la fonction d'appariement * * @see lcs_opt() * @param array $s * @param array $t * @return array **/ function lcs($s, $t) { $n = count($s); $p = count($t); if (!$n || !$p) { return array(0 => array(), 1 => array()); } $paths = array(); $paths_ymin = array(); $max_len = 0; $s_pos = $t_pos = array(); // Insertion des points foreach ($t as $y => $c) { $t_pos[trim($c)][] = $y; } foreach ($s as $x => $c) { $c = trim($c); if (!isset($t_pos[$c])) { continue; } krsort($t_pos[$c]); foreach ($t_pos[$c] as $y) { for ($len = $max_len; $len > 0; $len--) { if ($paths_ymin[$len] < $y) { $paths_ymin[$len + 1] = $y; // On construit le resultat sous forme de chaine d'abord, // car les tableaux de PHP sont dispendieux en taille memoire $paths[$len + 1] = $paths[$len] . " $x,$y"; break; } } if ($len + 1 > $max_len) { $max_len = $len + 1; } if ($len == 0) { $paths_ymin[1] = $y; $paths[1] = "$x,$y"; } } } if (isset($paths[$max_len]) and $paths[$max_len]) { $path = explode(" ", $paths[$max_len]); $u = $v = array(); foreach ($path as $p) { list($x, $y) = explode(",", $p); $u[$x] = $y; $v[$y] = $x; } return array($u, $v); } return array(0 => array(), 1 => array()); } /** * Génération de diff a plusieurs étages * * @package SPIP\Revisions\Diff **/ class Diff { /** * Objet DiffX d'un texte ou partie de texte * * @var Object Objet Diff* (DiffTexte, DiffPara, DiffPhrase) */ public $diff; public $fuzzy; /** * Constructeur * * @param Object $diff Objet Diff* d'un texte ou morceau de texte **/ public function __construct($diff) { $this->diff = $diff; $this->fuzzy = true; } // https://code.spip.net/@comparer public function comparer($new, $old) { $paras = $this->diff->segmenter($new); $paras_old = $this->diff->segmenter($old); if ($this->diff->fuzzy()) { list($trans_rev, $trans) = apparier_paras($paras_old, $paras); $lcs = lcs_opt($trans); $lcs_rev = array_flip($lcs); } else { list($trans_rev, $trans) = lcs($paras_old, $paras); $lcs = $trans; $lcs_rev = $trans_rev; } reset($paras_old); reset($paras); reset($lcs); unset($i_old); $fin_old = false; foreach ($paras as $i => $p) { if (!isset($trans[$i])) { // Paragraphe ajoute $this->diff->ajouter($p); continue; } $j = $trans[$i]; if (!isset($lcs[$i])) { // Paragraphe deplace $this->diff->deplacer($p, $paras_old[$j]); continue; } if (!$fin_old) { // Paragraphes supprimes jusqu'au paragraphe courant if (!isset($i_old)) { $i_old = key($paras_old); $p_old = current($paras_old); next($paras_old); if (!$p_old) { $fin_old = true; } } while (!$fin_old && $i_old < $j) { if (!isset($trans_rev[$i_old])) { $this->diff->supprimer($p_old); } unset($i_old); $i_old = key($paras_old); $p_old = current($paras_old); next($paras_old); if (!$p_old) { $fin_old = true; } } } // Paragraphe n'ayant pas change de place $this->diff->comparer($p, $paras_old[$j]); } // Paragraphes supprimes a la fin du texte if (!$fin_old) { if (!isset($i_old)) { $i_old = key($paras_old); $p_old = current($paras_old); next($paras_old); if (!strlen($p_old)) { $fin_old = true; } } while (!$fin_old) { if (!isset($trans_rev[$i_old])) { $this->diff->supprimer($p_old); } $i_old = key($paras_old); $p_old = current($paras_old); next($paras_old); if (!$p_old) { $fin_old = true; } } } if (isset($i_old)) { if (!isset($trans_rev[$i_old])) { $this->diff->supprimer($p_old); } } return $this->diff->resultat(); } } /** * Génération de diff sur un Texte * * @package SPIP\Revisions\Diff **/ class DiffTexte { public $r; /** * Constructeur **/ public function __construct() { $this->r = ""; } // https://code.spip.net/@_diff public function _diff($p, $p_old) { $diff = new Diff(new DiffPara); return $diff->comparer($p, $p_old); } // https://code.spip.net/@fuzzy public function fuzzy() { return true; } /** * Découper les paragraphes d'un texte en fragments * * @param string $texte Texte à fragmenter * @return string[] Tableau de fragments (paragraphes) **/ public function segmenter($texte) { return separer_paras($texte); } // NB : rem=\"diff-\" est un signal pour la fonction "afficher_para_modifies" // https://code.spip.net/@ajouter public function ajouter($p) { $p = trim($p); $this->r .= "\n\n\n<span class=\"diff-para-ajoute\" title=\"" . _T('revisions:diff_para_ajoute') . "\">" . $p . "</span rem=\"diff-\">"; } // https://code.spip.net/@supprimer public function supprimer($p_old) { $p_old = trim($p_old); $this->r .= "\n\n\n<span class=\"diff-para-supprime\" title=\"" . _T('revisions:diff_para_supprime') . "\">" . $p_old . "</span rem=\"diff-\">"; } // https://code.spip.net/@deplacer public function deplacer($p, $p_old) { $this->r .= "\n\n\n<span class=\"diff-para-deplace\" title=\"" . _T('revisions:diff_para_deplace') . "\">"; $this->r .= trim($this->_diff($p, $p_old)); $this->r .= "</span rem=\"diff-\">"; } // https://code.spip.net/@comparer public function comparer($p, $p_old) { $this->r .= "\n\n\n" . $this->_diff($p, $p_old); } // https://code.spip.net/@resultat public function resultat() { return $this->r; } } /** * Génération de diff sur un paragraphe * * @package SPIP\Revisions\Diff **/ class DiffPara { public $r; /** Constructeur */ public function __construct() { $this->r = ""; } // https://code.spip.net/@_diff public function _diff($p, $p_old) { $diff = new Diff(new DiffPhrase); return $diff->comparer($p, $p_old); } // https://code.spip.net/@fuzzy public function fuzzy() { return true; } // https://code.spip.net/@segmenter public function segmenter($texte) { $paras = array(); $texte = trim($texte); while (preg_match('/[\.!\?\]]+\s*/u', $texte, $regs)) { $p = strpos($texte, $regs[0]) + strlen($regs[0]); $paras[] = substr($texte, 0, $p); $texte = substr($texte, $p); } if ($texte) { $paras[] = $texte; } return $paras; } // https://code.spip.net/@ajouter public function ajouter($p) { $this->r .= "<span class=\"diff-ajoute\" title=\"" . _T('revisions:diff_texte_ajoute') . "\">" . $p . "</span rem=\"diff-\">"; } // https://code.spip.net/@supprimer public function supprimer($p_old) { $this->r .= "<span class=\"diff-supprime\" title=\"" . _T('revisions:diff_texte_supprime') . "\">" . $p_old . "</span rem=\"diff-\">"; } // https://code.spip.net/@deplacer public function deplacer($p, $p_old) { $this->r .= "<span class=\"diff-deplace\" title=\"" . _T('revisions:diff_texte_deplace') . "\">" . $this->_diff($p, $p_old) . "</span rem=\"diff-\">"; } // https://code.spip.net/@comparer public function comparer($p, $p_old) { $this->r .= $this->_diff($p, $p_old); } // https://code.spip.net/@resultat public function resultat() { return $this->r; } } /** * Génération de diff sur une phrase * * @package SPIP\Revisions\Diff **/ class DiffPhrase { public $r; /** Constructeur */ public function __construct() { $this->r = ""; } // https://code.spip.net/@fuzzy public function fuzzy() { return false; } // https://code.spip.net/@segmenter public function segmenter($texte) { $paras = array(); if (test_pcre_unicode()) { $punct = '([[:punct:]]|' . plage_punct_unicode() . ')'; $mode = 'u'; } else { // Plages de poncutation pour preg_match bugge (ha ha) $punct = '([^\w\s\x80-\xFF]|' . plage_punct_unicode() . ')'; $mode = ''; } $preg = '/(' . $punct . '+)(\s+|$)|(\s+)(' . $punct . '*)/' . $mode; while (preg_match($preg, $texte, $regs)) { $p = strpos($texte, $regs[0]); $l = strlen($regs[0]); $punct = $regs[1] ? $regs[1] : $regs[6]; $milieu = ""; if ($punct) { // notes if ($punct == '[[') { $avant = substr($texte, 0, $p) . $regs[5] . $punct; $texte = $regs[4] . substr($texte, $p + $l); } else { if ($punct == ']]') { $avant = substr($texte, 0, $p) . $regs[5] . $punct; $texte = substr($texte, $p + $l); } // Attacher les raccourcis fermants au mot precedent else { if (preg_match(',^[\]}]+$,', $punct)) { $avant = substr($texte, 0, $p) . (isset($regs[5]) ? $regs[5] : '') . $punct; $texte = $regs[4] . substr($texte, $p + $l); } // Attacher les raccourcis ouvrants au mot suivant else { if (isset($regs[5]) && $regs[5] && preg_match(',^[\[{]+$,', $punct)) { $avant = substr($texte, 0, $p) . $regs[5]; $texte = $punct . substr($texte, $p + $l); } // Les autres signes de ponctuation sont des mots a part entiere else { $avant = substr($texte, 0, $p); $milieu = $regs[0]; $texte = substr($texte, $p + $l); } } } } } else { $avant = substr($texte, 0, $p + $l); $texte = substr($texte, $p + $l); } if ($avant) { $paras[] = $avant; } if ($milieu) { $paras[] = $milieu; } } if ($texte) { $paras[] = $texte; } return $paras; } // https://code.spip.net/@ajouter public function ajouter($p) { $this->r .= "<span class=\"diff-ajoute\" title=\"" . _T('revisions:diff_texte_ajoute') . "\">" . $p . "</span rem=\"diff-\"> "; } // https://code.spip.net/@supprimer public function supprimer($p_old) { $this->r .= "<span class=\"diff-supprime\" title=\"" . _T('revisions:diff_texte_supprime') . "\">" . $p_old . "</span rem=\"diff-\"> "; } // https://code.spip.net/@comparer public function comparer($p, $p_old) { $this->r .= $p; } // https://code.spip.net/@resultat public function resultat() { return $this->r; } } // https://code.spip.net/@preparer_diff function preparer_diff($texte) { include_spip('inc/charsets'); $charset = $GLOBALS['meta']['charset']; if ($charset == 'utf-8') { return unicode_to_utf_8(html2unicode($texte)); } return unicode_to_utf_8(html2unicode(charset2unicode($texte, $charset, true))); } // https://code.spip.net/@afficher_diff function afficher_diff($texte) { $charset = $GLOBALS['meta']['charset']; if ($charset == 'utf-8') { return $texte; } return charset2unicode($texte, 'utf-8'); }
/home/checkyd/www/ecrire/../plugins/../7b5d0/inc.tar