Compare commits

...

No commits in common. 'master' and 'spip-3.2' have entirely different histories.

  1. 7
      .gitignore
  2. 18
      CHANGELOG.md
  3. 3
      README.md
  4. 18
      composer.json
  5. 98
      engine/textwheel.php
  6. 2
      engine/textwheelrule.php
  7. 70
      engine/textwheelruleset.php
  8. 32
      inc/autoliens.php
  9. 463
      inc/lien.php
  10. 80
      inc/notes.php
  11. 13
      inc/ressource.php
  12. 328
      inc/texte.php
  13. 88
      inc/textwheel.php
  14. 20
      inc/yaml-mini.php
  15. 17
      lang/paquet-tw.xml
  16. 15
      lang/paquet-tw_br.php
  17. 15
      lang/paquet-tw_eo.php
  18. 2
      lang/paquet-tw_fr.php
  19. 11
      lang/tw.xml
  20. 2
      lang/tw_fr.php
  21. 147
      lib/yaml/sfYaml.php
  22. 62
      lib/yaml/sfYamlDumper.php
  23. 426
      lib/yaml/sfYamlInline.php
  24. 542
      lib/yaml/sfYamlParser.php
  25. 6
      paquet.xml
  26. 15
      phpcs.xml.dist
  27. 422
      phpstan-baseline.neon
  28. 12
      phpstan.neon.dist
  29. 62
      tests/AutoliensTest.php
  30. 256
      tests/TextwheelNettoyerraccourcistypoTest.php
  31. 156
      tests/TextwheelPropreTest.php
  32. 99
      tests/TextwheelPropreTypoTest.php
  33. 20
      tests/data/.editorconfig
  34. 6
      tests/data/base/atx_heading.html
  35. 6
      tests/data/base/atx_heading.txt
  36. 8
      tests/data/base/backtick_block.html
  37. 9
      tests/data/base/backtick_block.txt
  38. 6
      tests/data/base/backtick_md_span.html
  39. 11
      tests/data/base/backtick_md_span.txt
  40. 5
      tests/data/base/backtick_span.html
  41. 9
      tests/data/base/backtick_span.txt
  42. 2
      tests/data/base/cadre_block.html
  43. 8
      tests/data/base/code_block.html
  44. 10
      tests/data/base/code_span.html
  45. 4
      tests/data/base/compound_emphasis.html
  46. 2
      tests/data/base/email.html
  47. 4
      tests/data/base/escaping.html
  48. 6
      tests/data/base/fenced_code_block.html
  49. 2
      tests/data/base/image_reference.html
  50. 6
      tests/data/base/inline_link.html
  51. 20
      tests/data/base/inline_link_title.html
  52. 0
      tests/data/base/math.html
  53. 0
      tests/data/base/math.txt
  54. 2
      tests/data/base/nested_block-level_html.html
  55. 22
      tests/data/base/notes.html
  56. 8
      tests/data/base/special_characters.html
  57. 4
      tests/data/base/unordered_list.html
  58. 6
      tests/data/modeles_block/atx_heading.html
  59. 6
      tests/data/modeles_block/atx_heading.txt
  60. 2
      tests/data/modeles_block/cadre_block.html
  61. 8
      tests/data/modeles_block/code_block.html
  62. 10
      tests/data/modeles_block/code_span.html
  63. 4
      tests/data/modeles_block/compound_emphasis.html
  64. 2
      tests/data/modeles_block/email.html
  65. 2
      tests/data/modeles_block/escaping.html
  66. 6
      tests/data/modeles_block/fenced_code_block.html
  67. 2
      tests/data/modeles_block/image_reference.html
  68. 10
      tests/data/modeles_block/inline_link.html
  69. 14
      tests/data/modeles_block/inline_link_title.html
  70. 2
      tests/data/modeles_block/nested_block-level_html.html
  71. 22
      tests/data/modeles_block/notes.html
  72. 4
      tests/data/modeles_block/unordered_list.html
  73. 6
      tests/data/modeles_inline/atx_heading.html
  74. 6
      tests/data/modeles_inline/atx_heading.txt
  75. 2
      tests/data/modeles_inline/cadre_block.html
  76. 8
      tests/data/modeles_inline/code_block.html
  77. 10
      tests/data/modeles_inline/code_span.html
  78. 4
      tests/data/modeles_inline/compound_emphasis.html
  79. 2
      tests/data/modeles_inline/email.html
  80. 2
      tests/data/modeles_inline/escaping.html
  81. 6
      tests/data/modeles_inline/fenced_code_block.html
  82. 2
      tests/data/modeles_inline/image_reference.html
  83. 12
      tests/data/modeles_inline/inline_link.html
  84. 20
      tests/data/modeles_inline/inline_link_title.html
  85. 2
      tests/data/modeles_inline/nested_block-level_html.html
  86. 22
      tests/data/modeles_inline/notes.html
  87. 4
      tests/data/modeles_inline/unordered_list.html
  88. 6
      tests/data/typo/atx_heading.html
  89. 6
      tests/data/typo/atx_heading.txt
  90. 8
      tests/data/typo/backtick_block.html
  91. 9
      tests/data/typo/backtick_block.txt
  92. 8
      tests/data/typo/backtick_block_php.html
  93. 9
      tests/data/typo/backtick_block_php.txt
  94. 6
      tests/data/typo/backtick_md_span.html
  95. 11
      tests/data/typo/backtick_md_span.txt
  96. 5
      tests/data/typo/backtick_span.html
  97. 9
      tests/data/typo/backtick_span.txt
  98. 2
      tests/data/typo/cadre_block.html
  99. 8
      tests/data/typo/code_block.html
  100. 10
      tests/data/typo/code_span.html
  101. Some files were not shown because too many files have changed in this diff Show More

7
.gitignore vendored

@ -1,7 +0,0 @@
/vendor/
/composer.phar
/composer.lock
/phpcs.xml
/phpstan.neon
/.php_cs.cache
/.php_cs.txt

18
CHANGELOG.md

@ -1,18 +0,0 @@
# Changelog
## [Unreleased]
### Added
- #4843 Support des backticks pour insérer du code dans le contenu éditorial
- Fichier `README.md`
### Changed
- spip/spip#5271 Refactoring de la mise en sécurité des textes
- Conversion des tests unitaires en PHPUnit
- Compatible SPIP 4.2.0-dev
### Removed
- #4836 `create_replace` n'est plus un type de règle supporté

3
README.md

@ -1,3 +0,0 @@
# Plugin Textwheel
Gestion des raccoucris typographiques de SPIP

18
composer.json

@ -1,18 +0,0 @@
{
"require": {
"php": "^7.4 || ^8.0"
},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.1",
"phpstan/phpstan": "^1.4",
"spip/coding-standards": "^1.2"
},
"config": {
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true
},
"platform": {
"php": "7.4.27"
}
}
}

98
engine/textwheel.php

