Исправляем ошибку меню для ссылок из url_rewrite.php. Хак.

+15
2.7K
Доброго всем времени суток!
Есть проблема, я описывал ее здесь Проблема активного меню с custom_rewrite
Вкратце:
создаем в меню пункт Вход, ставим тип "ссылка", сама ссылка вот такая /login.
Теперь у нас по клику по пункту меню Вход — можно зайти на сайт.
Кликаем по ней...
Но пункт меню ВХОД не стал активным!
Налицо ошибка в разборе меню, т.е. ссылки вида /login, /passremind b прочие не обрабатываются!
Вы скажете да зачем нам это нужно, у нас эти ссылки висят отдельно. Да, на самом деле, смысла вносить изменения ради этих ссылок нет никакого.
Смысл есть, если мы будем использовать custom_rewrite для своих низменных важных целей. Например, сделать так, чтобы урл контента в каком-либо компоненте был не пятнадцать слов а два

Итак поехали.

сегодня правим файл cms.php

добавляем в начале файла строку
  1. private $rules;
там закешируем правила rewrite

берем функцию loadMenuStruct (загрузка меню в память)
меняем цикл
  1. while ($item = $inDB->fetch_assoc($result)){
  2. $this->menu_struct[$item['id']] = $item;
  3. }
на
  1. while ($item = $inDB->fetch_assoc($result)){
  2. $this->menu_struct[$item['id']] = $item;
  3. $realink='/'.$this->detectorMenuURI($item['link']);
  4. $this->menu_struct[$item['id']]['realink'] = $realink;
  5. }
это мы посчитали "настоящую" ссылку в меню

добавляем новую функцию -
  1. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2.  
  3. private function detectorMenuURI($muri){
  4.  
  5. $uri = $muri;
  6. $uri = ltrim($uri, '/');
  7. $rules = array();
  8.  
  9. $folder = rtrim($uri, '/');
  10.  
  11. if (in_array($folder, array('admin', 'install', 'migrate'))) { return; }
  12. if (!$this->rules) {
  13. if(file_exists(PATH.'/url_rewrite.php')) {
  14. //подключаем список rewrite-правил
  15. $this->includeFile('url_rewrite.php');
  16. if(function_exists('rewrite_rules')){
  17. //получаем правила
  18. $rules = rewrite_rules();
  19. }
  20. }
  21.  
  22. if(file_exists(PATH.'/custom_rewrite.php')) {
  23. //подключаем список пользовательских rewrite-правил
  24. $this->includeFile('custom_rewrite.php');
  25. if(function_exists('custom_rewrite_rules')){
  26. //добавляем к полученным ранее правилам пользовательские
  27. $rules = array_merge($rules, custom_rewrite_rules());
  28. }
  29. }
  30. if ($rules){ $this->rules=$rules;}
  31. }
  32. else {
  33. $rules=$this->rules;
  34. }
  35.  
  36. $found = false;
  37.  
  38. if ($rules){
  39.  
  40. //перебираем правила
  41. foreach($rules as $rule_id=>$rule) {
  42.  
  43. //небольшая валидация правила
  44. if (!$rule['source'] || !$rule['target'] || !$rule['action']) { continue; }
  45. //проверяем совпадение выражения source с текущим uri
  46. if (preg_match($rule['source'], $uri, $matches)){
  47. //перебираем совпавшие сегменты и добавляем их в target
  48. //чтобы сохранить параметры из $uri в новом адресе
  49. foreach($matches as $key=>$value){
  50. if (!$key) { continue; }
  51. if (strstr($rule['target'], '{'.$key.'}')){
  52. $rule['target'] = str_replace('{'.$key.'}', $value, $rule['target']);
  53. }
  54. }
  55.  
  56. //действие по-умолчанию: rewrite
  57. if (!$rule['action']) { $rule['action'] = 'rewrite'; }
  58.  
  59. //выполняем действие
  60. switch($rule['action']){
  61. case 'rewrite' : $uri = $rule['target']; $found = true; break;
  62. case 'redirect' : $this->redirect($rule['target']); break;
  63. case 'redirect-301' : $this->redirect($rule['target'], '301'); break;
  64. case 'alias' : $this->includeFile($rule['target']); $this->halt();break;
  65. }
  66.  
  67. }
  68.  
  69. if ($found) { break; }
  70.  
  71. }
  72. }
  73.  
  74. return $uri;
  75.  
  76. }
  77.  
  78. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  79.  
