BxDolPermalinks.php 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. <?php defined('BX_DOL') or die('hack attempt');
  2. /**
  3. * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
  4. * CC-BY License - http://creativecommons.org/licenses/by/3.0/
  5. *
  6. * @defgroup TridentCore Trident Core
  7. * @{
  8. */
  9. /**
  10. * @page objects
  11. * @section permalinks Permalinks
  12. * @ref BxDolPermalinks
  13. */
  14. /**
  15. * Permalinks for any content.
  16. *
  17. * An object of the class allows to check whether permalink is enabled
  18. * and get it for specified standard URI.
  19. *
  20. *
  21. * All permalinks must match the whole URL, to be automatially determined.
  22. * There are only 2 permlinks which are replaced by prefix:
  23. * - modules/?r= - for module URLs
  24. * - page.php?i= - for pages URLs
  25. *
  26. *
  27. * @section example Example of usage:
  28. *
  29. * 1. Register permalink in database by adding necessary info in the `sys_permalinks` table.
  30. * 2. Create an object and process the URI
  31. * @code
  32. * $oPermalinks = new BxDolPermalinks();
  33. * $oPermalinks->permalink('modules/?r=news/home');
  34. * @endcode
  35. *
  36. *
  37. * @section acl Memberships/ACL:
  38. * Doesn't depend on user's membership.
  39. *
  40. *
  41. * @section alerts Alerts:
  42. *
  43. * The following alert can be used when permalink call is performed:
  44. * - $sUnit - system
  45. * - $sAction - permalink | unpermalink
  46. * - $iObjectId - 0 (not used)
  47. * - $iSenderId - 0 (not used)
  48. * - $aExtra['link'] - the link to check permalink for
  49. * - $aExtra['return_data'] - it is possible to override default behavior, by setting 'return_data' to non NULL value.
  50. *
  51. */
  52. class BxDolPermalinks extends BxDolDb implements iBxDolSingleton
  53. {
  54. protected $sCacheFile;
  55. protected $aLinksStandard;
  56. protected $aLinksPermalink;
  57. protected $aPrefixesStandard;
  58. protected $aPrefixesPermalink;
  59. protected function __construct()
  60. {
  61. if (isset($GLOBALS['bxDolClasses'][get_class($this)]))
  62. trigger_error ('Multiple instances are not allowed for the class: ' . get_class($this), E_USER_ERROR);
  63. parent::__construct();
  64. if (getParam('sys_db_cache_enable')) {
  65. $oCache = $this->getDbCacheObject();
  66. $sCacheKey = $this->genDbCacheKey('sys_permalinks');
  67. $aPermalinksData = $oCache->getData($sCacheKey);
  68. if (null === $aPermalinksData) {
  69. $aPermalinksData = $this->getPermalinksData();
  70. $oCache->setData ($sCacheKey, $aPermalinksData);
  71. }
  72. } else {
  73. $aPermalinksData = $this->getPermalinksData();
  74. }
  75. $this->aLinksStandard = $aPermalinksData['standard'];
  76. $this->aLinksPermalink = $aPermalinksData['permalink'];
  77. $this->aPrefixesStandard = $aPermalinksData['prefixes_standard'];
  78. $this->aPrefixesPermalink = $aPermalinksData['prefixes_permalink'];
  79. }
  80. public function cacheInvalidate()
  81. {
  82. return BxDolDb::getInstance()->cleanCache ('sys_permalinks');
  83. }
  84. /**
  85. * Prevent cloning the instance
  86. */
  87. public function __clone()
  88. {
  89. if (isset($GLOBALS['bxDolClasses'][get_class($this)]))
  90. trigger_error('Clone is not allowed for the class: ' . get_class($this), E_USER_ERROR);
  91. }
  92. /**
  93. * Get singleton instance of the class
  94. */
  95. public static function getInstance()
  96. {
  97. if(!isset($GLOBALS['bxDolClasses'][__CLASS__]))
  98. $GLOBALS['bxDolClasses'][__CLASS__] = new BxDolPermalinks();
  99. return $GLOBALS['bxDolClasses'][__CLASS__];
  100. }
  101. function getPermalinksData()
  102. {
  103. $aLinksStandard = $this->getAll("SELECT * FROM `sys_permalinks`");
  104. $aResult = array(
  105. 'standard' => array(),
  106. 'permalink' => array(),
  107. 'prefixes_standard' => array(),
  108. 'prefixes_permalink' => array(),
  109. );
  110. foreach ($aLinksStandard as $aLink) {
  111. $a = array(
  112. 'permalink' => $aLink['permalink'],
  113. 'standard' => $aLink['standard'],
  114. 'check' => $aLink['check'],
  115. 'enabled' => getParam($aLink['check']) == 'on',
  116. );
  117. $aResult['standard'][$aLink['standard']] = $a;
  118. $aResult['permalink'][$aLink['permalink']] = $a;
  119. if ($aLink['compare_by_prefix']) {
  120. $aResult['prefixes_standard'][$aLink['standard']] = strlen($aLink['standard']);
  121. $aResult['prefixes_permalink'][$aLink['permalink']] = strlen($aLink['permalink']);
  122. }
  123. }
  124. return $aResult;
  125. }
  126. /**
  127. * Get page name (changeable page of URL) from the URL.
  128. * @param $sLink - relative UNpermalinked, it is better to use unpermalink method to make sure that correct url is passed here
  129. * @param $iLength - position of the page name, or false to detect automatically
  130. * @returm page name (changeable part of URL) or empty string if page name is not detectable.
  131. */
  132. function getPageNameFromLink($sLink, $iLength = false)
  133. {
  134. if (false == $iLength) {
  135. $sLink = $this->_fixUrl($sLink);
  136. foreach ($this->aPrefixesStandard as $sKey => $iLen) {
  137. if (strncmp($sLink, $sKey, $iLen) === 0) {
  138. $iLength = $iLen;
  139. break;
  140. }
  141. }
  142. }
  143. return false === $iLength ? '' : substr($sLink, $iLength);
  144. }
  145. /**
  146. * Make permalink from link.
  147. * @param $sLink - relative URL.
  148. * @param $aParams - params to add to the url.
  149. * @return - relative permalinked URL if it was detected and permalinks are ON or unchanged URL otherwise.
  150. */
  151. function permalink($sLink, $aParams = array())
  152. {
  153. $sRet = null;
  154. bx_alert('system', 'permalink', 0, 0, array('link' => $sLink, 'params' => &$aParams, 'return_data' => &$sRet));
  155. if (null !== $sRet)
  156. return $sRet;
  157. $sLink = $this->_fixUrl($sLink);
  158. foreach ($this->aPrefixesStandard as $sKey => $iLength) {
  159. if (strncmp($sLink, $sKey, $iLength) !== 0)
  160. continue;
  161. $sPage = $this->getPageNameFromLink($sLink, $iLength);
  162. if (!$this->_isEnabled($sKey))
  163. return $sLink;
  164. return bx_append_url_params($this->aLinksStandard[$sKey]['permalink'] . $sPage, $aParams);
  165. }
  166. return bx_append_url_params($this->_isEnabled($sLink) ? $this->aLinksStandard[$sLink]['permalink'] : $sLink, $aParams);
  167. }
  168. /**
  169. * Remove permalink from link.
  170. * @param $sLink - relative or absoulte URL.
  171. * @param $isStripBaseUrl - strip site prefix (absolute URL) automatically (enabled by default)
  172. * @return - relative UNpermalinked URL if it was detected or relative URL if URL withing the site or unchanged URL otherwise.
  173. */
  174. function unpermalink($sLink, $isStripBaseUrl = true)
  175. {
  176. if ($isStripBaseUrl && 0 === strncmp($sLink, BX_DOL_URL_ROOT, strlen(BX_DOL_URL_ROOT)))
  177. $sLink = substr($sLink, strlen(BX_DOL_URL_ROOT));
  178. $sRet = null;
  179. bx_alert('system', 'unpermalink', 0, 0, array('link' => $sLink, 'return_data' => &$sRet));
  180. if (null !== $sRet)
  181. return $sRet;
  182. foreach ($this->aPrefixesPermalink as $sKey => $iLength) {
  183. if (strncmp($sLink, $sKey, $iLength) !== 0)
  184. continue;
  185. $sPage = substr($sLink, $iLength);
  186. return $this->aLinksPermalink[$sKey]['standard'] . $sPage;
  187. }
  188. return isset($this->aLinksPermalink[$sLink]) ? $this->aLinksPermalink[$sLink]['standard'] : $sLink;
  189. }
  190. function _isEnabled($sLink)
  191. {
  192. return array_key_exists($sLink, $this->aLinksStandard) && $this->aLinksStandard[$sLink]['enabled'];
  193. }
  194. /**
  195. * redirect to the correct url after switching skin or language
  196. * only correct modules urls are supported
  197. */
  198. function redirectIfNecessary ($aSkip = array())
  199. {
  200. $sCurrentUrl = $_SERVER['PHP_SELF'] . '?' . bx_encode_url_params($_GET, $aSkip);
  201. if (!preg_match('/modules\/index.php\?r=(\w+)(.*)/', $sCurrentUrl, $m))
  202. return false;
  203. $sStandardLink = 'modules/?r=' . $m[1] . '/';
  204. $sPermalink = $this->permalink ($sStandardLink);
  205. if (false !== strpos($sCurrentUrl, $sPermalink))
  206. return false;
  207. header("HTTP/1.1 301 Moved Permanently");
  208. header ('Location:' . BX_DOL_URL_ROOT . $sPermalink . rtrim(trim(urldecode($m[2]), '/'), '&'));
  209. return true;
  210. }
  211. protected function _fixUrl($sLink)
  212. {
  213. if (strncmp($sLink, 'modules/index.php?r=', 20) === 0)
  214. $sLink = str_replace('modules/index.php?r=', 'modules/?r=', $sLink);
  215. return $sLink;
  216. }
  217. }
  218. /** @} */