Dépôt officiel du core SPIP Les plugins-dist faisant partie de la distribution SPIP sont présents dans https://git.spip.net/spip/[nom du plugin dist] https://www.spip.net
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

281 lines
9.0 KiB

<?php
21 years ago
18 years ago
/***************************************************************************\
* SPIP, Système de publication pour l'internet *
18 years ago
* *
* Copyright © avec tendresse depuis 2001 *
* Arnaud Martin, Antoine Pitrou, Philippe Rivière, Emmanuel Saint-James *
18 years ago
* *
* 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. *
18 years ago
\***************************************************************************/
/**
* Gestion des URLS
*
* @package SPIP\Core\URLs
**/
if (!defined('_ECRIRE_INC_VERSION')) {
return;
}
include_spip('base/objets');
21 years ago
/**
* Décoder une URL en utilisant les fonctions inverses
*
* Gère les URLs transformées par le htaccess.
*
* @note
* `$renommer = 'urls_propres_dist';`
* renvoie `array($contexte, $type, $url_redirect, $nfond)`
*
* `$nfond` n'est retourné que si l'URL est définie apres le `?`
* et risque d'être effacée par un form en get.
* Elle est utilisée par form_hidden exclusivement.
*
* Compat ascendante si le retour est NULL en gérant une sauvegarde/restauration
* des globales modifiées par les anciennes fonctions
*
* @param string $url
* URL à décoder
* @param string $fond
* Fond initial par défaut
* @param array $contexte
* Contexte initial à prendre en compte
* @param bool $assembler
* `true` si l'URL correspond à l'URL principale de la page qu'on est en train d'assembler
* dans ce cas la fonction redirigera automatiquement si besoin
* et utilisera les eventuelles globales `$_SERVER['REDIRECT_url_propre']` et `$_ENV['url_propre']`
* provenant du htaccess
* @return array
* Liste `$fond, $contexte, $url_redirect`.
*
* Si l'url n'est pas valide, $fond restera à la valeur initiale passée.
* Il suffit d'appeler la fonction sans $fond et de vérifier qu'à son retour celui-ci
* est non vide pour vérifier une URL
*
*/
function urls_decoder_url($url, $fond = '', $contexte = [], $assembler = false) {
static $current_base = null;
// les anciennes fonctions modifient directement les globales
// on les sauve avant l'appel, et on les retablit apres !
$save = [
isset($GLOBALS['fond']) ? $GLOBALS['fond'] : null,
isset($GLOBALS['contexte']) ? $GLOBALS['contexte'] : null,
isset($_SERVER['REDIRECT_url_propre']) ? $_SERVER['REDIRECT_url_propre'] : null,
isset($_ENV['url_propre']) ? $_ENV['url_propre'] : null,
$GLOBALS['profondeur_url']
];
if (is_null($current_base)) {
include_spip('inc/filtres_mini');
11 years ago
// le decodage des urls se fait toujours par rapport au site public
$current_base = url_absolue(_DIR_RACINE ? _DIR_RACINE : './');
}
if (strncmp($url, $current_base, strlen($current_base)) == 0) {
$url = substr($url, strlen($current_base));
}
// si on est en train d'assembler la page principale,
// recuperer l'url depuis les globales url propres si fournies
// sinon extraire la bonne portion d'url
if ($assembler) {
if (isset($_SERVER['REDIRECT_url_propre'])) {
$url = $_SERVER['REDIRECT_url_propre'];
} elseif (isset($_ENV['url_propre'])) {
$url = $_ENV['url_propre'];
} else {
$qs = explode('?', $url);
// ne prendre que le segment d'url qui correspond, en fonction de la profondeur calculee
$url = ltrim($qs[0], '/');
$url = explode('/', $url);
while (count($url) > $GLOBALS['profondeur_url'] + 1) {
array_shift($url);
}
$qs[0] = implode('/', $url);
$url = implode('?', $qs);
}
}
unset($_SERVER['REDIRECT_url_propre']);
unset($_ENV['url_propre']);
include_spip('inc/filtres_mini');
if (strpos($url, '://') === false) {
$GLOBALS['profondeur_url'] = substr_count(ltrim(resolve_path("/$url"), '/'), '/');
} else {
$GLOBALS['profondeur_url'] = max(0, substr_count($url, '/') - substr_count($current_base, '/'));
}
$url_redirect = '';
$renommer = generer_url_entite('', '', '', '', true);
if (!$renommer and !function_exists('recuperer_parametres_url')) {
$renommer = charger_fonction('page', 'urls');
} // fallback pour decoder l'url
if ($renommer) {
$a = $renommer($url, $fond, $contexte);
if (is_array($a)) {
list($ncontexte, $type, $url_redirect, $nfond) = array_pad($a, 4, null);
if ($url_redirect == $url) {
$url_redirect = '';
} // securite pour eviter une redirection infinie
if ($assembler and strlen($url_redirect)) {
spip_log("Redirige $url vers $url_redirect");
include_spip('inc/headers');
redirige_par_entete($url_redirect, '', 301);
}
if (isset($nfond)) {
$fond = $nfond;
} else {
if (
$fond == ''
or $fond == 'type_urls' /* compat avec htaccess 2.0.0 */
) {
$fond = $type;
}
}
if (isset($ncontexte)) {
$contexte = $ncontexte;
}
if (defined('_DEFINIR_CONTEXTE_TYPE') and _DEFINIR_CONTEXTE_TYPE) {
12 years ago
$contexte['type'] = $type;
}
if (!defined('_DEFINIR_CONTEXTE_TYPE_PAGE') or _DEFINIR_CONTEXTE_TYPE_PAGE) {
$contexte['type-page'] = $type;
}
}
} // compatibilite <= 1.9.2
elseif (function_exists('recuperer_parametres_url')) {
$GLOBALS['fond'] = $fond;
$GLOBALS['contexte'] = $contexte;
recuperer_parametres_url($fond, nettoyer_uri());
// fond est en principe modifiee directement
$contexte = $GLOBALS['contexte'];
}
// retablir les globales
list($GLOBALS['fond'], $GLOBALS['contexte'], $_SERVER['REDIRECT_url_propre'], $_ENV['url_propre'], $GLOBALS['profondeur_url']) = $save;
// vider les globales url propres qui ne doivent plus etre utilisees en cas
// d'inversion url => objet
// maintenir pour compat ?
#if ($assembler) {
# unset($_SERVER['REDIRECT_url_propre']);
# unset($_ENV['url_propre']);
#}
return [$fond, $contexte, $url_redirect];
}
/**
* Lister les objets pris en compte dans les URLs
* c'est à dire suceptibles d'avoir une URL propre
*
* @param bool $preg
* Permet de définir si la fonction retourne une chaine avec `|` comme séparateur
* pour utiliser en preg, ou un array()
* @return string|array
*/
function urls_liste_objets($preg = true) {
static $url_objets = null;
if (is_null($url_objets)) {
$url_objets = [];
// recuperer les tables_objets_sql declarees
$tables_objets = lister_tables_objets_sql();
foreach ($tables_objets as $t => $infos) {
if ($infos['page']) {
$url_objets[] = $infos['type'];
$url_objets = array_merge($url_objets, $infos['type_surnoms']);
}
}
$url_objets = pipeline('declarer_url_objets', $url_objets);
}
if (!$preg) {
return $url_objets;
}
return implode('|', array_map('preg_quote', $url_objets));
}
/**
* Nettoyer une URL, en repérant notamment les raccourcis d'objets
*
* Repère les entités comme `?article13`, `?rubrique21` ...
* les traduisant pour compléter le contexte fourni en entrée
*
* @param string $url
* @param array $contexte
* @return array
*/
function nettoyer_url_page($url, $contexte = []) {
$url_objets = urls_liste_objets();
$raccourci_url_page_html = ',^(?:[^?]*/)?(' . $url_objets . ')([0-9]+)(?:\.html)?([?&].*)?$,';
$raccourci_url_page_id = ',^(?:[^?]*/)?(' . $url_objets . ')\.php3?[?]id_\1=([0-9]+)([?&].*)?$,';
$raccourci_url_page_spip = ',^(?:[^?]*/)?(?:spip[.]php)?[?](' . $url_objets . ')([0-9]+)=?(&.*)?$,';
if (
preg_match($raccourci_url_page_html, $url, $regs)
or preg_match($raccourci_url_page_id, $url, $regs)
or preg_match($raccourci_url_page_spip, $url, $regs)
) {
$regs = array_pad($regs, 4, null);
$type = objet_type($regs[1]);
$_id = id_table_objet($type);
$contexte[$_id] = $regs[2];
$suite = $regs[3];
return [$contexte, $type, null, $type, $suite];
}
return [];
}
/**
* Générer l'URL d'un objet dans l'espace privé
*
* L'URL est calculée en fonction de son état publié ou non,
* calculé à partir de la déclaration de statut.
*
* @param string $objet Type d'objet
* @param int $id Identifiant de l'objet
* @param string $args
* @param string $ancre
* @param bool|null $public
* @param string $connect
* @return string
*
*/
function generer_url_ecrire_objet($objet, $id, $args = '', $ancre = '', $public = null, $connect = '') {
static $furls = [];
if (!isset($furls[$objet])) {
if (
function_exists($f = 'generer_url_ecrire_' . $objet)
// ou definie par un plugin
or $f = charger_fonction($f, 'urls', true)
) {
$furls[$objet] = $f;
} else {
$furls[$objet] = '';
}
}
if ($furls[$objet]) {
return $furls[$objet]($id, $args, $ancre, $public, $connect);
}
// si pas de flag public fourni
// le calculer en fonction de la declaration de statut
if (is_null($public) and !$connect) {
$public = objet_test_si_publie($objet, $id, $connect);
}
if ($public or $connect) {
return generer_url_entite_absolue($id, $objet, $args, $ancre, $connect);
Revision de l'interface aux jeux de fonctions generer_url_$objet, commandés en particulier par la globale $type_urls. Ces fonctions n'admettaient auparavant qu'un seul argument, index de la clé primaire dans la table correspondante. En conséquence, il n'était pas possible d'exploiter à fond les possibilités des raccourcis données par la RegExp: define('_RACCOURCI_URL', ',^\s*(\w*?)\s*(\d+)(\?(.*?))?(#([^\s]*))?$,S'); qui autorise explicitement paramètres supplémentaires et ancre, lesquels étaient raoutés ultérieurement, et mal à propos en cas de redirection. A présent, les fonctions generer_url_*objet sont appelées avec les 2 arguments supplémentaires trouvés dans le raccourci, le premier étant censé etre une suite n1=v1&n2=v2 et le deuxième une ancre. Les définitions standards de ces jeux de fonctions (urls/page urls/html urls/propres et inc/urls) admettent ces nouveaux arguments et les injectent dans les URL construites. Dans le cas où les URLs sont en fait des scripts qui provoqueront une redirection, l'ancre est fourni sous la forme "ancre=nom", le script de redirection synthétisant "#$ancre" dans l'URL finale. Compatibilité: - totale pour les sites sans mes_options.php personnelle; - vraisemblablement assurée pour ceux définissant un jeu de fonctions generer_url_$objet car: -- PHP ne dit rien en présence d'arguments excédentaires -- les variantes generer_url_ecrire_$objet admettaient certes déjà un 2e argument, mais son utilisation comportait un bug suggérant qu'il n'avait jamais utilisé en dehors du noyau de Spip où il était transparent (cet argument est donc maintenant en 4e position, et permet de forcer le statut de l'objet) -- le 2e argument generer_url_forum n'avait jamais été utilisé; -- ce sont à présent ces fonctions qui doivent rajouter la suite d'arguments supplémentaires et l'ancre, mais il est douteux que certaines comptaient dessus sinon la faute de conception motivant ce dépot aurait été signalée depuis longtemps; - pour le sous-jeu de fonctions calculer_url_$objet, il reçoit à présent en 3e argument ce qu'a calculé le generer_url_$objet correspondant, ce qui leur facilite le travail. A noter une curieuse dissymétrie pour le raccourci des sites, qui dans ce cas retoune l'URL du site et non du script (conservée telle quelle). A signaler également que la RegExp des raccourcis était assez laxiste, cherchanten vain des fonctions generer_url_$objet avec $objet n'étant pas nécessairement un \w+.
16 years ago
}
$a = id_table_objet($objet) . '=' . intval($id);
if (!function_exists('objet_info')) {
include_spip('inc/filtres');
}
return generer_url_ecrire(objet_info($objet, 'url_voir'), $a . ($args ? "&$args" : '')) . ($ancre ? "#$ancre" : '');
}