| Current Path : /home/checkyd/www/7b5d0/ |
| Current File : /home/checkyd/www/7b5d0/inc.tar |
date_gestion.php 0000644 00000002465 15252511300 0007723 0 ustar 00 <?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.php 0000644 00000010661 15252511300 0007767 0 ustar 00 <?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.php 0000644 00000015101 15252511300 0007770 0 ustar 00 <?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.php 0000644 00000007545 15252511300 0010020 0 ustar 00 <?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.php 0000644 00000003220 15252541030 0007565 0 ustar 00 <?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.php 0000644 00000004135 15252541030 0011617 0 ustar 00 <?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.php 0000644 00000003661 15252541030 0010610 0 ustar 00 <?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.php 0000644 00000005305 15252541030 0012715 0 ustar 00 <?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.php 0000644 00000012211 15252541030 0011306 0 ustar 00 <?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.php 0000644 00000002323 15252541030 0013674 0 ustar 00 <?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.php 0000644 00000016746 15252541030 0007271 0 ustar 00 <?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.php 0000644 00000002315 15252541030 0013132 0 ustar 00 <?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.php 0000644 00000026016 15252541030 0010607 0 ustar 00 <?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.php 0000644 00000004373 15252541030 0007106 0 ustar 00 <?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.php 0000644 00000013117 15252541030 0011463 0 ustar 00 <?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.php 0000644 00000005145 15252615635 0011412 0 ustar 00 <?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.php 0000644 00000014575 15252615635 0010372 0 ustar 00 <?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.php 0000644 00000031215 15252615635 0011177 0 ustar 00 <?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.php 0000644 00000071776 15252615635 0007332 0 ustar 00 <?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.php 0000644 00000030275 15252615635 0006206 0 ustar 00 <?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');
}