А теперь в функции menuID() меняем цикл foreach
  1. foreach($menu as $item){
  2.  
  3. if (!$item['link']) { continue; }
  4.  
  5. //полное совпадение ссылки и адреса?
  6. if ($uri == $item['link']){
  7. $menuid = $item['id'];
  8. $is_strict = true; //полное совпадение
  9. break;
  10. }
  11. //частичное совпадение ссылки и адреса (по началу строки)?
  12. $uri_first_part = substr($uri, 0, strlen($item['link']));
  13. if ($uri_first_part == $item['link']){
  14. $menuid = $item['id'];
  15. break;
  16. }
  17. }
на

  1. foreach($menu as $item){
  2.  
  3. if (!$item['link']) { continue; }
  4.  
  5. //полное совпадение ссылки и адреса?
  6. if ($uri == $item['link']){
  7. $menuid = $item['id'];
  8. $is_strict = true; //полное совпадение
  9. break;
  10. }
  11. //////////////// Определяем правильную ссылку для пункта меню
  12. if ($uri == $item['realink']){
  13. $menuid = $item['id'];
  14. $is_strict = true; //полное совпадение
  15. break;
  16. }
  17. /////////////////////////////////////////////////////////////////////////////
  18. //частичное совпадение ссылки и адреса (по началу строки)?
  19. $uri_first_part = substr($uri, 0, strlen($item['link']));
  20. if ($uri_first_part == $item['link']){
  21. $menuid = $item['id'];
  22. break;
  23. }
  24. //////////////// Определяем правильную ссылку для пункта меню /////////////
  25. //частичное совпадение ссылки и адреса (по началу строки)?
  26. $uri_first_part = substr($uri, 0, strlen($item['realink']));
  27. if ($uri_first_part == $item['realink']){
  28. $menuid = $item['id'];
  29. break;
  30. }
  31. //////////////////////////////////////////////////////////////////////////////
  32. }
теперь меню работает правильно
делалось под 1.8 для 1.9 думаю будет аналогично
решение не претендует на красоту, но общая идея понятна
а по поводу "зачем все это нужно" я расскажу в следующих постах
0
universe universe 12 лет назад #
"Например, сделать так, чтобы урл контента в каком-либо компоненте был не пятнадцать слов а два"
не понял про что это=)
+1
Крот Крот 12 лет назад #
здесь очень много желающих чтобы ссылка до какого-либо объекта в каком-либо компоненте была не
http://сайт.ру/категория1/категория2/категория3/.../объект.html
а просто
http://сайт.ру/категория/объект.html вне зависимости от вложенности
это просто пример, через custom_rewrite много интересного сделать можно
0
universe universe 12 лет назад #
тоесть что то общего в нем есть с http://instantcms.ru/blogs/moi-blog-360/komponent-i-plagin-seo-pages.html ?
0
Крот Крот 12 лет назад #
не могу сказать, я еще не смотрел SEO компонент, может просто совпало так))
0
Крот Крот 12 лет назад #
но подозреваю, что есть smile
-3
Алексей Т Алексей Т 12 лет назад #
Спасибо крот...
0
Крот Крот 12 лет назад #
поправил функцию loadMenuStruct - была небольшая ошибка... если кто поставил, проверьте
0
Tarhun Tarhun 12 лет назад #
на 1.9 не работает...
0
Tarhun Tarhun 12 лет назад #
как отредактировать под 1.9?
После пункта "добавляем новую функцию"
Перестает вообще открываться сайт, происходит редирект - http://сайт.ру/сайт.ру/
0
BSB BSB 12 лет назад #
Хм, я в растерянности... Поставил апач на голую убунту 12.04, поправил Override None на Override All в конфиге, убедился, что установлен mod_rewrite - у меня icms 1.9.1 по ссылке на /login вываливается с ошибкой 404.
Есть, правда, вероятность, что версию неполноценную выкачал (критерий был - utf-8). Кто-нить, дайте сцыль на .htaccess правильный? А то у меня такое впечатление, что в транках он левый.
0
Крот Крот 12 лет назад #
для 1.9 надо
функция detectorMenuURI() - на самом деле это просто подправленная копия detectURI - сделано для ускорения работы
можно использовать оригинальный detectURI - но тогда при каждом клике будет пересчитываться список uri
соотв-но при изменении списка в custom_rewrite нужно как-то сбрасывать закэшированное значение

