Răsfoiți Sursa

#1091 User friendly URLs

Alex Trofimov 4 ani în urmă
părinte
comite
9f48c3036b

+ 109 - 0
inc/classes/BxDolPage.php

@@ -240,6 +240,115 @@ class BxDolPage extends BxDolFactory implements iBxDolFactoryObject, iBxDolRepla
         return true;
     }
 
+	/**
+     * Process SEO links. It takes request part from SEO link and process it 
+     * to make it work as regular page link
+     * @param $sRequest request URI with SEO link
+     * @return true - if page was found and processed correctly, false - if page wasn't found
+     */
+    static public function processSeoLink ($sRequest)
+    {
+        $a = explode('/', trim($sRequest, '/'));
+        if (!$a || empty($a[0]))
+            return false;
+
+        $sPageName = BxDolPageQuery::getPageObjectNameByURI($a[0]);
+        $aPage = $sPageName ? BxDolPageQuery::getPageObject($sPageName) : false;
+        if (!$aPage)
+            return false;
+
+        if (!empty($a[1])) { // page with params
+            $r = BxDolPageQuery::getSeoLink($aPage['module'], $a[0], ['uri' => $a[1]]);
+            if ($r)
+                $_GET[$r['param_name']] = $_REQUEST[$r['param_name']] = $r['param_value'];
+        }
+
+        $_REQUEST['i'] = $_GET['i'] = $a[0];
+        $oPage = BxDolPage::getObjectInstanceByURI($a[0], false, true);
+        $oPage->displayPage();
+
+        return true;
+    }
+
+	/**
+     * Transform regular link to SEO link. It takes regular list as param and return SEO link.
+     * @param $sLink regular link
+     * @param $sPrefix prefix to add to the final URL, usually BX_DOL_URL_ROOT
+     * @param $aParams additional GET params
+     * @return SEO link string on success, false if transform failed
+     */
+    static public function transformSeoLink ($sLink, $sPrefix, $aParams = array())
+    {
+        if (0 !== strncmp('page.php', $sLink, 8)) // only page.php links are supported
+            return false;
+
+        $sQuery = parse_url($sLink, PHP_URL_QUERY);
+        if (false === $sQuery)
+            return false;
+
+        parse_str($sQuery, $aQueryParams);
+
+        $sPageUri = !empty($aQueryParams['i']) ? $aQueryParams['i'] : false;
+        unset($aQueryParams['i']);
+
+        if (!$sPageUri) // page URI wasn't found
+            return false;
+
+        $aSeoParams = array('id', 'profile_id'); // supported SEO params which are transformed to SEO strings
+        $sSeoParamName = '';
+        $sSeoParamValue = '';
+        foreach ($aSeoParams as $k) {
+            if (empty($k) || empty($aQueryParams[$k]))
+                continue;
+            $sSeoParamName = $k;
+            $sSeoParamValue = $aQueryParams[$k];
+            unset($aQueryParams[$k]);
+            break; // only 1 SEO param will be transformed
+        }
+
+        $sSeoPageUri = $sPageUri;
+
+        if ($sSeoParamName && $sSeoParamValue) { // process page with SEO param
+            $sPageName = BxDolPageQuery::getPageObjectNameByURI($sPageUri);
+            $aPage = $sPageName ? BxDolPageQuery::getPageObject($sPageName) : false;
+            if ($aPage) {
+                $sModule = $aPage['module'];
+                if ('id' == $sSeoParamName) {
+                    $oContentInfo = BxDolContentInfo::getObjectInstance($sModule);
+                    $sSeoTitle = $oContentInfo ? $oContentInfo->getContentTitle($sSeoParamValue) : '';
+                    if (!$sSeoTitle) 
+                        $sSeoTitle = base_convert(substr(md5($sSeoParamValue), -8), 16, 36);
+                }
+                elseif ('profile_id' == $sSeoParamName) {
+                    $oProfile = BxDolProfile::getInstance($sSeoParamValue);
+                    $sSeoTitle = $oProfile ? $oProfile->getDisplayName() : '';
+                }
+                
+                $r = BxDolPageQuery::getSeoLink($sModule, $sPageUri, ['param_value' => $sSeoParamValue]);
+                if (!$r) {
+                    $sUri = uriGenerate ($sSeoTitle, 'sys_seo_links', 'uri', '-', '-', ['module' => $sModule, 'page_uri' => $sPageUri]);
+                    BxDolPageQuery::insertSeoLink($sModule, $sPageUri, $sSeoParamName, $sSeoParamValue, $sUri);
+                }
+                elseif ($r['param_name'] == $sSeoParamName) {
+                    $sUri = $r['uri'];
+                } 
+                else {
+                    $sUri = false;
+                }
+
+                if ($sUri)
+                    $sSeoPageUri .= '/' . $sUri;
+                else
+                    $sSeoPageUri = false;
+            }
+        }
+
+        if (!$sSeoPageUri)
+            return false;
+
+        return $sPrefix . bx_append_url_params($sSeoPageUri, array_merge($aQueryParams, $aParams));
+    }
+
     /**
      * Display complete page
      */

