source: spip-zone/_plugins_/polyhierarchie/branches/v2.0/urls/arbopoly.php @ 100812

Last change on this file since 100812 was 100812, checked in by cedric@…, 3 years ago

on extrait la seule reference a spip_rubriques_liens dans une fonction generique surchargeable url_verifier_parent_objet(), ce qui rend le module arbopoly generique, non dependant de la polyhierarchie et de son implementation

File size: 16.3 KB
Line 
1<?php
2
3/***************************************************************************\
4 *  SPIP, Systeme de publication pour l'internet                           *
5 *                                                                         *
6 *  Copyright (c) 2001-2016                                                *
7 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
8 *                                                                         *
9 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
10 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
11\***************************************************************************/
12
13if (!defined("_ECRIRE_INC_VERSION")) {
14        return;
15} // securiser
16
17# donner un exemple d'url pour le formulaire de choix
18define('URLS_ARBOPOLY_EXEMPLE', '/article/titre');
19# specifier le form de config utilise pour ces urls
20define('URLS_ARBOPOLY_CONFIG', 'arbo');
21
22// TODO: une interface permettant de verifier qu'on veut effectivment modifier
23// une adresse existante
24defined('CONFIRMER_MODIFIER_URL') || define('CONFIRMER_MODIFIER_URL', false);
25
26include_spip('urls/arbo');
27
28/**
29 * Renseigner les infos les plus recentes de l'url d'un objet
30 * et de quoi la (re)construire si besoin
31 *
32 * @param string $type
33 * @param int $id_objet
34 * @param array $contexte
35 *   id_parent : rubrique parent
36 * @return bool|null|array
37 */
38function renseigner_url_arbopoly($type, $id_objet, $contexte = array()) {
39        $urls = array();
40        $trouver_table = charger_fonction('trouver_table', 'base');
41        $desc = $trouver_table(table_objet($type));
42        $table = $desc['table'];
43        $col_id = @$desc['key']["PRIMARY KEY"];
44        if (!$col_id) {
45                return false;
46        } // Quand $type ne reference pas une table
47        $id_objet = intval($id_objet);
48
49        $id_parent = (isset($contexte['id_parent'])?$contexte['id_parent']:null);
50
51        $champ_titre = $desc['titre'] ? $desc['titre'] : 'titre';
52
53        // parent
54        $champ_parent = url_arbo_parent($type);
55        $sel_parent = ', 0 as parent';
56        $order_by_parent = "";
57        if ($champ_parent) {
58                // si un parent est fourni est qu'il est polyhierarchique, on recherche une URL pour ce parent
59                if ($id_parent
60                        and $type_parent = end($champ_parent)
61                        and $url_verifier_parent_objet = charger_fonction('url_verifier_parent_objet', 'inc', true)
62                        and $url_verifier_parent_objet($type, $id_objet, $type_parent, $id_parent)) {
63                        $sel_parent = ", ".intval($id_parent) . ' as parent';
64                        // trouver l'url qui matche le parent en premier
65                        $order_by_parent = "U.id_parent=".intval($id_parent)." DESC, ";
66                }
67                // sinon on prend son parent direct fourni par $champ_parent
68                else {
69                        $sel_parent = ", O." . reset($champ_parent) . ' as parent';
70                        // trouver l'url qui matche le parent en premier
71                        $order_by_parent = "O." . reset($champ_parent) . "=U.id_parent DESC, ";
72                }
73        }
74        //  Recuperer une URL propre correspondant a l'objet.
75        $row = sql_fetsel("U.url, U.date, U.id_parent, U.perma, $champ_titre $sel_parent",
76                "$table AS O LEFT JOIN spip_urls AS U ON (U.type='$type' AND U.id_objet=O.$col_id)",
77                "O.$col_id=$id_objet",
78                '',
79                $order_by_parent . 'U.perma DESC, U.date DESC', 1);
80        if ($row) {
81                $urls[$type][$id_objet] = $row;
82                $urls[$type][$id_objet]['type_parent'] = $champ_parent ? end($champ_parent) : '';
83        }
84
85        return isset($urls[$type][$id_objet]) ? $urls[$type][$id_objet] : null;
86}
87
88/**
89 * Retrouver/Calculer l'ensemble des segments d'url d'un objet
90 *
91 * @param string $type
92 * @param int $id_objet
93 * @param array $contexte
94 *   id_parent : rubrique parent
95 * @return string
96 */
97function declarer_url_arbopoly($type, $id_objet, $contexte = array()) {
98        static $urls = array();
99        // utiliser un cache memoire pour aller plus vite
100        if (!is_null($C = Cache())) {
101                return $C;
102        }
103        ksort($contexte);
104        $hash = json_encode($contexte);
105
106        // Se contenter de cette URL si elle existe ;
107        // sauf si on invoque par "voir en ligne" avec droit de modifier l'url
108
109        // l'autorisation est verifiee apres avoir calcule la nouvelle url propre
110        // car si elle ne change pas, cela ne sert a rien de verifier les autorisations
111        // qui requetent en base
112        $modifier_url = (defined('_VAR_URLS') and _VAR_URLS);
113
114        if (!isset($urls[$type][$id_objet][$hash]) or $modifier_url) {
115                $r = renseigner_url_arbopoly($type, $id_objet, $contexte);
116                // Quand $type ne reference pas une table
117                if ($r === false) {
118                        return false;
119                }
120
121                if (!is_null($r)) {
122                        $urls[$type][$id_objet][$hash] = $r;
123                }
124        }
125
126        if (!isset($urls[$type][$id_objet][$hash])) {
127                return "";
128        } # objet inexistant
129
130        $u = &$urls[$type][$id_objet][$hash];
131        $url_propre = $u['url'];
132
133        // si on a trouve l'url
134        // et que le parent est bon
135        // et (permanente ou pas de demande de modif)
136        if (!is_null($url_propre)
137                and $u['id_parent'] == $u['parent']
138                and ($u['perma'] or !$modifier_url)
139        ) {
140                return declarer_url_arbo_rec($url_propre, $type,
141                        isset($u['parent']) ? $u['parent'] : 0,
142                        isset($u['type_parent']) ? $u['type_parent'] : null);
143        }
144
145        // Si URL inconnue ou maj forcee sur une url non permanente, recreer une url
146        $url = $url_propre;
147        if (is_null($url_propre) or ($modifier_url and !$u['perma'])) {
148                $url = pipeline('arbo_creer_chaine_url',
149                        array(
150                                'data' => $url_propre,  // le vieux url_propre
151                                'objet' => array_merge($u, array('type' => $type, 'id_objet' => $id_objet)
152                                )
153                        )
154                );
155
156                // Eviter de tamponner les URLs a l'ancienne (cas d'un article
157                // intitule "auteur2")
158                include_spip('inc/urls');
159                $objets = urls_liste_objets();
160                if (preg_match(',^(' . $objets . ')[0-9]*$,', $url, $r)
161                        and $r[1] != $type
162                ) {
163                        $url = $url . _url_arbo_sep_id . $id_objet;
164                }
165        }
166
167
168        // Pas de changement d'url ni de parent
169        if ($url == $url_propre
170                and $u['id_parent'] == $u['parent']
171        ) {
172                return declarer_url_arbo_rec($url_propre, $type, $u['parent'], $u['type_parent']);
173        }
174
175        // verifier l'autorisation, maintenant qu'on est sur qu'on va agir
176        if ($modifier_url) {
177                include_spip('inc/autoriser');
178                $modifier_url = autoriser('modifierurl', $type, $id_objet);
179        }
180        // Verifier si l'utilisateur veut effectivement changer l'URL
181        if ($modifier_url
182                and CONFIRMER_MODIFIER_URL
183                and $url_propre
184                // on essaye pas de regenerer une url en -xxx (suffixe id anti collision)
185                and $url != preg_replace('/' . preg_quote(_url_propres_sep_id, '/') . '.*/', '', $url_propre)
186        ) {
187                $confirmer = true;
188        } else {
189                $confirmer = false;
190        }
191
192        if ($confirmer and !_request('ok')) {
193                die("vous changez d'url ? $url_propre -&gt; $url");
194        }
195
196        $set = array(
197                'url' => $url,
198                'type' => $type,
199                'id_objet' => $id_objet,
200                'id_parent' => $u['parent'],
201                'perma' => intval($u['perma'])
202        );
203        include_spip('action/editer_url');
204        if (url_insert($set, $confirmer, _url_arbo_sep_id)) {
205                $u['url'] = $set['url'];
206                $u['id_parent'] = $set['id_parent'];
207        } else {
208                // l'insertion a echoue,
209                //serveur out ? retourner au mieux
210                $u['url'] = $url_propre;
211        }
212
213        return declarer_url_arbo_rec($u['url'], $type, $u['parent'], $u['type_parent']);
214}
215
216/**
217 * Generer l'url arbo complete constituee des segments + debut + fin
218 *
219 * @param string $type
220 * @param int $id
221 * @param string $args
222 * @param string $ancre
223 * @return string
224 */
225function _generer_url_arbopoly($type, $id, $args = '', $ancre = '') {
226
227        if ($generer_url_externe = charger_fonction("generer_url_$type", 'urls', true)) {
228                $url = $generer_url_externe($id, $args, $ancre);
229                if (null != $url) {
230                        return $url;
231                }
232        }
233
234        // Mode propre
235        $c = array();
236        $propre = declarer_url_arbopoly($type, $id, $c);
237        parse_str($args, $contexte);
238        $champ_parent = url_arbo_parent($type);
239        if ($champ_parent
240          and $champ_parent = reset($champ_parent)
241          and isset($contexte[$champ_parent]) and $contexte[$champ_parent]) {
242                $c['id_parent'] = $contexte[$champ_parent];
243                $propre_contexte = declarer_url_arbopoly($type, $id, $c);
244                if ($propre_contexte !== $propre) {
245                        $propre = $propre_contexte;
246                        unset($contexte[$champ_parent]);
247                        $args = http_build_query($contexte);
248                }
249        }
250
251
252        if ($propre === false) {
253                return '';
254        } // objet inconnu. raccourci ?
255
256        if ($propre) {
257                $url = _debut_urls_arbo
258                        . rtrim($propre, '/')
259                        . url_arbo_terminaison($type);
260        } else {
261
262                // objet connu mais sans possibilite d'URL lisible, revenir au defaut
263                include_spip('base/connect_sql');
264                $id_type = id_table_objet($type);
265                $url = get_spip_script('./') . "?" . _SPIP_PAGE . "=$type&$id_type=$id";
266        }
267
268        // Ajouter les args
269        if ($args) {
270                $url .= ((strpos($url, '?') === false) ? '?' : '&') . $args;
271        }
272
273        // Ajouter l'ancre
274        if ($ancre) {
275                $url .= "#$ancre";
276        }
277
278        return _DIR_RACINE . $url;
279}
280
281
282/**
283 * API : retourner l'url d'un objet si i est numerique
284 * ou decoder cette url si c'est une chaine
285 * array([contexte],[type],[url_redirect],[fond]) : url decodee
286 *
287 * http://code.spip.net/@urls_arbo_dist
288 *
289 * @param string|int $i
290 * @param string $entite
291 * @param string|array $args
292 * @param string $ancre
293 * @return array|string
294 */
295function urls_arbopoly_dist($i, $entite, $args = '', $ancre = '') {
296        if (is_numeric($i)) {
297                return _generer_url_arbopoly($entite, $i, $args, $ancre);
298        }
299
300        // traiter les injections du type domaine.org/spip.php/cestnimportequoi/ou/encore/plus/rubrique23
301        if ($GLOBALS['profondeur_url'] > 0 and $entite == 'sommaire') {
302                $entite = 'type_urls';
303        }
304
305        // recuperer les &debut_xx;
306        if (is_array($args)) {
307                $contexte = $args;
308        } else {
309                parse_str($args, $contexte);
310        }
311
312        $url = $i;
313        $id_objet = $type = 0;
314        $url_redirect = null;
315
316        // Migration depuis anciennes URLs ?
317        // traiter les injections domain.tld/spip.php/n/importe/quoi/rubrique23
318        if ($GLOBALS['profondeur_url'] <= 0
319                and $_SERVER['REQUEST_METHOD'] != 'POST'
320        ) {
321                include_spip('inc/urls');
322                $r = nettoyer_url_page($i, $contexte);
323                if ($r) {
324                        list($contexte, $type, , , $suite) = $r;
325                        $_id = id_table_objet($type);
326                        $id_objet = $contexte[$_id];
327                        $url_propre = generer_url_entite($id_objet, $type);
328                        if (strlen($url_propre)
329                                and !strstr($url, $url_propre)
330                        ) {
331                                list(, $hash) = array_pad(explode('#', $url_propre), 2, null);
332                                $args = array();
333                                foreach (array_filter(explode('&', $suite)) as $fragment) {
334                                        if ($fragment != "$_id=$id_objet") {
335                                                $args[] = $fragment;
336                                        }
337                                }
338                                $url_redirect = generer_url_entite($id_objet, $type, join('&', array_filter($args)), $hash);
339
340                                return array($contexte, $type, $url_redirect, $type);
341                        }
342                }
343        }
344        /* Fin compatibilite anciennes urls */
345
346        // Chercher les valeurs d'environnement qui indiquent l'url-propre
347        $url_propre = preg_replace(',[?].*,', '', $url);
348
349        // Mode Query-String ?
350        if (!$url_propre
351                and preg_match(',[?]([^=/?&]+)(&.*)?$,', $url, $r)
352        ) {
353                $url_propre = $r[1];
354        }
355
356        if (!$url_propre
357                or $url_propre == _DIR_RESTREINT_ABS
358                or $url_propre == _SPIP_SCRIPT
359        ) {
360                return;
361        } // qu'est-ce qu'il veut ???
362
363
364        include_spip('base/abstract_sql'); // chercher dans la table des URLS
365
366        // Revenir en utf-8 si encodage type %D8%A7 (farsi)
367        $url_propre = rawurldecode($url_propre);
368
369        // Compatibilite avec .htm/.html et autres terminaisons
370        $t = array_diff(array_unique(array_merge(array('.html', '.htm', '/'), url_arbo_terminaison(''))), array(''));
371        if (count($t)) {
372                $url_propre = preg_replace('{('
373                        . implode('|', array_map('preg_quote', $t)) . ')$}i', '', $url_propre);
374        }
375
376        if (strlen($url_propre) and !preg_match(',^[^/]*[.]php,', $url_propre)) {
377                $parents_vus = array();
378
379                // recuperer tous les objets de larbo xxx/article/yyy/mot/zzzz
380                // on parcourt les segments de gauche a droite
381                // pour pouvoir contextualiser un segment par son parent
382                $url_arbo = explode('/', $url_propre);
383                $url_arbo_new = array();
384                $dernier_parent_vu = false;
385                $objet_segments = 0;
386                while (count($url_arbo) > 0) {
387                        $type = null;
388                        if (count($url_arbo) > 1) {
389                                $type = array_shift($url_arbo);
390                        }
391                        $url_segment = array_shift($url_arbo);
392                        // Rechercher le segment de candidat
393                        // si on est dans un contexte de parent, donne par le segment precedent,
394                        // prefixer le segment recherche avec ce contexte
395                        $cp = "0"; // par defaut : parent racine, id=0
396                        if ($dernier_parent_vu) {
397                                $cp = $parents_vus[$dernier_parent_vu];
398                        }
399                        // d'abord recherche avec prefixe parent, en une requete car aucun risque de colision
400                        $row = sql_fetsel('id_objet, type, url',
401                                'spip_urls',
402                                is_null($type)
403                                        ? "url=" . sql_quote($url_segment, '', 'TEXT')
404                                        : sql_in('url', array("$type/$url_segment", $type)),
405                                '',
406                                // en priorite celui qui a le bon parent et les deux segments
407                                // puis le bon parent avec 1 segment
408                                // puis un parent indefini (le 0 de preference) et les deux segments
409                                // puis un parent indefini (le 0 de preference) et 1 segment
410                                (intval($cp) ? "id_parent=" . intval($cp) . " DESC, " : "id_parent>=0 DESC, ") . "segments DESC, id_parent"
411                        );
412                        if ($row) {
413                                if (!is_null($type) and $row['url'] == $type) {
414                                        array_unshift($url_arbo, $url_segment);
415                                        $url_segment = $type;
416                                        $type = null;
417                                }
418                                $type = $row['type'];
419                                $col_id = id_table_objet($type);
420
421                                // le plus a droite l'emporte pour des objets presents plusieurs fois dans l'url (ie rubrique)
422                                $contexte[$col_id] = $row['id_objet'];
423
424                                $type_parent = '';
425                                if ($p = url_arbo_parent($type)) {
426                                        $type_parent = end($p);
427                                }
428                                // l'entite la plus a droite l'emporte, si le type de son parent a ete vu
429                                // sinon c'est un segment contextuel supplementaire a ignorer
430                                // ex : rub1/article/art1/mot1 : il faut ignorer le mot1, la vrai url est celle de l'article
431                                if (!$entite
432                                        or $dernier_parent_vu == $type_parent
433                                ) {
434                                        if ($objet_segments == 0) {
435                                                $entite = $type;
436                                        }
437                                } // sinon on change d'objet concerne
438                                else {
439                                        $objet_segments++;
440                                }
441
442                                $url_arbo_new[$objet_segments]['id_objet'] = $row['id_objet'];
443                                $url_arbo_new[$objet_segments]['objet'] = $type;
444                                $url_arbo_new[$objet_segments]['segment'][] = $row['url'];
445
446                                // on note le dernier parent vu de chaque type
447                                $parents_vus[$dernier_parent_vu = $type] = $row['id_objet'];
448                        } else {
449                                // un segment est inconnu
450                                if ($entite == '' or $entite == 'type_urls') {
451                                        // on genere une 404 comme il faut si on ne sait pas ou aller
452                                        return array(array(), '404');
453                                }
454                                // ici on a bien reconnu un segment en amont, mais le segment en cours est inconnu
455                                // on pourrait renvoyer sur le dernier segment identifie
456                                // mais de fait l'url entiere est inconnu : 404 aussi
457                                // mais conserver le contexte qui peut contenir un fond d'ou venait peut etre $entite (reecriture urls)
458                                return array($contexte, '404');
459                        }
460                }
461
462                if (count($url_arbo_new)) {
463                        $caller = debug_backtrace();
464                        $caller = $caller[1]['function'];
465                        // si on est appele par un autre module d'url c'est du decodage d'une ancienne URL
466                        // ne pas regenerer des segments arbo, mais rediriger vers la nouvelle URL
467                        // dans la nouvelle forme
468                        if (strncmp($caller, "urls_", 5) == 0 and $caller !== "urls_decoder_url") {
469                                // en absolue, car assembler ne gere pas ce cas particulier
470                                include_spip('inc/filtres_mini');
471                                $col_id = id_table_objet($entite);
472                                $url_new = generer_url_entite($contexte[$col_id], $entite);
473                                // securite contre redirection infinie
474                                if ($url_new !== $url_propre
475                                        and rtrim($url_new, "/") !== rtrim($url_propre, "/")
476                                ) {
477                                        $url_redirect = url_absolue($url_new);
478                                }
479                        } else {
480                                foreach ($url_arbo_new as $k => $o) {
481                                        $c = array();
482                                        if (isset($parents_vus['rubrique'])) {
483                                                $c['id_parent'] = $parents_vus['rubrique'];
484                                        }
485                                        if ($s = declarer_url_arbopoly($o['objet'], $o['id_objet'], $c)) {
486                                                $url_arbo_new[$k] = $s;
487                                        } else {
488                                                $url_arbo_new[$k] = implode('/', $o['segment']);
489                                        }
490                                }
491                                $url_arbo_new = ltrim(implode('/', $url_arbo_new), '/');
492
493                                if ($url_arbo_new !== $url_propre) {
494                                        $url_redirect = $url_arbo_new;
495                                        // en absolue, car assembler ne gere pas ce cas particulier
496                                        include_spip('inc/filtres_mini');
497                                        $url_redirect = url_absolue($url_redirect);
498                                }
499                        }
500                }
501
502                // gerer le retour depuis des urls propres
503                if (($entite == '' or $entite == 'type_urls')
504                        and $GLOBALS['profondeur_url'] <= 0
505                ) {
506                        $urls_anciennes = charger_fonction('propres', 'urls');
507
508                        return $urls_anciennes($url_propre, $entite, $contexte);
509                }
510        }
511        if ($entite == '' or $entite == 'type_urls' /* compat .htaccess 2.0 */) {
512                if ($type) {
513                        $entite = objet_type($type);
514                } else {
515                        // Si ca ressemble a une URL d'objet, ce n'est pas la home
516                        // et on provoque un 404
517                        if (preg_match(',^[^\.]+(\.html)?$,', $url)) {
518                                $entite = '404';
519                                $contexte['erreur'] = ''; // qu'afficher ici ?  l'url n'existe pas... on ne sait plus dire de quel type d'objet il s'agit
520                        }
521                }
522        }
523        define('_SET_HTML_BASE', 1);
524
525        return array($contexte, $entite, $url_redirect, null);
526}
Note: See TracBrowser for help on using the repository browser.