Код PHP:
 private function detectorMenuURI($muri){

     $uri    = $muri;
     $uri    = ltrim($uri, '/');
     $rules  = array();

     $folder = rtrim($uri, '/');

if (strstr($uri, "?") && !preg_match('/^admin\/(.*)/i', $uri) && !strstr($uri, 'go/url=')){
            $query_str = substr($uri, strpos($uri, "?")+1);
            $uri = substr($uri, 0, strpos($uri, "?"));
            parse_str($query_str, $temp_request);
			$_REQUEST = array_merge($_REQUEST, $temp_request);
        }

        if (in_array($folder, array('admin', 'install', 'migrate', 'index.php'))) { return; }
     
        //специальный хак для поиска по сайту, для совместимости со старыми шаблонами
        if (strstr($_SERVER['QUERY_STRING'], 'view=search')){ $uri = 'search'; }

if (!$this->rules) {
       if(file_exists(PATH.'/url_rewrite.php')) {
           //подключаем список rewrite-правил
            self::includeFile('url_rewrite.php');
            if(function_exists('rewrite_rules')){
                //получаем правила
                $rules = rewrite_rules();
            }
        }

        if(file_exists(PATH.'/custom_rewrite.php')) {
            //подключаем список пользовательских rewrite-правил
            self::includeFile('custom_rewrite.php');
            if(function_exists('custom_rewrite_rules')){
                //добавляем к полученным ранее правилам пользовательские
                $rules = array_merge($rules, custom_rewrite_rules());
            }
        }
        if ($rules){  $this->rules=$rules;}
     }
     else {
       $rules=$this->rules;
     }

     $found = false;

     if ($rules){

         //перебираем правила
         foreach($rules as $rule_id=>$rule) {

             //небольшая валидация правила
             if (!$rule['source'] || !$rule['target'] || !$rule['action']) { continue; }
             //проверяем совпадение выражения source с текущим uri
             if (preg_match($rule['source'], $uri, $matches)){
                 //перебираем совпавшие сегменты и добавляем их в target
                 //чтобы сохранить параметры из $uri в новом адресе
                 foreach($matches as $key=>$value){
                     if (!$key) { continue; }
                     if (strstr($rule['target'], '{'.$key.'}')){
                         $rule['target'] = str_replace('{'.$key.'}', $value, $rule['target']);
                     }
                 }

                 //действие по-умолчанию: rewrite
                 if (!$rule['action']) { $rule['action'] = 'rewrite'; }

                 //выполняем действие
                 switch($rule['action']){
                     case 'rewrite'      : $uri = $rule['target']; $found = true; break;
                     case 'redirect'     : $this->redirect($rule['target']); break;
                     case 'redirect-301' : $this->redirect($rule['target'], '301'); break;
                        case 'alias'        : self::includeFile($rule['target']); $this->halt();break;
                 }

             }

             if ($found) { break; }

         }
     }

     return $uri;
     
 }