@ -22,15 +22,15 @@ if (!defined('_ECRIRE_INC_VERSION')) {
return;
}
require_once __DIR__ . '/textwheelruleset.php';
require_once dirname(__FILE__) . "/textwheelruleset.php";
class TextWheel {
protected $ruleset;
protected static $subwheel = [];
protected static $subwheel = array();
// Experimental : projet de compilation PHP d'une wheel
// pour generation d'un fichier php execute a la place de ->text()
protected $compiled = [];
protected $compiled = array();
/**
* Constructor
@ -62,15 +62,16 @@ class TextWheel {
public function text($t) {
$rules = &$this->ruleset->getRules();
## apply each in order
foreach ($rules as $name => $rule) { #php4+php5
$this->apply($rules[$name], $t);
foreach ($rules as $name => $rule) #php4+php5
{
$this->apply($rules[$name], $t);
}
#foreach ($this->rules as &$rule) #smarter &reference, but php5 only
# $this->apply($rule, $t);
return $t;
}
private static function export($x) {
private function export($x) {
return addcslashes(var_export($x, true), "\n\r\t");
}
@ -78,21 +79,20 @@ class TextWheel {
$rules = &$this->ruleset->getRules();
## apply each in order
$pre = [];
$comp = [];
$pre = array();
$comp = array();
foreach ($rules as $name => $rule) {
$rule->name = $name;
$this->initRule($rule);
if (
$rule->replace
if ($rule->replace
and $compiledEntry = $this->ruleCompiledEntryName($rule->replace)
and isset($this->compiled[$compiledEntry])
and $fun = $this->compiled[$compiledEntry]
) {
$pre[] = "\n###\n## $name\n###\n" . $fun;
preg_match(',function (\w+), ', $fun, $r);
$rule->compilereplace = "'" . $r[1] . "'"; # ne pas modifier ->replace sinon on casse l'execution...
$rule->compilereplace = "'".$r[1]."'"; # ne pas modifier ->replace sinon on casse l'execution...
}
$r = "\t/* $name */\n";
@ -215,22 +215,30 @@ class TextWheel {
}
if ($rule->create_replace) {
// FORBIDDEN : rule->create_replace n'est plus supportée
$rule->disabled = true;
throw new Exception('Les règles de type create_replace ne sont plus autorisée (Règle '.($rule->name ?? '' . ' / ' . $rule->replace).')');
// DEPRECATED : rule->create_replace, on ne peut rien faire de mieux ici
// mais c'est voue a disparaitre
$compile = $rule->replace . '($t)';
$rule->replace = create_function('$m', $rule->replace);
$this->compiled[$this->ruleCompiledEntryName($rule->replace)] = $compile;
$rule->create_replace = false;
$rule->is_callback = true;
}
elseif ($rule->is_wheel) {
$rule_number = count(TextWheel::$subwheel);
TextWheel::$subwheel[] = $this->createSubWheel($rule->replace);
$cname = 'compiled_' . str_replace('-', '_', $rule->name ?? '') . '_' . substr(md5(spl_object_hash($rule)), 0, 7);
$cname = 'compiled_' . str_replace('-', '_', $rule->name) . '_' . substr(md5(spl_object_hash($rule)),0,7);
if ($rule->type == 'all' or $rule->type == 'str' or $rule->type == 'split' or !isset($rule->match)) {
$rule->replace = fn($m) => TextWheel::getSubWheel($rule_number)->text($m);
$rule->replace = function ($m) use ($rule_number) {
return TextWheel::getSubWheel($rule_number)->text($m);
};
$rule->compilereplace = "'$cname'";
}
else {
$pick_match = intval($rule->pick_match);
$rule->replace = fn($m) => TextWheel::getSubWheel($rule_number)->text($m[$pick_match]);
$rule->compilereplace = 'function ($m) { return ' . $cname . '($m[' . $pick_match . ']) }';
$rule->replace = function ($m) use ($rule_number, $pick_match) {
return TextWheel::getSubWheel($rule_number)->text($m[$pick_match]);
};
$rule->compilereplace = 'function ($m) { return '.$cname.'($m['.$pick_match.']) }';
}
$rule->is_wheel = false;
$rule->is_callback = true;
@ -248,8 +256,7 @@ class TextWheel {
case 'str':
$rule->func_replace = 'replace_str';
// test if quicker strtr usable
if (
!$rule->is_callback
if (!$rule->is_callback
and is_array($rule->match) and is_array($rule->replace)
and $c = array_map('strlen', $rule->match)
and $c = array_unique($c)
@ -267,7 +274,7 @@ class TextWheel {
break;
case 'split':
$rule->func_replace = 'replace_split';
$rule->match = [$rule->match, is_null($rule->glue) ? $rule->match : $rule->glue];
$rule->match = array($rule->match, is_null($rule->glue) ? $rule->match : $rule->glue);
break;
case 'preg':
default:
@ -278,7 +285,7 @@ class TextWheel {
$rule->func_replace .= '_cb';
}
}
if (!method_exists('TextWheel', $rule->func_replace)) {
if (!method_exists("TextWheel", $rule->func_replace)) {
$rule->disabled = true;
$rule->func_replace = 'replace_identity';
}
@ -311,11 +318,11 @@ class TextWheel {
$this->initRule($rule);
}
if (isset($rule->if_str) and strpos($t, (string) $rule->if_str) === false) {
if (isset($rule->if_str) and strpos($t, $rule->if_str) === false) {
return;
}
if (isset($rule->if_stri) and stripos($t, (string) $rule->if_stri) === false) {
if (isset($rule->if_stri) and stripos($t, $rule->if_stri) === false) {
return;
}
@ -402,7 +409,7 @@ class TextWheel {
* @param int $count
*/
protected static function replace_str_cb(&$match, &$replace, &$t, &$count) {
if (strpos($t, (string) $match) !== false) {
if (strpos($t, $match) !== false) {
if (count($b = explode($match, $t)) > 1) {
$t = join($replace($match), $b);
}
@ -505,11 +512,11 @@ class TextWheelDebug extends TextWheel {
if ($p < 1000) {
$s = '';
} else {
$s = sprintf('%d ', $x = floor($p / 1000));
$s = sprintf("%d ", $x = floor($p / 1000));
$p -= ($x * 1000);
}
return $s . sprintf('%.3f ms', $p);
return $s . sprintf("%.3f ms", $p);
}
}
@ -522,10 +529,11 @@ class TextWheelDebug extends TextWheel {
public function text($t) {
$rules = &$this->ruleset->getRules();
## apply each in order
foreach ($rules as $name => $rule) { #php4+php5
if (is_int($name)) {
foreach ($rules as $name => $rule) #php4+php5
{
if (is_int($name)) {
$name .= ' ' . $rule->match;
}
}
$this->timer($name);
$b = $t;
$this->apply($rule, $t);
@ -538,6 +546,7 @@ class TextWheelDebug extends TextWheel {
} else {
TextWheelDebug::$tnu[$name] += $v;
}
}
#foreach ($this->rules as &$rule) #smarter &reference, but php5 only
# $this->apply($rule, $t);
@ -573,19 +582,15 @@ class TextWheelDebug extends TextWheel {
$total += $t;
if (intval($t * 10)) {
echo "<tr>
<td class='number strong'>" . number_format(round($t * 10) / 10, 1) . '</td><td> ' . spip_htmlspecialchars($r) . '</td>
<td'
. (!$applications ? " class='zero'" : '')
. '>' . $applications . '/' . intval(TextWheelDebug::$w[$r]) . "</td>
<td class='number'>" . ($applications ? number_format(
round(TextWheelDebug::$tu[$r] / $applications * 100) / 100,
2
) : '') . "</td>
<td class='number'>" . (($nu = intval(TextWheelDebug::$w[$r]) - $applications) ? number_format(
round(TextWheelDebug::$tnu[$r] / $nu * 100) / 100,
2
) : '') . '</td>
</tr>';
<td class='number strong'>" . number_format(round($t * 10) / 10, 1) . "</td><td> " . spip_htmlspecialchars($r) . "</td>
<td"
. (!$applications ? " class='zero'" : "")
. ">" . $applications . "/" . intval(TextWheelDebug::$w[$r]) . "</td>
<td class='number'>" . ($applications ? number_format(round(TextWheelDebug::$tu[$r] / $applications * 100) / 100,
2) : "") . "</td>
<td class='number'>" . (($nu = intval(TextWheelDebug::$w[$r]) - $applications) ? number_format(round(TextWheelDebug::$tnu[$r] / $nu * 100) / 100,
2) : "") . "</td>
</tr>";
}
}
echo "</table>\n";
@ -596,9 +601,9 @@ class TextWheelDebug extends TextWheel {
<thead><tr><th>temps</th><th>rule</th></tr></thead>\n";
ksort($GLOBALS['totaux']);
TextWheelDebug::outputTotal($GLOBALS['totaux']);
echo '</table>';
echo "</table>";
# somme des temps des rules, ne tient pas compte des subwheels
echo '<p>temps total rules: ' . round($total) . "&nbsp;ms</p>\n";
echo "<p>temps total rules: " . round($total) . "&nbsp;ms</p>\n";
echo "</div>\n";
}
}
@ -626,6 +631,7 @@ class TextWheelDebug extends TextWheel {
protected function &createSubWheel(&$rules) {
return new TextWheelDebug($rules);
}
}
@ -634,7 +640,7 @@ class TextWheelDebug extends TextWheel {
*/
if (!function_exists('stripos')) {
function stripos($haystack, $needle) {
return strpos($haystack, (string) stristr($haystack, (string) $needle));
return strpos($haystack, stristr($haystack, $needle));
}
}

2
engine/textwheelrule.php

@ -23,6 +23,7 @@ if (!defined('_ECRIRE_INC_VERSION')) {
}
class TextWheelRule {
## rule description
# optional
public $priority = 0; # rule priority (rules are applied in ascending order)
@ -102,4 +103,5 @@ class TextWheelRule {
}
}
}
}

70
engine/textwheelruleset.php

@ -22,17 +22,11 @@ if (!defined('_ECRIRE_INC_VERSION')) {
return;
}
// Pour choisir le JSON ou le YAML
if (!defined('_WHEELS_FORMAT_DEFAUT')) {
define('_WHEELS_FORMAT_DEFAUT', 'json');
}
require_once __DIR__ . '/textwheelrule.php';
require_once dirname(__FILE__) . "/textwheelrule.php";
abstract class TextWheelDataSet {
# list of data
protected $data = [];
protected $data = array();
/**
* file finder : can be overloaded in order to use application dependant
@ -57,7 +51,7 @@ abstract class TextWheelDataSet {
// textwheel default path ?
if (!$default_path) {
$default_path = __DIR__ . '/../wheels/';
$default_path = dirname(__FILE__) . '/../wheels/';
}
if (file_exists($f = $default_path . $file)) {
return $f;
@ -67,60 +61,46 @@ abstract class TextWheelDataSet {
}
/**
* Load a YAML/JSON file describing data
* Load a yaml file describing data
*
* @param string $file
* @param string $default_path
* @return array
*/
protected function loadFile(&$file, $default_path = '') {
if (!preg_match(',[.](yaml|json)$,i', $file, $matches)) {
// Le fichier est fourni sans son extension, on essaie avec le json puis le yaml sinon.
$formats = (_WHEELS_FORMAT_DEFAUT === 'json') ? ['json', 'yaml'] : ['yaml', 'json'];
$name = $file;
foreach ($formats as $format) {
$file = $name . '.' . $format;
if ($file = $this->findFile($file, $default_path)) {
break;
}
}
} else {
$format = $matches[1];
}
if (
!$file
or (!$file = $this->findFile($file, $default_path))
if (!preg_match(',[.]yaml$,i', $file)
// external rules
or !$file = $this->findFile($file, $default_path)
) {
return [];
return array();
}
defined('_YAML_EVAL_PHP') || define('_YAML_EVAL_PHP', false);
if ($format == 'json') {
$dataset = json_decode(file_get_contents($file), true, 512, JSON_THROW_ON_ERROR);
} elseif (defined('_DIR_PLUGIN_YAML')) {
include_spip('inc/yaml');
$dataset = yaml_decode(file_get_contents($file));
} else {
$dataset = [];
if (!function_exists('yaml_decode')) {
if (function_exists('include_spip')) {
include_spip('inc/yaml-mini');
} else {
require_once dirname(__FILE__) . '/../inc/yaml.php';
}
}
$dataset = yaml_decode(file_get_contents($file));
if (is_null($dataset)) {
$dataset = [];
$dataset = array();
}
# throw new DomainException('rule file is empty, unreadable or badly formed: '.$file.var_export($dataset,true));
# throw new DomainException('yaml file is empty, unreadable or badly formed: '.$file.var_export($dataset,true));
// if a php file with same name exists
// include it as it contains callback functions
if (
$f = preg_replace(',[.](yaml|json)$,i', '.php', $file)
if ($f = preg_replace(',[.]yaml$,i', '.php', $file)
and file_exists($f)
) {
$dataset[] = ['require' => $f, 'priority' => -1000];
$dataset[] = array('require' => $f, 'priority' => -1000);
}
return $dataset;
}
}
class TextWheelRuleSet extends TextWheelDataSet {
@ -133,7 +113,7 @@ class TextWheelRuleSet extends TextWheelDataSet {
* @param array|string $ruleset
* @param string $filepath
*/
public function __construct($ruleset = [], $filepath = '') {
public function __construct($ruleset = array(), $filepath = '') {
if ($ruleset) {
$this->addRules($ruleset, $filepath);
}
@ -146,7 +126,7 @@ class TextWheelRuleSet extends TextWheelDataSet {
* @param array $ruleset
* @param string $callback
* @param string $class
* @return TextWheelRuleSet
* @return class
*/
public static function &loader($ruleset, $callback = '', $class = 'TextWheelRuleSet') {
@ -218,7 +198,7 @@ class TextWheelRuleSet extends TextWheelDataSet {
return;
}
// rules can be a string : yaml or json filename
// rules can be a string : yaml filename
if (is_string($rules)) {
$file = $rules; // keep the real filename
$rules = $this->loadFile($file, $filepath);
@ -251,14 +231,14 @@ class TextWheelRuleSet extends TextWheelDataSet {
*/
protected function sort() {
if (!$this->sorted) {
$rulz = [];
$rulz = array();
foreach ($this->data as $index => $rule) {
if (!$rule->disabled) {
$rulz[intval($rule->priority)][$index] = $rule;
}
}
ksort($rulz);
$this->data = [];
$this->data = array();
foreach ($rulz as $rules) {
$this->data += $rules;
}

32
inc/autoliens.php

@ -1,22 +1,30 @@
<?php
// Les URLs brutes sont converties en <a href='url'>url</a>
// https://code.spip.net/@traiter_raccourci_liens
function tw_autoliens($t) {
defined('_EXTRAIRE_LIENS') || define('_EXTRAIRE_LIENS', ',' . '\[[^\[\]]*(?:<-|->).*?\]' . '|<a\b.*?</a\b' . '|<\w.*?>' . '|((?:https?:/|www\.)[^"\'\s\[\]\}\)<>]*)' . ',imsS');
$t = preg_replace_callback(_EXTRAIRE_LIENS, 'tw_traiter_autoliens', $t);
// echapper les autoliens eventuellement inseres (en une seule fois)
if (strpos($t, "<html>") !== false) {
$t = echappe_html($t);
}
return $t;
}
// callback pour la fonction autoliens()
// https://code.spip.net/@autoliens_callback
function tw_traiter_autoliens($r) {
if ((is_countable($r) ? count($r) : 0) < 2) {
if (count($r) < 2) {
return reset($r);
}
[$tout, $l] = $r;
list($tout, $l) = $r;
if (!$l) {
return $tout;
}
@ -35,18 +43,14 @@ function tw_traiter_autoliens($r) {
// en particulier le "|" quand elles sont dans un tableau a la SPIP
preg_match('/^(.*?)([,.;?|]?)$/', $l, $k);
$url = $protocol . '://' . $k[1];
// si l'original ne contenait pas le 'http:' on le supprime du clic
$url_echap = echappe_html('<html>' . ($m ? $url : substr($url, strlen('http://'))) . '</html>');
$class = 'spip_url';
if (lien_is_url_externe($url)) {
$class .= ' spip_out';
}
$class .= ' auto';
$lien = charger_fonction('lien', 'inc');
$r = $lien($url, $url_echap, $class, '', '', 'nofollow') . $k[2];
// deux fois <html> car inc_lien echappe un coup et restaure ensuite
// => un perd 1 <html>
$r = $lien($url, "<html><html>$url</html></html>", '', '', '', 'nofollow') . $k[2];
return $r;
// ajouter la class auto
$r = inserer_attribut($r, 'class', trim(extraire_attribut($r, 'class') . ' auto'));
// si l'original ne contenait pas le 'http:' on le supprime du clic
return ($m ? $r : str_replace('>http://', '>', $r));
}

463
inc/lien.php

@ -1,13 +1,13 @@
<?php
/***************************************************************************\
* SPIP, Système de publication pour l'internet *
* SPIP, Systeme de publication pour l'internet *
* *
* Copyright © avec tendresse depuis 2001 *
* Arnaud Martin, Antoine Pitrou, Philippe Rivière, Emmanuel Saint-James *
* Copyright (c) 2001-2020 *
* Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
* *
* Ce programme est un logiciel libre distribué sous licence GNU/GPL. *
* Pour plus de détails voir le fichier COPYING.txt ou l'aide en ligne. *
* 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')) {
@ -15,8 +15,6 @@ if (!defined('_ECRIRE_INC_VERSION')) {
}
include_spip('base/abstract_sql');
include_spip('inc/modeles');
include_spip('inc/liens');
/**
* Production de la balise a+href à partir des raccourcis `[xxx->url]` etc.
@ -44,9 +42,8 @@ function inc_lien_dist(
$hlang = '',
$rel = '',
$connect = '',
$env = []
$env = array()
) {
$mime = null;
static $u = null;
if (!$u) {
$u = url_de_base();
@ -62,7 +59,7 @@ function inc_lien_dist(
$hlang
and $match = typer_raccourci($lien)
) {
[$type, , $id, , $args, , $ancre] = array_pad($match, 7, null);
@list($type, , $id, , $args, , $ancre) = $match;
$trouver_table = charger_fonction('trouver_table', 'base');
$desc = $trouver_table(table_objet($type, $connect), $connect);
if (
@ -74,11 +71,8 @@ function inc_lien_dist(
$row = sql_fetsel('*', $table_objet_sql, "$id_table_objet=" . intval($id))
and isset($row['id_trad'])
and isset($row['lang'])
and $id_dest = sql_getfetsel(
$id_table_objet,
$table_objet_sql,
'id_trad=' . intval($row['id_trad']) . ' AND lang=' . sql_quote($hlang)
)
and $id_dest = sql_getfetsel($id_table_objet, $table_objet_sql,
"id_trad=" . intval($row['id_trad']) . " AND lang=" . sql_quote($hlang))
and objet_test_si_publie($type, $id_dest)
) {
$lien = "$type$id_dest";
@ -98,30 +92,36 @@ function inc_lien_dist(
if (!$class and isset($lien['class'])) {
$class = $lien['class'];
}
$lang = $lien['lang'] ?? '';
$mime = isset($lien['mime']) ? " type='" . $lien['mime'] . "'" : '';
$lang = isset($lien['lang']) ? $lien['lang'] : '';
$mime = isset($lien['mime']) ? " type='" . $lien['mime'] . "'" : "";
$lien = $lien['url'];
}
$lien = trim($lien);
if (strncmp($lien, '#', 1) == 0) { # ancres pures (internes a la page)
if (strncmp($lien, "#", 1) == 0) { # ancres pures (internes a la page)
$class = 'spip_ancre';
} elseif (strncasecmp($lien, 'mailto:', 7) == 0) { # pseudo URL de mail
$class = 'spip_mail';
$class = "spip_mail";
} elseif (strncmp($texte, '<html>', 6) == 0) { # cf traiter_lien_explicite
$class = 'spip_url';
$class = "spip_url";
# spip_out sur les URLs externes
if (lien_is_url_externe($lien)) {
$class .= ' spip_out';
if (
preg_match(',^\w+://,iS', $lien)
and strncasecmp($lien, url_de_base(), strlen(url_de_base()))
) {
$class .= " spip_out";
}
} elseif (!$class) {
# spip_out sur les URLs externes
if (lien_is_url_externe($lien)) {
$class = 'spip_out';
if (
preg_match(',^\w+://,iS', $lien)
and strncasecmp($lien, url_de_base(), strlen(url_de_base()))
) {
$class = "spip_out"; # si pas spip_in|spip_glossaire
}
}
if ($class) {
$class = " class=\"$class\"";
$class = " class='$class'";
}
// Si l'objet n'est pas de la langue courante, on ajoute hreflang
@ -129,7 +129,7 @@ function inc_lien_dist(
$hlang = $lang;
}
$lang = ($hlang ? " hreflang=\"$hlang\"" : '');
$lang = ($hlang ? " hreflang='$hlang'" : '');
if ($title) {
$title = ' title="' . attribut_html($title) . '"';
@ -145,27 +145,21 @@ function inc_lien_dist(
$rel = trim("$rel external");
}
if ($rel) {
$rel = " rel=\"$rel\"";
$rel = " rel='$rel'";
}
$lang_objet_prev = '';
if ($hlang and $hlang !== $GLOBALS['spip_lang']) {
$lang_objet_prev = $GLOBALS['lang_objet'] ?? null;
$lang_objet_prev = isset($GLOBALS['lang_objet']) ? $GLOBALS['lang_objet'] : null;
$GLOBALS['lang_objet'] = $hlang;
}
// si pas de modele dans le texte du lien, on peut juste passer typo sur le texte, c'est plus rapide
// les rares cas de lien qui encapsule un modele passe en dessous, c'est plus lent
include_spip("src/Texte/Collecteur/AbstractCollecteur");
include_spip("src/Texte/Collecteur/Modeles");
$collecteurModeles = new Spip\Texte\Collecteur\Modeles();
if (!$collecteurModeles->detecter($texte)) {
if (traiter_modeles($texte, false, '', $connect, null, $env) == $texte) {
$texte = typo($texte, true, $connect, $env);
$lien = '<a href="' . str_replace(
'"',
'&quot;',
$lien
) . "\"$class$lang$title$rel" . ($mime ?? '') . ">$texte</a>";
$lien = "<a href=\"" . str_replace('"', '&quot;',
$lien) . "\"$class$lang$title$rel" . (isset($mime) ? $mime : '') . ">$texte</a>";
if ($lang_objet_prev !== '') {
if ($lang_objet_prev) {
$GLOBALS['lang_objet'] = $lang_objet_prev;
@ -181,7 +175,7 @@ function inc_lien_dist(
# Attention, le texte initial est deja echappe mais pas forcement
# celui retourne par calculer_url.
# Penser au cas [<imgXX|right>->URL], qui exige typo('<a>...</a>')
$lien = '<a href="' . str_replace('"', '&quot;', $lien) . "\"$class$lang$title$rel$mime>$texte</a>";
$lien = "<a href=\"" . str_replace('"', '&quot;', $lien) . "\"$class$lang$title$rel$mime>$texte</a>";
#$res = typo($lien, true, $connect, $env);
$p = $GLOBALS['toujours_paragrapher'];
$GLOBALS['toujours_paragrapher'] = false;
@ -201,22 +195,6 @@ function inc_lien_dist(
return $res;
}
/**
* Detecter qu'une URL est externe pour poser une class en fonction
* @param string $url_lien
* @return bool
*/
function lien_is_url_externe($url_lien) {
if (
preg_match(',^\w+://,iS', $url_lien)
and strncasecmp($url_lien, url_de_base(), strlen(url_de_base()))
) {
return true;
}
return false;
}
/**
* Générer le HTML d'un lien quelconque
*
@ -231,7 +209,7 @@ function lien_is_url_externe($url_lien) {
* @return string
* Retourne une balise HTML de lien ou une chaîne vide.
*/
function balise_a($args = []) {
function balise_a($args = array()) {
$balise_a = '';
// Il faut soit au minimum un href OU un name pour réussir à générer quelque chose
@ -266,51 +244,102 @@ function balise_a($args = []) {
return $balise_a;
}
function expanser_liens($t, $connect = '', $env = []) {
// Regexp des raccourcis, aussi utilisee pour la fusion de sauvegarde Spip
// Laisser passer des paires de crochets pour la balise multi
// mais refuser plus d'imbrications ou de mauvaises imbrications
// sinon les crochets ne peuvent plus servir qu'a ce type de raccourci
define('_RACCOURCI_LIEN', "/\[([^][]*?([[][^]>-]*[]][^][]*)*)->(>?)([^]]*)\]/msS");
// https://code.spip.net/@expanser_liens
function expanser_liens($t, $connect = '', $env = array()) {
$t = pipeline('pre_liens', $t);
include_spip("src/Texte/Collecteur/AbstractCollecteur");
include_spip("src/Texte/Collecteur/Liens");
if (strpos($t, '\[') !== false or strpos($t, '\]') !== false) {
$t = str_replace(array('\[', '\]'), array("\x1\x5", "\x1\x6"), $t);
}
$collecteurLiens = new Spip\Texte\Collecteur\Liens();
$hasLiens = $collecteurLiens->detecter($t);
if ($hasLiens) {
$t = $collecteurLiens->echapper($t);
expanser_un_lien($connect, 'init', $env);
if (strpos($t, '->') !== false) {
$t = preg_replace_callback(_RACCOURCI_LIEN, 'expanser_un_lien', $t);
}
// on passe a traiter_modeles le collecteur de liens reperes pour lui permettre
// on passe a traiter_modeles la liste des liens reperes pour lui permettre
// de remettre le texte d'origine dans les parametres du modele
$t = traiter_modeles($t, false, false, $connect ?? '', $hasLiens ? $collecteurLiens : null, $env);
$t = traiter_modeles($t, false, false, $connect, expanser_un_lien('', 'sources'), $env);
if (strpos($t, "\x1") !== false) {
$t = str_replace(array("\x1\x5", "\x1\x6"), array('[', ']'), $t);
}
$t = corriger_typo($t);
// traiter les liens si il y en avait
if ($hasLiens) {
$t = $collecteurLiens->retablir($t);
$liens = $collecteurLiens->collecter($t);
$t = expanser_un_lien($t, 'reinsert');
if (!empty($liens)) {
$lien = charger_fonction('lien', 'inc');
return $t;
}
$offset_pos = 0;
foreach ($liens as $l) {
[$titre, $bulle, $hlang] = traiter_raccourci_lien_atts($l['texte']);
// corrigeons pour eviter d'avoir un <a...> dans un href...
$href = $l['href'];
$balise_lien = $lien($href, $titre, '', $bulle, $hlang, '', $connect, $env);
function expanser_un_lien($reg, $quoi = 'echappe', $env = null) {
static $pile = array();
static $inserts;
static $sources;
static $regs;
static $k = 0;
static $lien;
static $connect = '';
static $contexte = array();
switch ($quoi) {
case 'init':
if (!$lien) {
$lien = charger_fonction('lien', 'inc');
}
if (!is_null($env)) {
$contexte = $env;
}
array_push($pile, array($inserts, $sources, $regs, $connect, $k));
$inserts = $sources = $regs = array();
$connect = $reg; // stocker le $connect pour les appels a inc_lien_dist
$k = 0;
return;
break;
case 'echappe':
$inserts[$k] = '@@SPIP_ECHAPPE_LIEN_' . $k . '@@';
$sources[$k] = $reg[0];
#$titre=$reg[1];
list($titre, $bulle, $hlang) = traiter_raccourci_lien_atts($reg[1]);
$r = end($reg);
// la mise en lien automatique est passee par la a tort !
// corrigeons pour eviter d'avoir un <a...> dans un href...
if (strncmp($r, '<a', 2) == 0) {
$href = extraire_attribut($r, 'href');
// remplacons dans la source qui peut etre reinjectee dans les arguments
// d'un modele
$sources[$k] = str_replace($r, $href, $sources[$k]);
// et prenons le href comme la vraie url a linker
$r = $href;
}
$regs[$k] = $lien($r, $titre, '', $bulle, $hlang, '', $connect, $contexte);
$t = substr_replace($t, $balise_lien, $l['pos'] + $offset_pos, $l['length']);
$offset_pos += strlen($balise_lien) - $l['length'];
return $inserts[$k++];
break;
case 'reinsert':
if (count($inserts)) {
$reg = str_replace($inserts, $regs, $reg);
}
}
list($inserts, $sources, $regs, $connect, $k) = array_pop($pile);
return $reg;
break;
case 'sources':
return array($inserts, $sources);
break;
}
return $t;
}
/**
* Nettoie un texte en enlevant les raccourcis typo, sans les traiter
*
@ -334,47 +363,41 @@ function nettoyer_raccourcis_typo($texte, $connect = '') {
$texte = preg_replace("/(^|\r|\n)(-[-#\*]*\s?|_ )/", "\n", $texte);
// travailler en accents charset
$texte = unicode2charset(html2unicode($texte, true /* secure */));
include_spip("src/Texte/Collecteur/AbstractCollecteur");
include_spip("src/Texte/Collecteur/Liens");
$collecteurLiens = new Spip\Texte\Collecteur\Liens();
$liens = $collecteurLiens->collecter($texte);
if (!empty($liens)) {
$offset_pos = 0;
foreach ($liens as $l) {
[$titre, , ] = traiter_raccourci_lien_atts($l['texte']);
$texte = unicode2charset(html2unicode($texte, true /* secure */ ));
if (preg_match_all(_RACCOURCI_LIEN, $texte, $regs, PREG_SET_ORDER)) {
include_spip('inc/texte');
foreach ($regs as $reg) {
list($titre, , ) = traiter_raccourci_lien_atts($reg[1]);
if (!$titre) {
$match = typer_raccourci($l['href']);
$match = typer_raccourci($reg[count($reg) - 1]);
if (!isset($match[0])) {
$match[0] = '';
}
[$type, , $id, , , , ] = array_pad($match, 7, null);
@list($type, , $id, , , , ) = $match;
if ($type) {
$url = generer_objet_url($id, $type, '', '', true);
$url = generer_url_entite($id, $type, '', '', true);
if (is_array($url)) {
[$type, $id] = $url;
list($type, $id) = $url;
}
$titre = traiter_raccourci_titre($id, $type, $connect);
}
$titre = $titre ? $titre['titre'] : $match[0];
}
$titre = corriger_typo(supprimer_tags($titre));
$texte = substr_replace($texte, $titre, $l['pos'] + $offset_pos, $l['length']);
$offset_pos += strlen($titre) - $l['length'];
$texte = str_replace($reg[0], $titre, $texte);
}
}
// supprimer les ancres
$texte = preg_replace(_RACCOURCI_ANCRE, '', $texte);
$texte = preg_replace(_RACCOURCI_ANCRE, "", $texte);
// supprimer les notes
$texte = preg_replace(',\[\[.*\]\],UimsS', '', $texte);
$texte = preg_replace(",\[\[.*\]\],UimsS", "", $texte);
// supprimer les codes typos
$texte = str_replace(['}', '{'], '', $texte);
$texte = str_replace(array('}', '{'), '', $texte);
// supprimer les tableaux
$texte = preg_replace(",(?:^|\r|\n)\|.*\|(?:\r|\n|$),s", "\r", $texte);
@ -398,12 +421,13 @@ function nettoyer_raccourcis_typo($texte, $connect = '') {
// pour ne pas confondre avec un autre raccourci
define('_RACCOURCI_ATTRIBUTS', '/^((?:[^[]*?(?:\[[^]]*\])?)*?)([|]([^<>]*?))?([{]([a-z_]*)[}])?$/');
// https://code.spip.net/@traiter_raccourci_lien_atts
function traiter_raccourci_lien_atts($texte) {
$bulle = $hlang = false;
// title et hreflang donnes par le raccourci ?
if (
strpbrk($texte, '|{') !== false
strpbrk($texte, "|{") !== false
and preg_match(_RACCOURCI_ATTRIBUTS, $texte, $m)
) {
$n = count($m);
@ -449,7 +473,7 @@ function traiter_raccourci_lien_atts($texte) {
$bulle = corriger_typo($bulle);
}
return [trim($texte), $bulle, $hlang];
return array(trim($texte), $bulle, $hlang);
}
define('_EXTRAIRE_DOMAINE', '/^(?:(?:[^\W_]((?:[^\W_]|-){0,61}[^\W_,])?\.)+[a-z0-9]{2,6}|localhost)\b/Si');
@ -457,14 +481,14 @@ define('_RACCOURCI_CHAPO', '/^(\W*)(\W*)(\w*\d+([?#].*)?)$/');
/**
* Retourne la valeur d'un champ de redirection (articles virtuels)
*
* L'entrée accepte plusiers types d'écritures :
* - une URL compète,
* - un lien SPIP tel que `[Lien->article23]`,
*
* L'entrée accepte plusiers types d'écritures :
* - une URL compète,
* - un lien SPIP tel que `[Lien->article23]`,
* - ou un raccourcis SPIP comme `rub2` ou `rubrique2`
*
* @param string $virtuel
* Texte qui définit la redirection, à analyser.
* Texte qui définit la redirection, à analyser.
* Plusieurs types peuvent être acceptés :
* - un raccourci Spip habituel, tel que `[texte->TYPEnnn]`
* - un ultra raccourci Spip, tel que `TYPEnnn`
@ -479,21 +503,14 @@ function virtuel_redirige($virtuel, $url = false) {
if (!strlen($virtuel)) {
return '';
}
if (preg_match(_RACCOURCI_CHAPO, $virtuel, $m)) {
return !$url ? $m[3] : traiter_lien_implicite($m[3]);
}
include_spip("src/Texte/Collecteur/AbstractCollecteur");
include_spip("src/Texte/Collecteur/Liens");
$collecteurLiens = new Spip\Texte\Collecteur\Liens();
if ($liens = $collecteurLiens->collecter($virtuel)) {
$lien = reset($liens);
return !$url ? $lien['href'] : traiter_lien_implicite($lien['href']);
if (
!preg_match(_RACCOURCI_LIEN, $virtuel, $m)
and !preg_match(_RACCOURCI_CHAPO, $virtuel, $m)
) {
return $virtuel;
}
return $virtuel;
return !$url ? $m[3] : traiter_lien_implicite($m[3]);
}
@ -506,18 +523,20 @@ function virtuel_redirige($virtuel, $url = false) {
// 'titre': seulement T ci-dessus (i.e. le TITRE ci-dessus ou dans table SQL)
// 'url': seulement U (i.e. generer_url_RACCOURCI)
// https://code.spip.net/@calculer_url
function calculer_url($ref, $texte = '', $pour = 'url', $connect = '', $echappe_typo = true) {
$r = traiter_lien_implicite($ref, $texte, $pour, $connect);
$r = ($r ?: traiter_lien_explicite($ref, $texte, $pour, $connect, $echappe_typo));
$r = traiter_lien_implicite($ref, $texte, $pour, $connect, $echappe_typo);
$r = ($r ? $r : traiter_lien_explicite($ref, $texte, $pour, $connect, $echappe_typo));
return $r;
}
define('_EXTRAIRE_LIEN', ',^\s*(http:?/?/?|mailto:?)\s*$,iS');
define('_EXTRAIRE_LIEN', ",^\s*(http:?/?/?|mailto:?)\s*$,iS");
// https://code.spip.net/@traiter_lien_explicite
function traiter_lien_explicite($ref, $texte = '', $pour = 'url', $connect = '', $echappe_typo = true) {
if (preg_match(_EXTRAIRE_LIEN, $ref)) {
return ($pour != 'tout') ? '' : ['', '', '', ''];
return ($pour != 'tout') ? '' : array('', '', '', '');
}
$lien = entites_html(trim($ref));
@ -532,19 +551,19 @@ function traiter_lien_explicite($ref, $texte = '', $pour = 'url', $connect = '',
}
$texte = $lien_court($texte);
if ($echappe_typo) {
$texte = '<html>' . quote_amp($texte) . '</html>';
$texte = "<html>" . quote_amp($texte) . "</html>";
}
}
// petites corrections d'URL
if (preg_match('/^www\.[^@]+$/S', $lien)) {
$lien = 'http://' . $lien;
$lien = "http://" . $lien;
} else {
if (strpos($lien, '@') && email_valide($lien)) {
if (strpos($lien, "@") && email_valide($lien)) {
if (!$texte) {
$texte = $lien;
}
$lien = 'mailto:' . $lien;
$lien = "mailto:" . $lien;
}
}
@ -556,7 +575,7 @@ function traiter_lien_explicite($ref, $texte = '', $pour = 'url', $connect = '',
return $texte;
}
return ['url' => $lien, 'titre' => $texte];
return array('url' => $lien, 'titre' => $texte);
}
function liens_implicite_glose_dist($texte, $id, $type, $args, $ancre, $connect = '') {
@ -581,6 +600,8 @@ function liens_implicite_glose_dist($texte, $id, $type, $args, $ancre, $connect
* unset($GLOBALS['lien_implicite_cible_public']);
* => retablit le comportement automatique
*
* https://code.spip.net/@traiter_lien_implicite
*
* @param string $ref
* @param string $texte
* @param string $pour
@ -588,22 +609,22 @@ function liens_implicite_glose_dist($texte, $id, $type, $args, $ancre, $connect
* @return array|bool|string
*/
function traiter_lien_implicite($ref, $texte = '', $pour = 'url', $connect = '') {
$cible = $GLOBALS['lien_implicite_cible_public'] ?? null;
$cible = ($connect ? $connect : (isset($GLOBALS['lien_implicite_cible_public']) ? $GLOBALS['lien_implicite_cible_public'] : null));
if (!($match = typer_raccourci($ref))) {
return false;
}
[$type, , $id, , $args, , $ancre] = array_pad($match, 7, null);
@list($type, , $id, , $args, , $ancre) = $match;
# attention dans le cas des sites le lien doit pointer non pas sur
# la page locale du site, mais directement sur le site lui-meme
$url = '';
if ($f = charger_fonction("implicite_$type", 'liens', true)) {
if ($f = charger_fonction("implicite_$type", "liens", true)) {
$url = $f($texte, $id, $type, $args, $ancre, $connect);
}
if (!$url) {
$url = generer_objet_url($id, $type, $args ?? '', $ancre ?? '', $cible, '', $connect ?? '');
$url = generer_url_entite($id, $type, $args, $ancre, $cible);
}
if (!$url) {
@ -611,8 +632,8 @@ function traiter_lien_implicite($ref, $texte = '', $pour = 'url', $connect = '')
}
if (is_array($url)) {
[$type, $id] = array_pad($url, 2, null);
$url = generer_objet_url($id, $type, $args ?? '', $ancre ?? '', $cible, '', $connect ?? '');
@list($type, $id) = $url;
$url = generer_url_entite($id, $type, $args, $ancre, $cible);
}
if ($pour === 'url') {
@ -641,16 +662,9 @@ function traiter_lien_implicite($ref, $texte = '', $pour = 'url', $connect = '')
// dans le cas d'un lien vers un doc, ajouter le type='mime/type'
if (
$type == 'document'
and $mime = sql_getfetsel(
'mime_type',
'spip_types_documents',
'extension IN (' . sql_get_select('extension', 'spip_documents', 'id_document=' . sql_quote($id)) . ')',
'',
'',
'',
'',
$connect
)
and $mime = sql_getfetsel('mime_type', 'spip_types_documents',
"extension IN (" . sql_get_select("extension", "spip_documents", "id_document=" . sql_quote($id)) . ")",
'', '', '', '', $connect)
) {
$r['mime'] = $mime;
}
@ -662,9 +676,10 @@ function traiter_lien_implicite($ref, $texte = '', $pour = 'url', $connect = '')
define('_RACCOURCI_URL', '/^\s*(\w*?)\s*(\d+)(\?(.*?))?(#([^\s]*))?\s*$/S');
// https://code.spip.net/@typer_raccourci
function typer_raccourci($lien) {
if (!preg_match(_RACCOURCI_URL, $lien, $match)) {
return [];
return array();
}
$f = $match[1];
@ -718,14 +733,14 @@ function traiter_raccourci_titre($id, $type, $connect = null) {
$desc = $trouver_table(table_objet($type));
if (!($desc and $s = $desc['titre'])) {
return [];
return array();
}
$_id = $desc['key']['PRIMARY KEY'];
$r = sql_fetsel($s, $desc['table'], "$_id=$id", '', '', '', '', $connect);
if (!$r) {
return [];
return array();
}
$r['titre'] = supprimer_numero($r['titre']);
@ -741,12 +756,144 @@ function traiter_raccourci_titre($id, $type, $connect = null) {
return $r;
}
// traite les modeles (dans la fonction typo), en remplacant
// le raccourci <modeleN|parametres> par la page calculee a
// partir du squelette modeles/modele.html
// Le nom du modele doit faire au moins trois caracteres (evite <h2>)
// Si $doublons==true, on repere les documents sans calculer les modeles
// mais on renvoie les params (pour l'indexation par le moteur de recherche)
// https://code.spip.net/@traiter_modeles
define('_PREG_MODELE',
'(<([a-z_-]{3,})' # <modele
. '\s*([0-9]*)\s*' # id
. '([|](?:<[^<>]*>|[^>])*?)?' # |arguments (y compris des tags <...>)
. '\s*/?' . '>)' # fin du modele >
);
define('_RACCOURCI_MODELE',
_PREG_MODELE
. '\s*(<\/a>)?' # eventuel </a>
);
define('_RACCOURCI_MODELE_DEBUT', '@^' . _RACCOURCI_MODELE . '@isS');
// https://code.spip.net/@traiter_modeles
function traiter_modeles($texte, $doublons = false, $echap = '', $connect = '', $liens = null, $env = array()) {
// preserver la compatibilite : true = recherche des documents
if ($doublons === true) {
$doublons = array('documents' => array('doc', 'emb', 'img'));
}
// detecter les modeles (rapide)
if (
strpos($texte, "<") !== false
and preg_match_all('/<[a-z_-]{3,}\s*[0-9|]+/iS', $texte, $matches, PREG_SET_ORDER)
) {
include_spip('public/assembler');
$wrap_embed_html = charger_fonction("wrap_embed_html", "inc", true);
// Recuperer l'appel complet (y compris un eventuel lien)
foreach ($matches as $match) {
$a = strpos($texte, $match[0]);
preg_match(_RACCOURCI_MODELE_DEBUT, substr($texte, $a), $regs);
// s'assurer qu'il y a toujours un 5e arg, eventuellement vide
while (count($regs) < 6) {
$regs[] = "";
}
list(, $mod, $type, $id, $params, $fin) = $regs;
if (
$fin
and preg_match('/<a\s[^<>]*>\s*$/i', substr($texte, 0, $a), $r)
) {
$lien = array(
'href' => extraire_attribut($r[0], 'href'),
'class' => extraire_attribut($r[0], 'class'),
'mime' => extraire_attribut($r[0], 'type'),
'title' => extraire_attribut($r[0], 'title'),
'hreflang' => extraire_attribut($r[0], 'hreflang')
);
$n = strlen($r[0]);
$a -= $n;
$cherche = $n + strlen($regs[0]);
} else {
$lien = false;
$cherche = strlen($mod);
}
// calculer le modele
# hack indexation
if ($doublons) {
$texte .= preg_replace(',[|][^|=]*,s', ' ', $params);
} # version normale
else {
// si un tableau de liens a ete passe, reinjecter le contenu d'origine
// dans les parametres, plutot que les liens echappes
if (!is_null($liens)) {
$params = str_replace($liens[0], $liens[1], $params);
}
$modele = inclure_modele($type, $id, $params, $lien, $connect, $env);
// en cas d'echec,
// si l'objet demande a une url,
// creer un petit encadre vers elle
if ($modele === false) {
$modele = substr($texte, $a, $cherche);
if (!is_null($liens)) {
$modele = str_replace($liens[0], $liens[1], $modele);
}
$contexte = array_merge($env, array('id' => $id, 'type' => $type, 'modele' => $modele));
if ($lien) {
# un eventuel guillemet (") sera reechappe par #ENV
$contexte['lien'] = str_replace("&quot;", '"', $lien['href']);
$contexte['lien_class'] = $lien['class'];
$contexte['lien_mime'] = $lien['mime'];
$contexte['lien_title'] = $lien['title'];
$contexte['lien_hreflang'] = $lien['hreflang'];
}
$modele = recuperer_fond("modeles/dist", $contexte, array(), $connect);
}
// le remplacer dans le texte
if ($modele !== false) {
$modele = protege_js_modeles($modele);
if ($wrap_embed_html) {
$modele = $wrap_embed_html($mod, $modele);
}
$rempl = code_echappement($modele, $echap);
$texte = substr($texte, 0, $a)
. $rempl
. substr($texte, $a + $cherche);
}
}
// hack pour tout l'espace prive
if (((!_DIR_RESTREINT) or ($doublons)) and ($id)) {
foreach ($doublons ? $doublons : array('documents' => array('doc', 'emb', 'img')) as $quoi => $modeles) {
if (in_array(strtolower($type), $modeles)) {
$GLOBALS["doublons_{$quoi}_inclus"][] = $id;
}
}
}
}
}
return $texte;
}
//
// Raccourcis ancre [#ancre<-]
//
define('_RACCOURCI_ANCRE', '/\[#?([^][]*)<-\]/S');
define('_RACCOURCI_ANCRE', "/\[#?([^][]*)<-\]/S");
// https://code.spip.net/@traiter_raccourci_ancre
function traiter_raccourci_ancre($letexte) {
if (preg_match_all(_RACCOURCI_ANCRE, $letexte, $m, PREG_SET_ORDER)) {
foreach ($m as $regs) {
@ -766,9 +913,10 @@ function traiter_raccourci_ancre($letexte) {
// Wikipedia par defaut, avec ses contraintes techniques
// cf. http://fr.wikipedia.org/wiki/Wikip%C3%A9dia:Conventions_sur_les_titres
define('_RACCOURCI_GLOSSAIRE', '/\[\?+\s*([^][<>]+)\]/S');
define('_RACCOURCI_GLOSSAIRE', "/\[\?+\s*([^][<>]+)\]/S");
define('_RACCOURCI_GLOSES', '/^([^|#{]*\w[^|#{]*)([^#]*)(#([^|{}]*))?(.*)$/S');
// https://code.spip.net/@traiter_raccourci_glossaire
function traiter_raccourci_glossaire($texte) {
if (!preg_match_all(_RACCOURCI_GLOSSAIRE, $texte, $matches, PREG_SET_ORDER)) {
return $texte;
@ -787,7 +935,7 @@ function traiter_raccourci_glossaire($texte) {
$_n = intval($m[2]);
$gloss = $m[1] ? ('#' . $m[1]) : '';
$t = $r[1] . $r[2] . $r[5];
[$t, $bulle, $hlang] = traiter_raccourci_lien_atts($t);
list($t, $bulle, $hlang) = traiter_raccourci_lien_atts($t);
if ($bulle === false) {
$bulle = $m[1];
@ -802,25 +950,26 @@ function traiter_raccourci_glossaire($texte) {
return $texte;
}
// https://code.spip.net/@glossaire_std
function glossaire_std($terme) {
global $url_glossaire_externe;
static $pcre = null;
if ($pcre === null) {
$pcre = $GLOBALS['meta']['pcre_u'] ?? '';
$pcre = isset($GLOBALS['meta']['pcre_u']) ? $GLOBALS['meta']['pcre_u'] : '';
if (strpos($url_glossaire_externe, '%s') === false) {
if (strpos($url_glossaire_externe, "%s") === false) {
$url_glossaire_externe .= '%s';
}
}
$glosateur = str_replace(
'@lang@',
"@lang@",
$GLOBALS['spip_lang'],
$GLOBALS['url_glossaire_externe']
);
$terme = rawurlencode(preg_replace(',\s+,' . $pcre, '_', $terme));
return str_replace('%s', $terme, $glosateur);
return str_replace("%s", $terme, $glosateur);
}

80
inc/notes.php

@ -1,13 +1,13 @@
<?php
/***************************************************************************\
* SPIP, Système de publication pour l'internet *
* SPIP, Systeme de publication pour l'internet *
* *
* Copyright © avec tendresse depuis 2001 *
* Arnaud Martin, Antoine Pitrou, Philippe Rivière, Emmanuel Saint-James *
* Copyright (c) 2001-2020 *
* Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
* *
* Ce programme est un logiciel libre distribué sous licence GNU/GPL. *
* Pour plus de détails voir le fichier COPYING.txt ou l'aide en ligne. *
* 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. *
\***************************************************************************/
/**
@ -16,7 +16,7 @@
* @package SPIP\Textwheel\Notes
**/