+ 24 - 0
inc/classes/BxDolPageQuery.php

@@ -110,6 +110,30 @@ class BxDolPageQuery extends BxDolDb
         $sQuery = $this->prepare("SELECT `id`, `module`, `template` FROM `sys_pages_content_placeholders` WHERE `id` = ?", $iId);
         return $this->getRow($sQuery);
     }
+
+    static public function getSeoLink($sModule, $sPageUri, $aCond = [])
+    {
+        $oDb = BxDolDb::getInstance();
+        $sWhere = " 1 ";
+        if ($aCond)
+            $sWhere = $oDb->arrayToSQL($aCond, " AND ");
+        return $oDb->getRow("SELECT `uri`, `param_name`, `param_value` FROM `sys_seo_links` WHERE " . $sWhere . " AND `module` = :module AND `page_uri` = :page_uri", [
+            'module' => $sModule,
+            'page_uri' => $sPageUri,
+        ]);
+    }
+
+    static public function insertSeoLink($sModule, $sPageUri, $sSeoParamName, $sSeoParamValue, $sUri)
+    {
+        return BxDolDb::getInstance()->query("INSERT INTO `sys_seo_links` SET `module` = :module, `page_uri` = :page_uri, `param_name` = :param_name, `param_value` = :param_value, `uri` = :uri, `added` = :ts", [
+            'module' => $sModule,
+            'page_uri' => $sPageUri,
+            'param_name' => $sSeoParamName,
+            'param_value' => $sSeoParamValue,
+            'uri' => $sUri,
+            'ts' => time(),
+        ]);
+    }
 }
 
 /** @} */

+ 3 - 0
inc/classes/BxDolPermalinks.php

@@ -174,6 +174,9 @@ class BxDolPermalinks extends BxDolDb implements iBxDolSingleton
             
         $sLink = $this->_fixUrl($sLink);
 