menuID
Код PHP:
    public function menuId(){

        //если menu_id был определен ранее, то вернем и выйдем
        if ($this->menu_id) { return $this->menu_id; }

        $view       = self::request('view', 'str', '');
        
        if ($this->is_content){
            $uri = substr($this->uri, strlen('content/'));
        } else {
            $uri = $this->uri;
        }

        $uri        = '/'.$uri;

        //флаг, показывающий было совпадение URI и ссылки пунта меню
        //полным или частичным
        $is_strict  = false;

        //главная страница?
        $menuid     = ($uri == '/' ? 1 : 0);
        if ($menuid == 1) {
            $this->is_menu_id_strict = 1;
            return $menuid;
        }

        //перевернем массив меню чтобы перебирать от последнего пункта к первому
        $menu = array_reverse($this->menu_struct);

        //перебираем меню в поисках текущего пункта
        foreach($menu as $item){

            if (!$item['link']) { continue; }

			// uri с учетом имени хоста
			$full_uri = HOST . $uri;

            //полное совпадение ссылки и адреса?
            if ($uri == $item['link'] || $full_uri == $item['link']){
                $menuid = $item['id'];
                $is_strict = true; //полное совпадение
                break;
            }

////////////////  Определяем правильную ссылку для пункта меню
            if ($uri == $item['realink']){
                $menuid = $item['id'];
                $is_strict = true; //полное совпадение
                break;
            }

            //частичное совпадение ссылки и адреса (по началу строки)?
            $uri_first_part = substr($uri, 0, strlen($item['link']));
            if ($uri_first_part == $item['link']){
                $menuid = $item['id'];
                break;
            }
////////////////  Определяем правильную ссылку для пункта меню  /////////////
            //частичное совпадение ссылки и адреса (по началу строки)?
            $uri_first_part = substr($uri, 0, strlen($item['realink']));
            if ($uri_first_part == $item['realink']){
                $menuid = $item['id'];
                break;
            }
           
        }

        $this->menu_id              = $menuid;
        $this->is_menu_id_strict    = $is_strict;

        return $menuid;

    }
сравните со своим cms.php
+1
Fuze Fuze 12 лет назад #
Более простое решение, файл core.php

После
Код PHP:
  1. private $uri;
вставляем
Код PHP:
  1. private $real_uri;
Далее в методе detectURI() перед
Код PHP:
  1. if ($rules){
  2. //перебираем правила
Вставляем
Код PHP:
  1. $this->real_uri = $uri;
Далее в методе menuId() после
Код PHP:
  1. $uri = '/'.$uri;
вставляем
Код PHP:
  1. $real_uri = '/'.$this->real_uri;
И далее условия по полному или частичному совпадению меняем на
Код PHP:
  1. //полное совпадение ссылки и адреса?
  2. if (in_array($item['link'], array($uri, $full_uri, $real_uri))){
  3. $menuid = $item['id'];
  4. $is_strict = true; //полное совпадение
  5. break;
  6. }
  7. //частичное совпадение ссылки и адреса (по началу строки)?
  8. $uri_first_part = mb_substr($uri, 0, mb_strlen($item['link']));
  9. $real_uri_first_part = mb_substr($real_uri, 0, mb_strlen($item['link']));
  10. if (in_array($item['link'], array($uri_first_part, $real_uri_first_part))){
  11. $menuid = $item['id'];
  12. break;
  13. }
0
Крот Крот 12 лет назад #
посмотрел, да так проще
НО:
второе решение, также как и мое содержит один нюанс -
есть два пункта меню
1) /catalog/1001
2) /catalog

при клике на первое - активным становится второе. решается сначала перебором полных путей, а затем, если не найдено перебор частичных путей.
вот теперь меня метод menuID() устраивает полностью )))