+        if ($sSeoLink = BxDolPage::transformSeoLink ($sLink, $sPrefix, $aParams))
+            return $sSeoLink;
+
         foreach ($this->aPrefixesStandard as $sKey => $iLength) {
 
             if (strncmp($sLink, $sKey, $iLength) !== 0)

+ 16 - 18
inc/utils.inc.php

@@ -638,27 +638,24 @@ function clear_xss($val)
 
 //--------------------------------------- friendly permalinks --------------------------------------//
 //------------------------------------------- main functions ---------------------------------------//
-function uriGenerate ($s, $sTable, $sField, $sEmpty = '-', $sDivider = '-')
+function uriGenerate ($sValue, $sTable, $sField, $sEmpty = '-', $sDivider = '-', $aCond = [])
 {
-    $s = uriFilter($s, $sEmpty, $sDivider);
-    if(uriCheckUniq($s, $sTable, $sField))
-        return $s;
+    $sValue = uriFilter($sValue, $sEmpty, $sDivider);
+    if(uriCheckUniq($sValue, $sTable, $sField, $aCond))
+        return $sValue;
 
     // cut off redundant part
-    if(get_mb_len($s) > 240)
-        $s = get_mb_substr($s, 0, 240);
-
-    // try to add date
-    $s .= $sDivider . date('Y-m-d');
-    if(uriCheckUniq($s, $sTable, $sField))
-        return $s;
+    if(get_mb_len($sValue) > 240)
+        $sValue = get_mb_substr($sValue, 0, 240);
 
     // try to add number
-    for($i = 0 ; $i < 999 ; ++$i)
-        if(uriCheckUniq($s . $sDivider . $i, $sTable, $sField))
-            return ($s . $sDivider . $i);
+    for($i = 0 ; $i < 999 ; ++$i) {
+        $iRnd = mt_rand(1000, 9999);
+        if(uriCheckUniq($sValue . $sDivider . $iRnd, $sTable, $sField, $aCond))
+            return ($sValue . $sDivider . $iRnd);
+    }
 
-    return rand(0, 999999999);
+    return rand(0, PHP_INT_MAX);
 }
 
 function uriFilter ($s, $sEmpty = '-', $sDivider = '-')
@@ -671,14 +668,15 @@ function uriFilter ($s, $sEmpty = '-', $sDivider = '-')
     $s = get_mb_replace ('/([' . $sDivider . '^]+)/', $sDivider, $s);
     $s = get_mb_replace ('/([' . $sDivider . ']+)$/', '', $s); // remove trailing dash
     if (!$s) $s = $sEmpty;
-    return $s;
+    return mb_strtolower($s);
 }
 
-function uriCheckUniq ($s, $sTable, $sField)
+function uriCheckUniq ($sValue, $sTable, $sField, $aCond = [])
 {
     $oDb = BxDolDb::getInstance();
 
-    $sSql = $oDb->prepare("SELECT 1 FROM `$sTable` WHERE `$sField`=? LIMIT 1", $s);
+    $sWhere = $aCond ? $oDb->arrayToSQL($aCond, ' AND ') : '1';
+    $sSql = $oDb->prepare("SELECT 1 FROM `$sTable` WHERE $sWhere AND `$sField`=? LIMIT 1", $sValue);
     return !$oDb->query($sSql);
 }
 

+ 18 - 0
install/sql/system.sql

@@ -5495,6 +5495,24 @@ INSERT INTO `sys_rewrite_rules` (`preg`, `service`, `active`) VALUES
 
 -- --------------------------------------------------------
 
+--
+-- SEO Links
+--
+CREATE TABLE `sys_seo_links` (
+  `id` int(11) NOT NULL,
+  `module` varchar(32) NOT NULL,
+  `page_uri` varchar(255) NOT NULL,
+  `param_name` varchar(32) NOT NULL,
+  `param_value` varchar(32) NOT NULL,
+  `uri` varchar(255) NOT NULL,
+  `added` int(11) NOT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `module_page_param` (`module`,`page_uri`,`param_value`),
+  UNIQUE KEY `module_page_uri` (`module`,`page_uri`,`uri`)
+);
+
+-- --------------------------------------------------------
+
 --
 -- Table structure for table `sys_preloader`
 --

+ 3 - 1
r.php

@@ -27,6 +27,8 @@ foreach ($aRewriteRules as $a) {
     }
 }
 
-BxDolTemplate::getInstance()->displayPageNotFound();
+if (!BxDolPage::processSeoLink($sRequest)) {
+    BxDolTemplate::getInstance()->displayPageNotFound();
+}
 
 /** @} */

+ 1 - 1
template/scripts/BxBaseConfig.php

@@ -10,7 +10,7 @@
 class BxBaseConfig extends BxDol implements iBxDolSingleton
 {
     protected $_aConfig = array (
-        'bAllowUnicodeInPreg' => false, ///< allow unicode in regular expressions
+        'bAllowUnicodeInPreg' => true, ///< allow unicode in regular expressions
         'aLessConfig' => array (
             'bx-page-width' => '1000px',