Код PHP:
  1. /**
  2. * Возвращает ID текущего пункта меню
  3. * @return int
  4. */
  5. public function menuId(){
  6. //если menu_id был определен ранее, то вернем и выйдем
  7. if ($this->menu_id) { return $this->menu_id; }
  8. $view = self::request('view', 'str', '');
  9. if ($this->is_content){
  10. $uri = substr($this->uri, strlen('content/'));
  11. } else {
  12. $uri = $this->uri;
  13. }
  14. $uri = '/'.$uri;
  15. $real_uri = '/'.$this->real_uri;
  16. //флаг, показывающий было совпадение URI и ссылки пунта меню
  17. //полным или частичным
  18. $is_strict = false;
  19. //главная страница?
  20. $menuid = ($uri == '/' ? 1 : 0);
  21. if ($menuid == 1) {
  22. $this->is_menu_id_strict = 1;
  23. return $menuid;
  24. }
  25. //перевернем массив меню чтобы перебирать от последнего пункта к первому
  26. $menu = array_reverse($this->menu_struct);
  27. $fnd=false;
  28. //перебираем меню в поисках текущего пункта
  29. foreach($menu as $item){
  30. if (!$item['link']) { continue; }
  31. // uri с учетом имени хоста
  32. $full_uri = HOST . $uri;
  33. if (in_array($item['link'], array($uri, $full_uri, $real_uri))){
  34. $menuid = $item['id'];
  35. $is_strict = true; //полное совпадение
  36. $fnd=true;
  37. break;
  38. }
  39. }
  40. //перебираем меню в поисках текущего пункта
  41. if (!$fnd)
  42. foreach($menu as $item){
  43. if (!$item['link']) { continue; }
  44. // uri с учетом имени хоста
  45. $full_uri = HOST . $uri;
  46. //частичное совпадение ссылки и адреса (по началу строки)?
  47. $uri_first_part = mb_substr($uri, 0, mb_strlen($item['link']));
  48. $real_uri_first_part = mb_substr($real_uri, 0, mb_strlen($item['link']));
  49. if (in_array($item['link'], array($uri_first_part, $real_uri_first_part))){
  50. $menuid = $item['id'];
  51. break;
  52. }
  53. }
  54. $this->menu_id = $menuid;
  55. $this->is_menu_id_strict = $is_strict;
  56. return $menuid;
  57. }
0
Tarhun Tarhun 12 лет назад #
Firefox определил, что сервер перенаправляет запрос на этот адрес таким образом, что он никогда не завершится.

Можете скинуть сам файл cms.php для 1.9?
Олег Васильевич я Олег Васильевич я 12 лет назад #
Комментарий удален
+1
Bubble Gumoff Bubble Gumoff 12 лет назад #
А как можно без rewrite разбить seolink, чтобы формировались ссылки так
ссылка статьи была host/statya
ссылка категории host/kategoriya
ссылка подкатегории host/podkategoriya
0
*wildbeez* *wildbeez* 12 лет назад #
пишу в надежде, что ответят
0
Крот Крот 12 лет назад #
в 1.10 ошибки больше нет)

Еще от автора

Инвайтер 1.9 для 1.10.6
Доброго всем времени суток! Решил нарушить сложившуюся здесь традицию и решил вернуться сюда с обновлениями.
Инстант "по взрослому". Часть 2. Авторизация. Аякс. v1.10
Доброго всем времени суток! Продолжая серию Инстант "по взрослому", сегодня хотел бы показать более мощный плагин авторизации.
Инстант "по взрослому". Часть 1. Авторизация. Счетчик неверных входов. v1.10
Доброго всем дня! Продолжим наши уроки по усовершенствованию Инстанта. Сегодня мы будем изучать и усовершенствовать первую ветку Инстанта.
Используя этот сайт, вы соглашаетесь с тем, что мы используем файлы cookie.